Как заставить QSortFilterProxyModel работать с пользовательским ItemDelegate QTreeView

Ниже мое изменение кода для реализации сортировки без учета регистра:

Пользовательский ItemDelegate «RegistryItemDelegate» будет аварийно завершать работу после того, как mTreeView->setModel() примет ProxyModel в качестве аргумента вместо mModel.

Как заставить QSortFilterProxyModel работать с пользовательским ItemDelegate? какого кода не хватает?

RegistryTreeView::RegistryTreeView(Registry &inRegistry, QWidget *inParent)
{
    mModel = new RegistryItemModel;
    mModel->setRegistry(&inRegistry);

    QSortFilterProxyModel *ProxyModel = new QSortFilterProxyModel(this);
    ProxyModel->setSourceModel(mModel);
    ProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    ProxyModel->sort(0, Qt::DescendingOrder);

    mTreeView = new QTreeView;
    mTreeView->setContextMenuPolicy(Qt::DefaultContextMenu);
    //mTreeView->setModel(mModel);
    mTreeView->setModel(ProxyModel);
    mTreeView->setItemDelegate(new RegistryItemDelegate(mModel));
    mTreeView->installEventFilter(new A utoSelectEventFilter(this));
    
    mainLayout->addWidget(mTreeView);
    
}

class RegistryItemModel : public QAbstractItemModel
{
Q_OBJECT;

public:
RegistryItemModel(QWidget *parent=NULL);
virtual ~RegistryItemModel();
......
} 

При использовании mTreeView->setModel(mModel):

При использовании mTreeView->setModel(ProxyModel):

void RegistryItemDelegate::initStyleOption(QStyleOptionViewItem *inOption, const QModelIndex &inIndex) const
{
    QStyledItemDelegate::initStyleOption(inOption, inIndex);

    const Node *node = mModel->node(inIndex);// the inIndex are different when using ProxyModel and mModel, and get a Node object which is not null, but will get a access violation exception when calling isDefault().  
    
    if (node == NULL)
        return;

    if (!node->isDefault())
        inOption->palette.setColor(QPalette::Text, mModifiedColor);

    if (node->nodeType() == eRegDir && inOption->version == 4)
    { 
        ((QStyleOptionViewItemV4*)inOption)->features |= QStyleOptionViewItemV2::HasDecoration;
        ((QStyleOptionViewItemV4*)inOption)->icon = mFolderIcon;
    }
}

 class Node
 {
    protected:
        Node(const QString &inName);


    private:
        Node(const QString &inName, Node *inParent);
        Node(const Node &inNode);


    public:
        virtual ~Node();
        virtual bool isDefault() const = 0;
  }

  class NodeKey : public Node
  class NodeDir : public Node
  bool NodeKey::isDefault() const
  {
      if (mDefaultValue.isNull())
          return false;

      if (mValue.isNull())
          return true;

      return mDefaultValue == mValue;
  }
  
  bool NodeDir::isDefault() const
  {
      foreach(Node *node, visibleChildren(false))
      {
         if (!node->isDefault())
         return false;
  }

       return mDefault;
  }

////////////////////////////////////////////////// ///////////////////

void RegistryItemDelegate::initStyleOption(QStyleOptionViewItem *inOption, const QModelIndex &inIndex) const
{
    QStyledItemDelegate::initStyleOption(inOption, inIndex);

    //const Node *node = mModel->node(inIndex);
    const Node* node = nullptr;

    QModelIndex sourceIndex = inIndex;
    auto proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
    //We map the index back to its source as many times as needed.
    //We expect to do it once for now but that may change in the future.
    while (proxyModel) {
          sourceIndex = proxyModel->mapToSource(sourceIndex);
          proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
    }
    if (auto regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model()) ; regModel) // line 38
       node = regModel->node(sourceIndex); // line 39
    
    if (node == NULL)
        return;

    if (!node->isDefault())
        inOption->palette.setColor(QPalette::Text, mModifiedColor);

    if (node->nodeType() == eRegDir && inOption->version == 4)
    { 
        ((QStyleOptionViewItemV4*)inOption)->features |= QStyleOptionViewItemV2::HasDecoration;
        ((QStyleOptionViewItemV4*)inOption)->icon = mFolderIcon;
    }
}

Ошибки построения:

ClCompile: 1> RegistryItemDelegate.cpp 1>RegistryItemDelegate.cpp(38): ошибка C2143: синтаксическая ошибка: отсутствует ')' перед ';' 1>RegistryItemDelegate.cpp(38): предупреждение C4390: ';' : найден пустой контролируемый оператор; это намерение? 1>RegistryItemDelegate.cpp(38): ошибка C2059: синтаксическая ошибка: ')' 1>RegistryItemDelegate.cpp(39): ошибка C2065: «regModel»: необъявленный идентификатор 1>RegistryItemDelegate.cpp(39): ошибка C2146: синтаксическая ошибка: отсутствует ';' перед идентификатором "узел" 1>RegistryItemDelegate.cpp(39): ошибка C2065: «regModel»: необъявленный идентификатор 1>RegistryItemDelegate.cpp(39): ошибка C2227: слева от '->node' должен указывать на класс/структуру/объединение/общий тип 1> тип ''неизвестный тип''

Эта ошибка не имеет ничего общего с взаимодействием модели и делегата; Предоставьте код, в котором, по вашему мнению, возникает ошибка (не скриншот) и дополнительную информацию об отладке. Кроме того, мне кажется странным, что вы так обрамили и вручную выделили строку, когда могли оставить отладчик, как будто ошибка возникает не там; это может быть внутри метода isDefault. Кстати, вы работаете на C++, поэтому следует избегать приведения типов NULL или C (+ QStyleOptionViewItemV4 уже давно устарел, см. здесь).

Atmo 06.04.2023 05:13

@Atmo, спасибо, по умолчанию это чисто виртуальная функция, когда mTreeView->setModel использует mModel, сбоев не происходит.

Ming 06.04.2023 08:41

мне немного любопытно; если у него чисто виртуальный метод, как получить экземпляр? Или я должен сказать: не могли бы вы предоставить код RegistryItemModel::node, чтобы мы выяснили, как компилятор не кричит на вас за создание экземпляра абстрактного класса? Что касается комментария, который вы добавили, это действительно большая подсказка. Но боюсь без ответа на мой предыдущий вопрос у меня все равно не будет полной картины.

Atmo 06.04.2023 10:10

@Atmo Существуют NodeKey и NodeDir, производные от Node, и Isdefault реализован в этих двух классах. Я прикрепил картинку, чтобы показать, откуда взялся этот «QModelIndex inIndex». Когда я передаю прокси-модель в mTreeView->setModel(), этот «QModelIndex inIndex» не совпадает, и mModel->node(inIndex) не возвращает ожидаемый NodeKey или NodeDir.

Ming 07.04.2023 04:33

В будущем, пожалуйста, размещайте код, а не картинки. Что бы вы сделали, если бы никто не ответил на ваш вопрос? Во всяком случае, я ответил на него.

Atmo 07.04.2023 07:24

@Atmo Спасибо. Мне сказали, что еще один способ реализовать сортировку без учета регистра — это переопределить виртуальный QAbstractItemModel::sort() для реализации вашей собственной сортировки без учета регистра. Вы знаете, где я могу найти пример кода?

Ming 07.04.2023 08:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как вы уже догадались, проблема в том, что inIndex является индексом вашего QSortFilterProxyModel, а не индексом вашего RegistryItemModel. Будем надеяться, что QAbstractProxyModel (базовый класс для QSortFilterProxyModel) предоставляет все необходимое для сопоставления inIndex с исходной моделью.

Я предполагаю, что у вас может быть 0, 1 или несколько слоев прокси-моделей, то есть модель, прикрепленная к вашему представлению, представляет собой цепочку, которая может напоминать: QAbstractProxyModel -> QAbstractProxyModel -> ... -> RegisterItemModel. Я буду называть слоями количество прокси-моделей между тем, что прикреплено к вашему представлению (включено), и вашим экземпляром RegisterItemModel.

Ты можешь сделать:

const Node* node = nullptr;

QModelIndex sourceIndex = inIndex;
auto proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
//We map the index back to its source as many times as needed.
//We expect to do it once for now but that may change in the future.
while (proxyModel) {
    sourceIndex = proxyModel->mapToSource(sourceIndex);
    proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
}
if (auto regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model()); regModel)
    node = regModel->node(sourceIndex); 

Обновлено:
В Qt5/C++14 или более ранних версиях if:

if (auto regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model()); regModel)
    node = regModel->node(sourceIndex);

необходимо изменить на 2 отдельные строки кода:

auto regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model()) ;
if (regModel)
    node = regModel->node(sourceIndex);

Чтобы быть полным, также можно использовать возвращаемое значение оператора присваивания, но мне это решение не нравится. Это всегда выглядит так, как будто == по ошибке превратили в =:

if (auto regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model()))
    node = regModel->node(sourceIndex);

Qt5 подходит к концу и не получит никаких дальнейших улучшений. Я настоятельно рекомендую вам перейти на Qt6 / C++17, чем раньше, тем лучше.


Я не проверял, будет ли dynamic_cast основным фактором, влияющим на время выполнения, но на всякий случай его можно оптимизировать, добавив 2 атрибута-члена к вашему делегату (mutable unsigned int layerCount; и mutable QAbstractItemModel* savedModel;) и выполнив что-то вроде:

const Node* node = nullptr;

QModelIndex sourceIndex = inIndex;
const RegistryItemModel* regModel = nullptr;

//We test if the delegate knows the model used.
if (savedModel == inIndex.model()) {
    //We already did the mapping back to the source for inIndex.model()
    //We know how many layers of QAbstractProxyModel must be browsed through.
    for (unsigned int l = 0; l < layerCount; ++l) {
        auto proxyModel = static_cast<const QAbstractProxyModel*>(sourceIndex.model());
        sourceIndex = proxyModel->mapToSource(sourceIndex);
    }
    regModel = static_cast<const RegistryItemModel*>(sourceIndex.model());
}
else {
    //We have not done the mapping back to the source yet.
    //We must discover how many layers of QAbstractProxyModel there are.
    auto proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
    savedModel = const_cast<QAbstractItemModel*>(inIndex.model());
    layerCount = 0;
    //We map the index back to its source as many times as needed.
    //While we do so, we save how many layers there are so we can use the above faster loop next time.
    while (proxyModel) {
        ++layerCount;
        sourceIndex = proxyModel->mapToSource(sourceIndex);
        proxyModel = dynamic_cast<const QAbstractProxyModel*>(sourceIndex.model());
    }
    regModel = dynamic_cast<const RegistryItemModel*>(sourceIndex.model());
    if (!regModel)
        savedModel = nullptr;
}
if (regModel)
    node = regModel->node(sourceIndex);
[...]

Обратите внимание, что с этой оптимизацией вы не можете изменить количество слоев в вашей модели, за исключением случаев, когда вы меняете верхний слой, то есть слой, который вы добавляете/удаляете, является тем, который прикреплен к вашему виду.

не могли бы вы взглянуть на этот новый вопрос? stackoverflow.com/questions/76170343/… @Atmo

Ming 04.05.2023 08:59

Другие вопросы по теме