QTreeView: поддержание сопоставления между QModelIndex и базовыми данными

У меня проблемы с переходом с QTreeWidget на QtreeView. Вещи, которые были очевидны и тривиальны с QTreeWidget, кажутся невозможными с view. В частности: у меня есть главное окно с древовидной структурой. TreeView использует реализованную мной модель, но не напрямую, а через QSortFilterProxyModel, заданный как модель дерева. Теперь пользователь активирует элемент в дереве, и главные окна получают сигнал itemActivated(QModelIndex item). Как узнать, какой элемент базовых данных был активирован? Данные — это вектор, поэтому с помощью TreeWidget я мог просто сохранить векторный индекс элемента в QTreeWidgetItem, но QModelIndex даже не имеет setData API.


person Violet Giraffe    schedule 22.09.2013    source источник


Ответы (3)


Вы можете определить пользовательские роли в исходной модели, возвращая базовые данные или идентификатор (если он есть) в качестве варианта. Это имеет то преимущество, что работает с любым количеством прокси-моделей между ними, поскольку данные будут передаваться через модели без изменений, и теперь требуется сопоставление индексов.

Предположим, что модель содержит список контактов со значением struct/class Contact, содержащим данные. Для этого требуется, чтобы Contact был зарегистрирован через Q_DECLARE_METATYPE.

class ContactModel ... {
    ...

    enum Role {
        ContactRole=Qt::UserRole,
        ContactIdRole
    };

    QVariant data(...) const {
        ...
        const Contact& contact = ...get from datastructure...
        ...
        switch (role) {
        ...
        case ContactRole:
             return QVariant::fromValue( contact );
        case ContactIdRole:
             return contact.id;
        }
    }
    ...

И в коде, получающем индекс:

void SomeWidget::indexSelected(const QModelIndex& index)
{
    const int id = index.data(ContactModel::ContactIdRole).toInt();
    // look up Contact, do something with it

    //or:

    const Contact contact = index.data(ContactModel::ContactRole).value<Contact>();
    // do something with the contact

    ...
}

Индекс может быть из самой контактной модели или любого прокси поверх нее — код здесь не должен заботиться.

person Frank Osterfeld    schedule 22.09.2013

Как узнать, какой элемент базовых данных был активирован?

Инвертируя прокси-модель:

// supposing to connect this to your itemActivated signal
void onItemActivated(const QModelIndex &index)
{
    QModelIndex originalIndex = proxyModel->mapToSource(index);
    originalModel->doSomething(originalIndex);
}
person peppe    schedule 22.09.2013

Модель для хранения ваших данных. Данные больше не принадлежат элементам/QModelIndex в представлении. QModelIndex — это всего лишь уникальный идентификатор, передаваемый между представлением и моделью (в данном случае через QSortFilterProxyModel). Модель должна наследовать QAbstractItemModel, в которой есть несколько чистых виртуальных функций, которые необходимо определить (вы можете скопировать шаблон из http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html). Вы будете, например. должны определить QAbstractItemModel::data( const QModelIndex & index, int role = Qt::DisplayRole), который определяет, какие данные соответствуют конкретному QModelIndex.

QSortFilterProxyModel находится между представлением и моделью, но не меняет принципов модели. См. другой ответ на этот вопрос о том, как справиться с конверсией QModelIndex.

В заключение: QAbstractItemModel::data( const QModelIndex & index) предоставит вам данные для конкретного QModelIndex после того, как вы его определили.

person user2672165    schedule 22.09.2013