Tricks QComboBox + QTreeView

  • Tutorial
In practice, it is sometimes necessary to show a tree data structure in QComboBox.

QTreeView is a standard component in Qt for such a data structure, moreover,
QComboBox can display this component inside itself, but as always, there are small spaces in the documentation, because you need not only to display the tree, but also to set the current user-selected item.

Let's see how to do it right

First, we will create the component itself, which will display the data, for this we inherit from QComboBox and assign it with the properties we need.

In the closed part of the class, we declare the variable m_view of the QTreeView class, which will display the tree in QComboBox, redefine the 2 functions that are responsible for the behavior of the component when expanding and closing:

  • void showPopup () override; - executed when the user opens the list
  • void hidePopup () override; - executed when the user selected an item by clicking on it.

We also add the hideColumn (int n) function, which will hide the columns you need in QTreeView, because if your model consists of several columns, QComboBox will show them all (the standard component uses the list), which will look very ugly

treecombobox.h

#ifndef TREECOMBOBOX_H#define TREECOMBOBOX_H#include<QtWidgets/QComboBox>#include<QtWidgets/QTreeView>classTreeComboBoxfinal :public QComboBox
{
public:
    TreeComboBox();
    voidshowPopup() override;
    voidhidePopup() override;
    voidhideColumn(int n);
    voidexpandAll();
    voidselectIndex(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();
}

In the constructor, we set the view we need in the tree so that it looks “embedded” in QComboBox, we remove the headers, hide the expansion elements and set it as a display element.

The whole trick to properly setting a user-selected item in QComboBox is in the showPopup () and hidePopup () functions.

Since QComboBox works with a “flat” model view, it cannot set the correct index, the user selected item in the tree models, since they use the index relative to the parent element, for this:
showPopup ()

root element - we set the root index to an invalid model index so that QComboBox displays all the elements of the model.
hidePopup ()

root element - we set the parent index of the model element chosen by the user, and then relative to the parent element, we set the selected user element by index.

It's all used like this:

intmain(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();
}


Also popular now: