Ниже мое изменение кода для реализации сортировки без учета регистра:
Пользовательский 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> тип ''неизвестный тип''
@Atmo, спасибо, по умолчанию это чисто виртуальная функция, когда mTreeView->setModel использует mModel, сбоев не происходит.
мне немного любопытно; если у него чисто виртуальный метод, как получить экземпляр? Или я должен сказать: не могли бы вы предоставить код RegistryItemModel::node
, чтобы мы выяснили, как компилятор не кричит на вас за создание экземпляра абстрактного класса? Что касается комментария, который вы добавили, это действительно большая подсказка. Но боюсь без ответа на мой предыдущий вопрос у меня все равно не будет полной картины.
@Atmo Существуют NodeKey и NodeDir, производные от Node, и Isdefault реализован в этих двух классах. Я прикрепил картинку, чтобы показать, откуда взялся этот «QModelIndex inIndex». Когда я передаю прокси-модель в mTreeView->setModel(), этот «QModelIndex inIndex» не совпадает, и mModel->node(inIndex) не возвращает ожидаемый NodeKey или NodeDir.
В будущем, пожалуйста, размещайте код, а не картинки. Что бы вы сделали, если бы никто не ответил на ваш вопрос? Во всяком случае, я ответил на него.
@Atmo Спасибо. Мне сказали, что еще один способ реализовать сортировку без учета регистра — это переопределить виртуальный QAbstractItemModel::sort() для реализации вашей собственной сортировки без учета регистра. Вы знаете, где я могу найти пример кода?
Как вы уже догадались, проблема в том, что 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
Эта ошибка не имеет ничего общего с взаимодействием модели и делегата; Предоставьте код, в котором, по вашему мнению, возникает ошибка (не скриншот) и дополнительную информацию об отладке. Кроме того, мне кажется странным, что вы так обрамили и вручную выделили строку, когда могли оставить отладчик, как будто ошибка возникает не там; это может быть внутри метода isDefault. Кстати, вы работаете на C++, поэтому следует избегать приведения типов
NULL
или C (+QStyleOptionViewItemV4
уже давно устарел, см. здесь).