Стандартным компонентом в Qt для такой структуры данных является QTreeView, более того,
QComboBox умеет отображать этот компонент внутри себя, но как всегда, в документации существуют небольшие пробелы, ведь нужно не только отображать дерево, но и устанавливать текущим, выбранный пользователем элемент.
Давайте разберём как правильно это
Во первых создадим сам компонент, который будет отображать данные, для этого наследуемся от QComboBox наделяем его нужными нам свойствами.
Объявим в закрытой части класса переменную m_view, класса QTreeView, которая будет отображать дерево в QComboBox,
переопределим 2 функции, которые отвечают за поведение компонента, при раскрытии и закрытии:
- void showPopup() override; — выполняется, когда пользователь раскрывает список
- void hidePopup() override; — выполняется, когда пользователь выбрал элемент кликнув по нему
Так же добавим функцию hideColumn(int n), которая будет скрывать, нужные вам, колонки в QTreeView, так как если ваша модель состоит из нескольких колонок, combobox — покажет их все (стандартный компонент использует список), что будет выглядеть очень некрасиво
treecombobox.h
#ifndef TREECOMBOBOX_H #define TREECOMBOBOX_H #include <QtWidgets/QComboBox> #include <QtWidgets/QTreeView> class TreeComboBox final : public QComboBox { public: TreeComboBox(); void showPopup() override; void hidePopup() override; void hideColumn(int n); void expandAll(); void selectIndex(const QModelIndex &index); private: QTreeView *m_view = nullptr; };
treecombobox.cpp
TreeComboBox::TreeComboBox() { m_view = new QTreeView; m_view->setFrameShape(QFrame::NoFrame); m_view->setEditTriggers(QTreeView::NoEditTriggers); m_view->setAlternatingRowColors(true); m_view->setSelectionBehavior(QTreeView::SelectRows); m_view->setRootIsDecorated(false); m_view->setWordWrap(true); m_view->setAllColumnsShowFocus(true); m_view->setItemsExpandable(false); setView(m_view); m_view->header()->setVisible(false); } void TreeComboBox::hideColumn(int n) { m_view->hideColumn(n); } void TreeComboBox::expandAll() { m_view->expandAll(); } void TreeComboBox::selectIndex(const QModelIndex &index) { setRootModelIndex(index.parent()); setCurrentIndex(index.row()); m_view->setCurrentIndex( index ); } void TreeComboBox::showPopup() { setRootModelIndex(QModelIndex()); QComboBox::showPopup(); } void TreeComboBox::hidePopup() { setRootModelIndex(m_view->currentIndex().parent()); setCurrentIndex( m_view->currentIndex().row()); QComboBox::hidePopup(); }
В конструкторе, мы устанавливаем у дерева, нужный нам вид, чтобы оно выглядило «встроенным» в QComboBox, убираем заголовки, скрываем элементы раскрытия, и устанавливаем его как элемент отображения.
Вся хитрость, для правильной установки выбранного пользователем элемента в QComboBox, заключается в функциях showPopup() и hidePopup().
Так как QComboBox работает с «плоским» модельным представлением, он не может установить правильный индекс, выбранного пользователем элемента в древовидных моделях, так как они используют индекс относительно родительского элемента, для этого:
showPopup()
корневым элементом — мы устанавливаем корневым индексом недействительный индекс модели, чтобы QComboBox отобразил все элементы модели.
hidePopup()
корневым элементом — мы устанавливаем индекс родителя, выбранного полльзователем элемента модели,
а затем уже относительно родительского элемента, устанавливаем выбранный пользовательский элемент по индексу.
Используется это всё примерно так:
int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; QStandardItemModel model; QStandardItem *parentItem = model.invisibleRootItem(); for (int i = 0; i < 4; ++i) { QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); parentItem->appendRow(item); parentItem = item; } TreeComboBox t; t.setModel(&model); t.expandAll(); auto lay = new QVBoxLayout; lay->addWidget( &t); w.setLayout(lay); w.show(); return a.exec(); }
ссылка на оригинал статьи https://habr.com/post/420977/
Добавить комментарий