В настоящее время я переношу свой проект из QTreeWidget
в QtreeView
и имеют много проблем, вызванных плохим пониманием дизайна модельного представления Qt. До сих пор я не мог найти ответы даже в примерах Qt.
Я реализовал мой QAbstractItemModel
, Я возвращаю строки для просмотра в QTreeView
через data
метод. Теперь базовые данные будут меняться во время выполнения. Для этого моя модель подписана на уведомление, которое делает emit dataChanged(index(0,0), index(rowCount() - 1, LastColumn));
, Вопрос: как создать и очистить QModelIndex
объекты? Один из примеров переопределения в Qt index
метод, поэтому я сделал то же самое:
QModelIndex CFileListModel::index(int row, int column, const QModelIndex &/*parent*/) const
{
QModelIndex index = createIndex(row, column);
return index;
}
Однако в этом примере данные статичны, а в моем случае они меняются во время выполнения. Мой index
реализация правильная? Что, если index
вызывается более одного раза для одних и тех же координат? Нужно ли как-то очищать старые индексы перед испусканием dataChanged
?
Ваш вопрос об «удалении» индексов не имеет смысла в свете семантики C ++. У вас просто нет возможности уничтожить объект, который вы вернули по значению изнутри функции — по крайней мере, не прибегая к целенаправленным грязным взломам. Итак, давайте забудем об этом.
dataChanged
Сигнал и время жизни индексов на самом деле не связаны. Когда ваш index()
метод возвращает индекс, вы не тот, кто может его «удалить»; кто бы ни назвал вашу модель index()
Метод отвечает за разрушение индекса. Не берите в голову, что индекс, который вы выдаете, в любом случае не размещен в бесплатном хранилище, поэтому понятие удаления вообще не применяется.
QModelIndex
это то, что написано на коробке: индекс. Когда дело доходит до того, как это можно использовать, это очень похоже на итератор C ++. Он поставляется с несколькими оговорками, которые отражают предостережения итераторов:
Он должен быть создан моделью с использованием заводского метода index()
, Внутренне вы используете createIndex()
Фабрика, чтобы создать его для вас в модели. Подумайте о методах, возвращающих итераторы в контейнерах C ++:begin()
, end()
, так далее.).
Его нужно использовать сразу, а затем выбросить. Он не останется действительным, если вы внесете изменения в модель. То же самое общее ограничение применяется к итераторам контейнера C ++.
Если вам нужно сохранить модельный индекс с течением времени, используйте QPersistentModelIndex
, Стандартная библиотека C ++ не предлагает этого.
Время жизни индекса вне вашего контроля. Вы создаете это, вы даете это с ожиданием, что это будет использоваться согласно этому протоколу. Пользователь (например, представление) должен использовать его в соответствии с перечисленными выше ограничениями. Если представление, например, слишком долго удерживает индекс (посредством промежуточных модификаций), вполне нормально, что это приведет к неопределенному поведению (скажем, к аварийному завершению).
Когда вы излучаете (или получаете, если вы представление или модель прокси) dataChanged
Вы не должны ожидать, что какие-либо индексы, выданные до этого момента, останутся пригодными для использования. Постоянные индексы, конечно, все еще должны работать, но их можно аннулировать, если, скажем, индекс, на который указывает указатель, был удален (представьте, что ячейка удалена из электронной таблицы, не данные ячейки изменяются!).
Если вы выделили индекс, то эмитируйте dataChanged
, а также любой из методов вашей модели вызываются с этим старым индексом, вы можете свободно падать, утверждать, прерывать, что угодно.
Давайте также проясним, как вы используете dataChanged
: вы должны испускать его всякий раз, когда изменяется элемент данных с данным индексом. Вы должны быть максимально конкретными: это не Хорошая идея — просто сказать своим взглядам, что все изменилось, если на самом деле это не изменилось. Если один индекс изменился, подайте сигнал с topLeft
а также bottomRight
установить на тот же индекс. Если небольшая прямоугольная область изменилась, испустите углы этого прямоугольника. Если изменилось несколько несвязанных элементов, которые находятся слишком далеко, чтобы их можно было осмысленно объединить в небольшой заключительный индексный прямоугольник, вы должны указать такие изменения отдельно для каждого измененного элемента.
Вы должны обязательно использовать modeltest чтобы убедиться, что ваша модель ведет себя разумно.
Это можно сделать, добавив modeltest.cpp
а также modeltest.h
к вашему проекту, и создание тестера для каждого экземпляра модели. Вы можете сделать это прямо в вашей модели:
#include "modeltest.h"
MyModel(QObject * parent) : ... {
new ModelTest(this, parent);
...
}
Вам также необходимо обрабатывать постоянные индексы для вашей модели, и это отдельная проблема. В документации сказано:
Модели, которые предоставляют интерфейсы для изменяемых размеров структур данных, могут предоставлять реализации insertRows (), removeRows (), insertColumns () и removeColumns (). При реализации этих функций важно уведомлять любые связанные представления об изменениях размеров модели как до, так и после того, как они происходят:
- Реализация insertRows () должна вызывать beginInsertRows () перед вставкой новых строк в структуру данных и сразу после нее endInsertRows ().
- Реализация insertColumns () должна вызывать beginInsertColumns () перед вставкой новых столбцов в структуру данных, а endInsertColumns () сразу после этого.
- Реализация removeRows () должна вызывать beginRemoveRows (), прежде чем строки будут удалены из структуры данных, и endRemoveRows () сразу после этого.
- Реализация removeColumns () должна вызывать beginRemoveColumns () до того, как столбцы будут удалены из структуры данных, и endRemoveColumns () сразу после этого.
Частные сигналы, которые излучают эти функции, дают присоединенным компонентам возможность предпринять действия до того, как какие-либо данные станут недоступны. Инкапсуляция операций вставки и удаления с этими функциями начала и конца также позволяет модели правильно управлять постоянными модельными индексами. Если вы хотите, чтобы выбор был обработан правильно, вы должны убедиться, что вы вызываете эти функции. Если вы вставляете или удаляете элемент с дочерними элементами, вам не нужно вызывать эти функции для дочерних элементов. Другими словами, родительский элемент позаботится о своих дочерних элементах.
Других решений пока нет …