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 {
    Q_OBJECT

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    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
    
    private:
        QStandardItem *addItemAt(QStandardItem *parentItem, QString itemString);
        QModelIndex hierarchicalIndex(int highest, ...);
    private:
        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);
    
    verticalLayout->addWidget(comboBox_1);
    verticalLayout->addWidget(comboBox_2);
    verticalLayout->addWidget(comboBox_3);
    verticalLayout->addWidget(treeView);
    
    setLayout(verticalLayout);
    
    treeModel = new QStandardItemModel(this);
    QList<QStandardItem*> itemlist;
    itemlist.append(treeModel->invisibleRootItem());
    itemlist.append(NULL);
    for (int tree = 0; tree < Trees; tree ++ ) {
        itemlist[1] = addItemAt(itemlist.at(0), QString("Tree %1").arg(tree));
        itemlist.append(NULL);
        for (int branch = 0; branch < Branches; branch++) {
            itemlist[2] = addItemAt(itemlist.at(1),
                QString("Branch %2 of tree %1").arg(tree).arg(branch)
            );
            itemlist.append(NULL);
            for (int leaf = 0; leaf < Leaves; leaf ++) {
                itemlist[3] = addItemAt(itemlist.at(2),
                    QString("Leaf %3 of tree %1 (%2)")
                        .arg(tree).arg(branch).arg(leaf)
                );
            }
        }
    }
    treeView->setModel(treeModel);
    
    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
    comboBox_3->setModel(treeModel);
    comboBox_2->setModel(treeModel);
    comboBox_1->setModel(treeModel);
    

    }

    MainWindow::~MainWindow() {
    }

    QStandardItem *MainWindow::addItemAt(QStandardItem *parentItem, QString itemString) {
    QStandardItem *item = new QStandardItem(itemString);
    parentItem->appendRow(item);
    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);
    }
    va_end(arguments);
    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));
    comboBox_3->setCurrentIndex(0);
    }

    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;
    w.show();
    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
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.