How to add QCheckBox to column and make model appear in QTreeView.



  • I'm using Qt 5.12. I'm trying to create a custom model as opposed to custom views and delegates for reason like keeping my applications data in the model and not having to sync it externally.

    My goal is to have a tree like the following where I can add checkboxes to only specific child nodes.

    Column 1               Column 2
    
    `- Root
       |- System1
       |   |- subsystem1   checkbox
       |   `- subsystem2   checkbox
       `- System2
           |- subsystem3   
           `- subsystem4    
    

    Aside from not knowing how to get the checkbox to appear. the problem I'm having is that the model is not showing up in my QTreeView. The view is empty.

    MyTreeModel header:

    class MyTreeModel : public QAbstractItemModel
    {
        Q_OBJECT
    
        private:
            QObject *root_item;
    
        public:
            explicit MyTreeModel(QObject *, QObject *parent = 0);
    
            Qt::ItemFlags flags(const QModelIndex &index) const;
            QVariant data(const QModelIndex &index, int role) const;
            QVariant headerData(int section, Qt::Orientation orientation,
                int role = Qt::DisplayRole) const;
            QModelIndex index(int row, int column, const QModelIndex &parent =
                QModelIndex()) const;
            int columnCount(const QModelIndex &parent = QModelIndex()) const;
            int rowCount(const QModelIndex &parent = QModelIndex()) const;
            QModelIndex parent(const QModelIndex &index) const;
    };
    

    MyTreeModel source:

    MyTreeModel::MyTreeModel(QObject *obj, QObject *parent) :
        QAbstractItemModel(parent)
    {
        root_item = obj;
    }
    
    Qt::ItemFlags MyTreeModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return Qt::ItemIsEnabled;
    
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    }
    
    QVariant MyTreeModel::data(const QModelIndex &index, int role) const
    { 
        if (!index.isValid())
            return QVariant();
    
        if (role == Qt::DisplayRole) {
            switch (index.column()) {
                case 0:
                    return static_cast<QObject *>
                        (index.internalPointer())->objectName();
    
                case 1:
                    return static_cast<QObject *>
                        (index.internalPointer())->metaObject()->className();
    
                default:
                    break;
            }
        }
        else if (role == Qt::ToolTipRole) {
            // ...
        }
    
        return QVariant();
    }
    
    QVariant MyTreeModel::headerData(int section, Qt::Orientation orient,
        int role) const
    {
        if (role != Qt::DisplayRole || orient != Qt::Horizontal)
            return QVariant();
    
        switch (section)
        {
            case 0:
                return QString("object");
    
            case 1:
                return QString("class");
    
            default:
                return QVariant();
        }
    }
    
    QModelIndex MyTreeModel::index(int row, int column,
        const QModelIndex &parent) const
    {
        QObject *parentObject;
    
        if (!parent.isValid())
            parentObject = root_item;
        else
            parentObject = static_cast<QObject *>(parent.internalPointer());
    
        if (row >= 0 && row < parentObject->children().count())
            return createIndex(row, column, parentObject->children().at(row));
        else
            return QModelIndex();
    }
    
    int MyTreeModel::columnCount(const QModelIndex &parent) const
    {
        return 2;
    }
    
    int MyTreeModel::rowCount(const QModelIndex &parent) const
    {
        QObject *parentObject;
    
        if (!parent.isValid())
            parentObject = root_item;
        else
            parentObject = static_cast<QObject *>(parent.internalPointer());
    
        return parentObject->children().count();
    }
    
    QModelIndex MyTreeModel::parent(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QModelIndex();
    
        QObject *indexObject = static_cast<QObject*>(index.internalPointer());
        QObject *parentObject = indexObject->parent();
    
        if (parentObject == root_item)
            return QModelIndex();
    
        QObject *grandParentObject = parentObject->parent();
    
        return createIndex(grandParentObject->children().indexOf(parentObject),
        0, parentObject);
    }           
    

    And I have this is my MainWindow constructor ...

    QObject root;
    QObject *subsys;
    
    root.setObjectName("Root");
        
    QObject *sys1 = new QObject(&root);
    sys1->setObjectName("System1");
    subsys = new QObject(sys1);
    subsys->setObjectName("subsystem1");
    subsys = new QObject(sys1);
    subsys->setObjectName("subsystem2");
    
    QObject *sys2 = new QObject(&root);
    bar->setObjectName("System2");
    subsys = new QObject(sys2);
    subsys->setObjectName("subsystem3");
    subsys = new QObject(sys2);
    subsys->setObjectName("subsystem4");
    
    MyTreeModel tree_model(&root);
    
    tree_view = new MyTreeView;
    tree_view->setModel(&tree_model);
                           
    projectLayout->addWidget(tree_view);    
    

    Any help appreciated.


  • Qt Champions 2018

    Your flags() reimplementation must return Qt::ItemIsUserCheckable and data() must return the desired checkState for Qt::CheckStateRole



  • Still having issues with this. I initiate my tree as such ...

    MyTreeItem *root_item = new MyTreeItem;
    MyTreeModel tree_model(root_item);
    
    tree_view = new MyTreeView;
    tree_view->setModel(&tree_model);
    
    // ...
    
    projectLayout->addWidget(tree_view);
    

    Then I load a file and build the tree dynamically using ...

    {
        QString mps = QString::fromStdString("Root Item");
                                                                               
        MyTreeItem* mp_item = model->get_root_item();
        mp_item->setObjectName(mps);
    
        for (auto& i : myList) {
            QString str = QString::fromStdString(m.get_text());
                                                                               
            MyTreeItem *item = new MyTreeItem(mp_item);
            item->setObjectName(str);
        }
    }
    

    My problem is that I can't see anything loading in my view. The following is MyTreeModel.

    MyTreeModel::MyTreeModel(MyTreeItem *item, QObject *parent) :
        QAbstractItemModel(parent)
    {
        root_item = item;
    }
    
    MyTreeModel::~MyTreeModel()  
    {
        delete root_item;
    }
    
    Qt::ItemFlags MyTreeModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return Qt::ItemIsEnabled;
    
        Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable;
    
        if (index.column() == 1)
            flags | Qt::ItemIsUserCheckable;
    
        return flags;
    }
    
    QVariant MyTreeModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
    
        MyTreeItem *mti = static_cast<MyTreeItem*>
            (index.internalPointer());
    
        if (role == Qt::CheckStateRole && index.column() == 1) {
            return static_cast<int>(mti->isChecked() ? Qt::Checked :
                Qt::Unchecked);
        }
    
        if (role != Qt::DisplayRole && role != Qt::EditRole)
            return QVariant();
    
        MyTreeItem *item = getItem(index);
    
        return item->data(index.column());
    }
    
    QVariant MyTreeModel::headerData(int section, Qt::Orientation orient,
        int role) const
    {
        if (role != Qt::DisplayRole || orient != Qt::Horizontal)
            return QVariant();
    
        switch (section)
        {
            case 0:
                return QString("Object");
    
            case 1:
                return QString("Class");
    
            default:
                return QVariant();
        }
    }
    
    QModelIndex MyTreeModel::index(int row, int column, 
        const QModelIndex &parent) const
    {
        MyTreeItem *parentObject;
    
        if (!parent.isValid())
            parentObject = root_item;
        else
            parentObject = static_cast<MyTreeItem *>(parent.internalPointer());
    
        if (row >= 0 && row < parentObject->children().count())
            return createIndex(row, column, parentObject->children().at(row));
        else
            return QModelIndex();
    }
    
    int MyTreeModel::columnCount(const QModelIndex &parent) const
    {
        return 2;
    }
    
    int MyTreeModel::rowCount(const QModelIndex &parent) const
    {
        MyTreeItem *parentObject;
    
        if (!parent.isValid())
            parentObject = root_item;
        else
            parentObject = static_cast<MyTreeItem *>(parent.internalPointer());
    
        return parentObject->children().count();
    }
    
    QModelIndex MyTreeModel::parent(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QModelIndex();
    
        QObject *indexObject = static_cast<QObject*>(index.internalPointer());
        QObject *parentObject = indexObject->parent();
    
        if (parentObject == root_item)
            return QModelIndex();
    
        QObject *grandParentObject = parentObject->parent();
    
        return createIndex(grandParentObject->children().indexOf(parentObject),
            0, parentObject);
    }
    
    MyTreeItem* MyTreeModel::getItem(const QModelIndex &index) const
    {
        if (index.isValid()) {
            MyTreeItem *item = static_cast<MyTreeItem*>
                (index.internalPointer());
    
            if (item)
                return item;
        }
    
        return root_item;
    }
    
    MyTreeItem* MyTreeModel::get_root_item() {
        return root_item;
    }

  • Lifetime Qt Champion

    Hi,

    @SRaD said in How to add QCheckBox to column and make model appear in QTreeView.:

    MyTreeModel tree_model(&root);

    tree_view = new MyTreeView;
    tree_view->setModel(&tree_model);

    tree_model is a local variable, it will be destroyed at the end of the method and thus your tree view won't have any data to show.



  • Thanks for catching that, however, I'm still not seeing any items. I am however seeing the headers get filled in now so at least something of it is working.

    After loading a file and dynamically creating new MyTreeItems, do I need to do anything to "reload" or refresh the view to get it to appear?

    Specifically, here's the piece of code that creates the items, but by this time, I've already called tree_view->setModel, which was called in the main window constructor..

    {
        QString mps = QString::fromStdString("Root Item");
                                                                               
        MyTreeItem* mp_item = model->get_root_item();
        mp_item->setObjectName(mps);
    
        for (auto& i : myList) {
            QString str = QString::fromStdString(m.get_text());
                                                                               
            MyTreeItem *item = new MyTreeItem(mp_item);
            item->setObjectName(str);
        }
    }

  • Qt Champions 2018

    I'm sorry but I don't think will compile.
    Please provide a minimal, compilable example which shows your problem. E.g

    int main (int argc, char **argv)
    {
      QApplication app(argc, argv);
      QTreeWidget treeWidget;
      QTreeWidgetItem *cities = new QTreeWidgetItem(&treeWidget);
      cities->setText(0, "Cities");
      treeWidget.show();
      return app.exec();
    }
    

    See http://doc.qt.io/qt-5/qtreewidgetitem.html#details


Log in to reply
 

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