Qt World Summit: Register Today!

Presenting a tree model with a hierarchical set of comboboxes

  • Task is to provide a view for a simple read-only tree model with the help of *QComboboxes *to let the user choose a specific sub item. As questions about this topic are poping up very often, but rarely being answered, here an simple example. It is open for discussion on how to improve it.

    It was a quite tedious task after having started with QDataWidgetMapper, trying to use QStyledItemDelegate and other possible implementations. If you know it, it is basically rather simple and even well documented in the section "Parents of items" in the help file qthelp://org.qt-project.qtwidgets.540/qtwidgets/model-view-programming.html. The trick is to use hierarchicalIndex = treeModel->index(lower, 0, hierarchicalIndex) for each level of hierachy.

    An alternative solution is described at "Implementing_QTreeView_in_QComboBox_using_Qt":http://developer.nokia.com/community/wiki/Implementing_QTreeView_in_QComboBox_using_Qt-_Part_2
    unfortunately having several drawbacks as for example not working on Mac and not resizing well.

    Here is the complete code for main.cpp:
    #include <QObject>
    #include <QApplication>
    #include <QMainWindow>
    #include <QVBoxLayout>
    #include <QComboBox>
    #include <QStandardItemModel>
    #include <QTreeView>

    // macro to overcome overloading issue - I like it cryptic ;-)
    #define SIGTYPE(t, c, f) static_cast<void (c:: *)(t)>(&c::f)

    // some model parameters
    enum {
    Trees = 5,
    Branches = 4,
    Leaves = 3

    // our widget main window
    class MainWindow : public QWidget {

        explicit MainWindow(QWidget *parent = 0);
    private slots:
        void on_comboBox_1_currentIndexChanged(int arg1);       // select the tree
        void on_comboBox_2_currentIndexChanged(int arg1);       // select the branch
        void on_comboBox_3_currentIndexChanged(int);            // select the leaf
        QStandardItem *addItemAt(QStandardItem *parentItem, QString itemString);
        QModelIndex hierarchicalIndex(int highest, ...);
        QVBoxLayout *verticalLayout;
        QComboBox *comboBox_1;
        QComboBox *comboBox_2;
        QComboBox *comboBox_3;
        QTreeView *treeView;
        QStandardItemModel *treeModel;
        int tree, branch;


    MainWindow::MainWindow(QWidget *parent): QWidget(parent) {
    tree = 0;
    branch = 0;
    // compose a simple layout using 3 QComboBoxes and a QTreeView vertically aligned
    verticalLayout= new QVBoxLayout(this);

    comboBox_1 = new QComboBox(this);
    comboBox_2 = new QComboBox(this);
    comboBox_3 = new QComboBox(this);
    treeView = new QTreeView(this);
    treeModel = new QStandardItemModel(this);
    QList<QStandardItem*> itemlist;
    for (int tree = 0; tree < Trees; tree ++ ) {
        itemlist[1] = addItemAt(itemlist.at(0), QString("Tree %1").arg(tree));
        for (int branch = 0; branch < Branches; branch++) {
            itemlist[2] = addItemAt(itemlist.at(1),
                QString("Branch %2 of tree %1").arg(tree).arg(branch)
            for (int leaf = 0; leaf < Leaves; leaf ++) {
                itemlist[3] = addItemAt(itemlist.at(2),
                    QString("Leaf %3 of tree %1 (%2)")
    connect(comboBox_1, SIGTYPE(int, QComboBox, currentIndexChanged),
            this, &MainWindow::on_comboBox_1_currentIndexChanged
    connect(comboBox_2, SIGTYPE(int, QComboBox, currentIndexChanged),
            this, &MainWindow::on_comboBox_2_currentIndexChanged
    connect(comboBox_3, SIGTYPE(int, QComboBox, currentIndexChanged),
            this, &MainWindow::on_comboBox_3_currentIndexChanged
    // reverse order to avoid runtime errors


    MainWindow::~MainWindow() {

    QStandardItem *MainWindow::addItemAt(QStandardItem *parentItem, QString itemString) {
    QStandardItem *item = new QStandardItem(itemString);
    return item;

    // scans indices of a tree model (highestLevel, nextLevel, ...-1)
    // a negative int signals end of the variadic list
    QModelIndex MainWindow::hierarchicalIndex(int highest, ...) {
    va_list arguments;
    va_start(arguments, highest);
    QModelIndex hierarchicalIndex = treeModel->index(highest, 0);
    for (;;) {
    int lower = va_arg(arguments, int);
    if (lower < 0) break;
    hierarchicalIndex = treeModel->index(lower, 0, hierarchicalIndex);
    return hierarchicalIndex;

    void MainWindow::on_comboBox_1_currentIndexChanged(int arg1) {
    tree = arg1;
    comboBox_2->setRootModelIndex(hierarchicalIndex(tree, -1));
    comboBox_2->setCurrentIndex(0); // strangely enough we need to update manually here

    void MainWindow::on_comboBox_2_currentIndexChanged(int arg1) {
    branch = arg1;
    comboBox_3->setRootModelIndex(hierarchicalIndex(tree, branch, -1));

    void MainWindow::on_comboBox_3_currentIndexChanged(int /arg1/) {
    // here very likely we do our job

    int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    return a.exec();

    #include "main.moc"

    and the corresponding qmake file HierarchicalComboBox.pro:
    lessThan(QT_MAJOR_VERSION, 5): message("Requires Qt5")

    QT += core gui widgets

    TARGET = HierarchicalComboBox
    TEMPLATE = app

    SOURCES += main.cpp

Log in to reply