Na prática, às vezes é necessário exibir uma estrutura de dados semelhante a uma árvore no QComboBox.
O componente padrão no Qt para essa estrutura de dados é o QTreeView, além disso,
O QComboBox pode exibir esse componente dentro de si, mas como sempre, há pequenos espaços na documentação, porque você precisa não apenas exibir a árvore, mas também definir o elemento atual selecionado pelo usuário.
Vamos ver como fazê-lo corretamente.
Primeiro, crie o próprio componente que exibirá os dados, para isso herdamos do QComboBox e fornecemos as propriedades necessárias.
Na parte fechada da classe, declaramos a variável m_view da classe QTreeView, que exibirá a árvore no QComboBox, redefinirá 2 funções responsáveis pelo comportamento do componente ao abrir e fechar:
- substituição de showPopup (); - executado quando o usuário expande a lista
- substituição de hidePopup (); - realizada quando o usuário seleciona um item clicando nele
Também adicionamos a função hideColumn (int n), que oculta as colunas necessárias no QTreeView, pois se o seu modelo consiste em várias colunas, o QComboBox mostrará todas elas (o componente padrão usa uma lista), que ficará muito feia
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; }; #endif //TREECOMBOBOX_H
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(); }
No construtor, configuramos a árvore que precisamos procurar para que fique “incorporada” no QComboBox, removem os cabeçalhos, ocultam os elementos de expansão e a definimos como um elemento de exibição.
O truque para instalar corretamente um item selecionado pelo usuário em um QComboBox são as funções showPopup () e hidePopup ().
Como o QComboBox trabalha com uma visualização de modelo "plana", ele não pode definir o índice correto do elemento selecionado pelo usuário em modelos de árvore, pois eles usam o índice relativo ao elemento pai para isso:
showPopup ()
elemento raiz - definimos o índice raiz como um índice de modelo inválido para que o QComboBox exiba todos os elementos do modelo.
hidePopup ()
elemento raiz - configuramos o índice do pai do elemento de modelo selecionado pelo usuário e, em seguida, em relação ao elemento pai, configuramos o elemento de usuário selecionado pelo índice.
É usado assim:
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(); }
