Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Multiple QTreeWidget with unique item list



  • Hi everyone !

    I am trying to create an app that have multiple docks, and some of them needs to have a QTreeWidget to show some files list (which is always the same for all docks).
    The problem is I can't display one QTreeWidget instance several times, so I created several instances of my QTreeWidget. But to make sure that all my dock's Trees will display the same data and avoid multiple items definition, I defined my Tree's items as statics.
    So to add data to my Tree, I need to call a static method that add all my files to the good Item in my tree.
    Now the problem is that nothing happen when I add childs to an item through the static method, and I can't call the update method of the QTreeWidget because I am in a static method.

    What do you think I should do ? May be I'm doing it the wrong way...

    Thanks for your help !

    Code :

    #ifndef FILETREE_H
    #define FILETREE_H
    
    
    #include <QtWidgets>
    
    class FileTree: public QTreeWidget
    {
        public:
    
        FileTree();
        static void addFiles(QStringList files);
    
        private:
    
        static QList<QTreeWidgetItem *> *items;
        static QTreeWidgetItem *files;
        static QList<QTreeWidgetItem *> *filesList;
    };
    
    #endif // FILETREE_H
    
    #include "filetree.h"
    
    QList<QTreeWidgetItem*> *FileTree::items;
    QTreeWidgetItem *FileTree::files;
    QList<QTreeWidgetItem*> *FileTree::filesList;
    
    FileTree::FileTree()
    {
        QStringList headerLabels;
        headerLabels.push_back(tr("Name"));
        headerLabels.push_back(tr("Path"));
        headerLabels.push_back(tr("Info"));
        this->setColumnCount(headerLabels.count());
        this->setHeaderLabels(headerLabels);
        this->header()->resizeSection(0, 140);
        this->header()->resizeSection(1, 100);
        this->header()->resizeSection(2, 60);
        this->header()->setMinimumWidth(60);
        this->header()->setDefaultAlignment(Qt::AlignCenter);
    
        items = new QList<QTreeWidgetItem *>;
    
        files = new QTreeWidgetItem(this);
        files->setText(0, tr("Files"));
        files->setIcon(0, QIcon(":/rsc/files.svg"));
        filesList = new QList<QTreeWidgetItem *>;
        files->addChildren(*filesList);
        items->append(files);
    
        ...
    
        this->insertTopLevelItems(0, *items);
    }
    
    void FileTree::addFiles(QStringList files)
    {
        imageList->clear();
        for (int i=0;i<files.length();i++)
        {
            QFileInfo info (files.at(i));
            QTreeWidgetItem *image = new QTreeWidgetItem(images);
            image->setIcon(0, QIcon(":/rsc/Images.svg"));
            image->setText(0,files.at(i).split("/").last());
            image->setText(1,info.path());
            image->setText(2,QString::number(info.size()));
            imageList->append(image);
        }
    }
    
    

    and in my Mainwindow

    MainWindow::MainWindow()
    {
        ...
        dock->addWidget(dockTabWidget);
        dockTabWidget->addTab(tr("Tree"), new FileTree);
        ...
    }
    
    ...
    
    void MainWindow::openFiles()
    {
        QFileDialog dialog(this);
        QStringList filters;
        filters << tr("Text files (*.txt)");
        dialog.setNameFilters(filters);
        dialog.setFileMode(QFileDialog::ExistingFiles);
        if (dialog.exec())
        {
            FileTree::addFiles(dialog.selectedFiles());
        }
    }
    


  • @HB76

    to make sure that all my dock's Trees will display the same data

    QTreeWidget is a wrapper around QTreeView with its own internal data model for its items, and you cannot affect that. Therefore it will not be a good choice for sharing common data. If you want that, you will want to look at QTreeView instead, where you have control over the models used. You can have multiple QTreeViews using the same model instance as each other, which is what you want. And if you ever wanted to enhance, you can even have the treeviews show different views/sorts/filters of the same shared data model, as a bonus.



  • QTreeWidgetItem cannot be shared between QTreeWidget(s)
    I think you should look at The model/view architecture
    How about QTreeView + QStandardItemModel?



  • @JonB

    So if I define a single model for all my QTreeView instances I should be capable of adding and removing data to my model and it will update my QTreeViews ?



  • @HB76
    Yes, that is it how works for QTreeViews, and indeed for all Qt model-view classes. As you change data in the model all attached views update automatically to reflect the new data. This is because the model emits signals for changes and the views have slots on those signals to do their work. QTreeWidget is just a supplied widget deriving from QTreeView and supplying its own internal model, local to that QTreeWidget instance. If you go back to QTreeViews, you can have multiple ones all using the same shared model instance if you wish., which QTreeWidget cannot do.



  • So i've done all the modifications to use QTreeView + model instead of QTreeWidget. But now I got some problems for handling QTreeView.

    I started from the simple Tree Model example so all my class are basically the same exept for the setUpData method that I rewrited and renamed addData, to load any kinf of data at anytime.

    But I am facing some small problems using the QTreeView :

    • How is it possible to set Icons for QAbstractItemModel ?
    • When I run my code, the small expanding arrow is not showing, whereas it is when I run it from a test project.
    • When I add data to the QTreeView, the view doesn't update until I resize the widget or ask it to expand all. Is there a way to update it after adding data ?
    • Is it possible to expand only a specific section instead of expanding the whole tree ?
    • I used the clicked(const QModelIndex &index) signal of QAbstractItemView to get the content of the clicked item, but I only get the data from the index and not the data from the whole item (I would like to have a QVector<QVariant> for the whole row)

    Here is my slot connected to the clicked() signal :

    void MainWindow::getItem(const QModelIndex index)
    {
        QVariant data = view->model()->data(index);
        QModelIndex parent = view->model()->parent(index);
        qDebug() << data.toString() << index.row() << index.column() << parent.data().toString();
    }
    

    And a screen of the QTreeView :

    Sans titre.png

    Thank your for your help !



  • @HB76 I recommend you to use QStandardItemModel as the model, again.
    This will solve the first and the third problem of yours.
    If not:

    1. QAbstractItemModel do not have the function of set icon, that needs to be done by the subclass.
      Neither does this example, unless you use a column for icon only and change the data() function.
      Or you should have at least two variables for each column each item, to save both the icon and the content.
    2. Not sure yet.
      Your view looks have some color set. Do you use stylesheet or something like that?
    3. If you want the view to update when changing the model data, you need to use those begin*() and end*() functions in QAbstractItemModel before and after the change. They'll send signals to the view. You can start with
    void beginInsertRows(const QModelIndex &parent, int first, int last)
    void endInsertRows()
    
    1. Not understand.
    2. You cannot get the data for the whole row from a QModelIndex.
      But you can get its sibings' index by
    QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const
    

    Of course, if you write the model and the item class yourself, you can do whatever you want.
    For example, you can write a method to get the item from the index, and then get the QVector<QVariant> from the item.



  • @Bonnie thank you for your answer, I got confused between QStandardItemModel and QAbstractItemModel in the doc but I will try with QStandardItemModel.

    For the 4th point, I was wondering if it is possible, when you add some data to the tree (for example in the Images item of the tree), to expand only the Images item to see all it's childs but not the others items (sorry if my question was/is not very clear enough, I'm struggling with my english)

    And for the 2nd point, yes I'm using a stylesheet for my QTreeView which is :

    view->setStyleSheet("QTreeView {"
                            "   background-color: #C4C4C4;"
                            "   color: black;"
                            "}"
                            "QTreeView::item {"
                            "   border: none;"
                            "}"
                            "QTreeView::item:selected {"
                            "   border: 1px solid #0A94A8;"
                            "   background-color: #0A94A8;"
                            "}"
                            "QHeaderView {"
                            "   font-family: Helvetica;"
                            "   font: bold 12px;"
                            "}"
                            "QHeaderView::section {"
                            "   background-color: #8F8F8F;"
                            "   color: black;"
                            "   border: 1px solid #4F4F4F;"
                            "}");
    


  • @HB76 I'm gonna answer the 4th, if I understand it correctly.
    Yes, you can expand any item you want using

    [slot] void QTreeView::expand(const QModelIndex &index)
    

    or

    void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
    

Log in to reply