Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. What should I do to use treeView childs to select a subset of data to show in a TableView?
QtWS25 Last Chance

What should I do to use treeView childs to select a subset of data to show in a TableView?

Scheduled Pinned Locked Moved Solved General and Desktop
17 Posts 5 Posters 1.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    Vil_JL
    wrote on 10 Jun 2024, 04:14 last edited by Vil_JL 6 Oct 2024, 04:35
    #1

    Hello. Here I'm trying to create an Qt model/view that shows different data in a TableView from a model depending of the child selected/clicked in the QTreeView, but I'm new at this and the examples I'm finding does not do anything close to this. Also I'm not finding the Qt Documentation to be really clear about how to do it either.

    15c625a8-9ce0-4828-ba68-02c24ef7f5ba-image.png

    I stored the data in a QMap just as an example data and used it to fill the model of the table, but what should I do to make the tree and the table to work together? How can I tell the model to show only the data of the child I clicked in the treeview? I read something about proxies but I'm not sure if it applies for this case. Please help me with your suggestions.

    At the time I'm using a model for the treeview and another model for the tableview, that's why I have two Qmaps.

        QMap<QString,QList<QString>> namesMap;
        namesMap = {{"Parent #1", {"Child 1-1", "Child 1-2", "Child 1-3"}},
                    {"Parent #2", {"Child 2-1", "Child 2-2"}},
                    {"Parent #3", {"Child 3-1"}}};
    
        QMap<QString,QList<QList<QString>>> dataMap;
        dataMap = {{"Child 1-1", {{"Name 1", "00000000000000", "1-1"},{"Name 2", "00000000000000", ""}}},
                   {"Child 1-2", {{"Name 1", "00000000000000", "1-2"},{"Name 2", "00000000000000", ""}}},
                   {"Child 1-3", {{"Name 1", "00000000000000", "1-3"},{"Name 2", "00000000000000", ""}}},
                   {"Child 2-1", {{"Name 1", "00000000000000", "2-1"},{"Name 2", "00000000000000", ""}}},
                   {"Child 2-2", {{"Name 1", "00000000000000", "2-2"},{"Name 2", "00000000000000", ""}}},
                   {"Child 3-1", {{"Name 1", "00000000000000", "3-1"},{"Name 2", "00000000000000", ""}}}};
    

    This is how I set the model I used to work with the tableview, and the model for the treeview.

        tableModel = new QStandardItemModel(3,3,this);
    
        QMap<QString,QList<QList<QString>>>::iterator itr;
        for (itr = dataMap.begin(); itr != dataMap.end(); ++itr)
        {
            int row = 0;
            // iterating through the Qlist
            QListIterator<QList<QString>> i(itr.value());
            while (i.hasNext())
            {
                QList list = i.next();
                for (int col = 0; col < list.size(); ++col)
                {
                    QModelIndex index = tableModel->index(row, col, QModelIndex());
                    tableModel->setData(index, list.at(col));
                }
                ++row;
            }
        }
    
        treeModel = new QStandardItemModel(this);
    
        QStandardItem *root = treeModel->invisibleRootItem();
        QList<QStandardItem*> rowItems;
    
        QMap<QString,QList<QString>>::iterator iter;
        for (iter = namesMap.begin(); iter != namesMap.end(); ++iter)
        {
            QString parentName = QString(iter.key());
            QStandardItem *parent = new QStandardItem(parentName);
            parent->setEditable(false);
            rowItems << parent;
            root->appendRow(rowItems);
            rowItems.clear();
    
            QListIterator<QString> i(iter.value());
            while (i.hasNext())
            {
                QString childName = QString(i.next());
                QStandardItem *item = new QStandardItem(childName);
                item->setEditable(false);
                rowItems << item;
                parent->appendRow(rowItems);
                rowItems.clear();
            }
        }
    
    treeView = new QTreeView(this);
    treeView->setModel(treeModel);
    
    dataTable = new QTableView(this);
    dataTable->setModel(tableModel);
    
    J 1 Reply Last reply 10 Jun 2024, 08:44
    0
    • J JonB
      12 Jun 2024, 15:18

      @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

      to select one element in an tree to show me in a table the values it has stored in that tree element (or use the tree element as a key to look for the data), and to be able to edit those values (I think I already sorted that out with a delegate).

      Since it's "one element" you might have been better to use the QDataWidgetMapper which would have produced a bunch of label-editwidget pairs in lines in the right-hand pane, instead of a single row in a generic tableview with columns for each value. But that's up to you and we will stick with the table view.

      You have chosen to create to keep and create the models/maps for the tree part (namesMap) and the table part (dataMap) separate from each other. That means we cannot somehow use a selection in the tree view/model to directly access an element in the table view/model. We could have done that by index (from the selection) had they been in the same model, but we can't with separate models.

      I don't have time to write the whole thing for you. What you will need to code is:

      • You only want to select a single row in the tree. I think QTreeView or its selection model allows you to specify "only allow single selection". You might want to add that. If you don't then your code should check for multiple rows selected and perhaps decide to have the table view show detail on only the first one selected.

      • You only want to be able to select a leaf in the tree, not a node. Either find a setting for that if there is one, or make your code ignore a selection if it is a node not a leaf, or make the table view show "nothing" if what is selected is not a leaf.

      • At this point you have a single leaf selected, in namesMap. You want to display information about that from dataMap.

      • Because you cannot use indexes into namesMap to directly access elements in dataMap, you need to access (from the current selection) the right element in namesMap to retrieve the string of the "Child" item (e.g. Child 1-2) you will need to look up in dataMap.

      • At this point you have e.g. Child 1-2 string. You want to display that in the table view. Your problem is that your tableModel has all the rows from dataMap. But you're only interested in one. You have about 3 choices:

      1. Don't pre-populate tableModel with all the rows. Create a new table model or clear out an existing one. When a leaf is selected in the tree populate the table model with just the matching single row from dataMap.

      2. If you do have all the rows already in the table model/view, would you be prepared to do something like just highlight the selected one and/or "dim" the unselected ones?

      3. To do it properly (and this is what I would recommend/experienced users would do) where the table model has all the rows but you only want one to be shown you need to filter the model to pick out just the one you want to show in the table view. Qt's QSortFilterProxyModel Class allows for that. You have to interpose that between the underlying source model (tableModel) and the table view, like:

      QStandardItemModel tableModel;
      QSortFilterProxyModel tableFilterModel;
      QTableView tableView;
      tableFilterModel.setSourceModel(&tableModel);
      tableView.setModel(&tableFilterModel);
      

      Then, armed with the string Child 1-2 from the selected leaf in the tree model you can use tableFilterModel.setFilterFixedString("Child 1-2"), and that will make the table view show only that row.

      V Offline
      V Offline
      Vil_JL
      wrote on 25 Jun 2024, 19:18 last edited by
      #16

      Thank you very much, @JonB. It took me a while but I finally achieved what I was trying to do, and your suggestions helped me a lot.

      @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

      You only want to select a single row in the tree. I think QTreeView or its selection model allows you to specify "only allow single selection". You might want to add that. If you don't then your code should check for multiple rows selected and perhaps decide to have the table view show detail on only the first one selected.

      You only want to be able to select a leaf in the tree, not a node. Either find a setting for that if there is one, or make your code ignore a selection if it is a node not a leaf, or make the table view show "nothing" if what is selected is not a leaf.

      Here I coded my slot function to get the selected item on the tree:

      // To populate the Tableview with data through the treeView:
          QItemSelectionModel *selectionModel = treeView->selectionModel(); // selection changes shall trigger a slot
          connect(selectionModel, &QItemSelectionModel::selectionChanged,
                  this, &MainWindow::treeChild_selection);
      
      } // end of MainWindow
      
      void MainWindow::treeChild_selection(const QItemSelection &/*selected*/, 
                                           const QItemSelection &/*deselected*/)
      {
          // to get the text of the selected item
          const QModelIndex index = treeView->selectionModel()->currentIndex();
          QString selectedLeaf = index.data(Qt::DisplayRole).toString();
      

      @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

      Don't pre-populate tableModel with all the rows. Create a new table model or clear out an existing one. When a leaf is selected in the tree populate the table model with just the matching single row from dataMap.

      I changed the table model to be empty of data so the function will be the one to fill it:

      // Setting the table model
          tableModel = new QStandardItemModel(3,3,this);
          tableModel->setHeaderData(0, Qt::Horizontal, tr("1"));
          tableModel->setHeaderData(1, Qt::Horizontal, tr("2"));
          tableModel->setHeaderData(2, Qt::Horizontal, tr("3"));
      
      void MainWindow::treeChild_selection(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
      {
          // to get the text of the selected item
          const QModelIndex index = treeView->selectionModel()->currentIndex();
          QString selectedLeaf = index.data(Qt::DisplayRole).toString();
      
          // iterating through the dataMap to find the rows corresponding to the selected item
          QMap<QString,QList<QList<QString>>>::iterator itr;
          for (itr = dataMap->begin(); itr != dataMap->end(); ++itr)
          {
              if (itr.key() == selectedLeaf)
              {
                  // look up code to fill the tableView with the item data from the dataMap
              }
      
              // if a parent item is selected then the table is cleared of any data
              if (selectedLeaf == "Parent #1" || selectedLeaf == "Parent #2" || selectedLeaf == "Parent #3")
              {
                  for (int row = 0; row < 3; ++row) {
                      for (int col = 0; col < 3; ++col) {
                          QModelIndex index2 = tableModel->index(row, col, QModelIndex());
                          tableModel->clearItemData(index2);
                      }
                  }
              }
          }
      
      }
      

      So that's it, maybe it looks rough but it works.

      (I made some changes in the way I declared and defined the Qmaps initially too, but I don't want to spam this answer with a lot of code. If anyone is curious just ask)

      1 Reply Last reply
      1
      • V Vil_JL
        10 Jun 2024, 04:14

        Hello. Here I'm trying to create an Qt model/view that shows different data in a TableView from a model depending of the child selected/clicked in the QTreeView, but I'm new at this and the examples I'm finding does not do anything close to this. Also I'm not finding the Qt Documentation to be really clear about how to do it either.

        15c625a8-9ce0-4828-ba68-02c24ef7f5ba-image.png

        I stored the data in a QMap just as an example data and used it to fill the model of the table, but what should I do to make the tree and the table to work together? How can I tell the model to show only the data of the child I clicked in the treeview? I read something about proxies but I'm not sure if it applies for this case. Please help me with your suggestions.

        At the time I'm using a model for the treeview and another model for the tableview, that's why I have two Qmaps.

            QMap<QString,QList<QString>> namesMap;
            namesMap = {{"Parent #1", {"Child 1-1", "Child 1-2", "Child 1-3"}},
                        {"Parent #2", {"Child 2-1", "Child 2-2"}},
                        {"Parent #3", {"Child 3-1"}}};
        
            QMap<QString,QList<QList<QString>>> dataMap;
            dataMap = {{"Child 1-1", {{"Name 1", "00000000000000", "1-1"},{"Name 2", "00000000000000", ""}}},
                       {"Child 1-2", {{"Name 1", "00000000000000", "1-2"},{"Name 2", "00000000000000", ""}}},
                       {"Child 1-3", {{"Name 1", "00000000000000", "1-3"},{"Name 2", "00000000000000", ""}}},
                       {"Child 2-1", {{"Name 1", "00000000000000", "2-1"},{"Name 2", "00000000000000", ""}}},
                       {"Child 2-2", {{"Name 1", "00000000000000", "2-2"},{"Name 2", "00000000000000", ""}}},
                       {"Child 3-1", {{"Name 1", "00000000000000", "3-1"},{"Name 2", "00000000000000", ""}}}};
        

        This is how I set the model I used to work with the tableview, and the model for the treeview.

            tableModel = new QStandardItemModel(3,3,this);
        
            QMap<QString,QList<QList<QString>>>::iterator itr;
            for (itr = dataMap.begin(); itr != dataMap.end(); ++itr)
            {
                int row = 0;
                // iterating through the Qlist
                QListIterator<QList<QString>> i(itr.value());
                while (i.hasNext())
                {
                    QList list = i.next();
                    for (int col = 0; col < list.size(); ++col)
                    {
                        QModelIndex index = tableModel->index(row, col, QModelIndex());
                        tableModel->setData(index, list.at(col));
                    }
                    ++row;
                }
            }
        
            treeModel = new QStandardItemModel(this);
        
            QStandardItem *root = treeModel->invisibleRootItem();
            QList<QStandardItem*> rowItems;
        
            QMap<QString,QList<QString>>::iterator iter;
            for (iter = namesMap.begin(); iter != namesMap.end(); ++iter)
            {
                QString parentName = QString(iter.key());
                QStandardItem *parent = new QStandardItem(parentName);
                parent->setEditable(false);
                rowItems << parent;
                root->appendRow(rowItems);
                rowItems.clear();
        
                QListIterator<QString> i(iter.value());
                while (i.hasNext())
                {
                    QString childName = QString(i.next());
                    QStandardItem *item = new QStandardItem(childName);
                    item->setEditable(false);
                    rowItems << item;
                    parent->appendRow(rowItems);
                    rowItems.clear();
                }
            }
        
        treeView = new QTreeView(this);
        treeView->setModel(treeModel);
        
        dataTable = new QTableView(this);
        dataTable->setModel(tableModel);
        
        J Offline
        J Offline
        JonB
        wrote on 10 Jun 2024, 08:44 last edited by
        #2

        @Vil_JL
        You only want the right-hand pane to show information about the items selected in the left-hand pane. So you need to know when an item in the left-hand treeview is selected/selection changes. Various ways:

        • Subclass QTreeView and reimplement void QTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected).

        • Don't need to subclass, neater is that every QTreeView has a default QItemSelectionModel *QAbstractItemView::selectionModel() const. That manages what items are selected. It also emits a void QItemSelectionModel::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) signal to which you can attach a slot, so you will know when a new item is selected in the lefthand pane and can repopulate the righthand pane correspondingly. See e.g. https://stackoverflow.com/questions/2062889/qtableview-what-signal-is-sent-when-user-selects-a-row-by-clicking-to-it (applies to QTreeView as well).

        • At present your right-hand pane is a full table. To show only the one selected row you could either just clear and re-populate it with one row each time, or you could put a proxy model (such as a QSortFilterProxyModel) between the treeview model and the tableview) and filter so as to show only one row.

        • But if your right-hand view is only going to show information about one row at a time why have it as a table model at all? You can if you wish, but seems a bit wasteful when it only ever show one row. Yours sounds like a standard master-detail view (master is many rows, detail is one row). You could make the right-hand pane just be, say, a label+value for each column of the selected item you want to show. Or, if you want to edit those values you could use QDataWidgetMapper which displays individual widgets for each column in a selected row and does all the work for you.

        1 Reply Last reply
        3
        • J JonB referenced this topic on 10 Jun 2024, 08:55
        • V Offline
          V Offline
          Vil_JL
          wrote on 10 Jun 2024, 21:52 last edited by
          #3

          Thank you for answering.
          About your second point, I don't understand how to implement the QItemSelectionModel::selectionChanged:

          void QItemSelectionModel::selectionChanged ( const QItemSelection & selected, const QItemSelection & deselected )   [signal]
          

          What should I put in the &selected and &deselected places, and is it going inside a connect like this?:

          connect(treeView, QItemSelectionModel::selectionChanged(), dataTable, SLOT());
          

          How is the slot going to receive that signal?
          Thank you very much.

          J 1 Reply Last reply 10 Jun 2024, 22:24
          0
          • V Vil_JL
            10 Jun 2024, 21:52

            Thank you for answering.
            About your second point, I don't understand how to implement the QItemSelectionModel::selectionChanged:

            void QItemSelectionModel::selectionChanged ( const QItemSelection & selected, const QItemSelection & deselected )   [signal]
            

            What should I put in the &selected and &deselected places, and is it going inside a connect like this?:

            connect(treeView, QItemSelectionModel::selectionChanged(), dataTable, SLOT());
            

            How is the slot going to receive that signal?
            Thank you very much.

            J Offline
            J Offline
            JonB
            wrote on 10 Jun 2024, 22:24 last edited by JonB 6 Oct 2024, 22:24
            #4

            @Vil_JL
            The examples in
            https://doc.qt.io/qt-6/signalsandslots.html
            https://wiki.qt.io/New_Signal_Slot_Syntax
            show the correct syntax for connecting signals and slots. Only use the new style, do not use SIGNAL() or SLOT() macros. Have a go yourself because you need to learn/discover, come back if you get stuck.

            Pl45m4P V 2 Replies Last reply 10 Jun 2024, 22:59
            3
            • J JonB
              10 Jun 2024, 22:24

              @Vil_JL
              The examples in
              https://doc.qt.io/qt-6/signalsandslots.html
              https://wiki.qt.io/New_Signal_Slot_Syntax
              show the correct syntax for connecting signals and slots. Only use the new style, do not use SIGNAL() or SLOT() macros. Have a go yourself because you need to learn/discover, come back if you get stuck.

              Pl45m4P Online
              Pl45m4P Online
              Pl45m4
              wrote on 10 Jun 2024, 22:59 last edited by
              #5

              @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

              https://wiki.qt.io/New_Signal_Slot_Syntax

              In a decade from now, when Qt10 was released, we will still posts links about the "NEW" signal and slot syntax here 😂😂
              It's inside people's brains and you can't get rid if it :D


              If debugging is the process of removing software bugs, then programming must be the process of putting them in.

              ~E. W. Dijkstra

              1 Reply Last reply
              1
              • J JonB
                10 Jun 2024, 22:24

                @Vil_JL
                The examples in
                https://doc.qt.io/qt-6/signalsandslots.html
                https://wiki.qt.io/New_Signal_Slot_Syntax
                show the correct syntax for connecting signals and slots. Only use the new style, do not use SIGNAL() or SLOT() macros. Have a go yourself because you need to learn/discover, come back if you get stuck.

                V Offline
                V Offline
                Vil_JL
                wrote on 11 Jun 2024, 06:56 last edited by
                #6

                @JonB What about this?

                connect(treeView->selectionModel()->selectedRows(), &QItemSelectionModel::selectionChanged, dataTable, &MainWindow::treeChild_clicked);
                

                And treeChild_clicked is my Slot function for the QTableView, declared like this:

                void MainWindow::treeChild_clicked(const QItemSelection &selected, const QItemSelection &deselected) {}
                

                But I still don't get how to define the selected and deselected QItemSelection variables.

                jsulmJ J 2 Replies Last reply 11 Jun 2024, 07:06
                0
                • V Vil_JL
                  11 Jun 2024, 06:56

                  @JonB What about this?

                  connect(treeView->selectionModel()->selectedRows(), &QItemSelectionModel::selectionChanged, dataTable, &MainWindow::treeChild_clicked);
                  

                  And treeChild_clicked is my Slot function for the QTableView, declared like this:

                  void MainWindow::treeChild_clicked(const QItemSelection &selected, const QItemSelection &deselected) {}
                  

                  But I still don't get how to define the selected and deselected QItemSelection variables.

                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on 11 Jun 2024, 07:06 last edited by
                  #7

                  @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                  What about this?

                  treeView->selectionModel()->selectedRows() returns a QList which does not have selectionChanged signal - why do you call selectedRows()?

                  "But I still don't get how to define the selected and deselected QItemSelection variables" - what do you mean? The signal will pass both to the slot.

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  V 1 Reply Last reply 12 Jun 2024, 04:05
                  1
                  • jeremy_kJ Offline
                    jeremy_kJ Offline
                    jeremy_k
                    wrote on 11 Jun 2024, 07:26 last edited by jeremy_k 6 Nov 2024, 07:48
                    #8

                    This is a simple technique that I find effective.

                    All data goes in a single model. This makes it easier to synchronize across multiple views. Child data that should only appear in the table view can be hidden by a number of techniques. The example below makes these nodes a child of the second column. Filter models and disabling user expansion of rows also work.

                        QStandardItemModel model;
                        for (int i=0; i < 3; i++) {
                            QList<QStandardItem *> items{ new QStandardItem(QString::number(i)), new QStandardItem } ;
                            QStandardItem *child = new QStandardItem(QString("Child of %1").arg(QString::number(i)));
                            items[1]->appendRow(child);
                            model.appendRow(items);
                        }
                    
                        QTreeView tree;
                        tree.setModel(&model);
                        tree.hideColumn(1);
                        tree.show();
                    
                        QTableView table;
                        table.setModel(&model);
                        QItemSelectionModel *sm = tree.selectionModel();
                        QObject::connect(sm, &QItemSelectionModel::currentChanged,
                                         &table, [&table](const QModelIndex &current) { table.setRootIndex(current.siblingAtColumn(1)); });
                        table.show();
                    

                    Asking a question about code? http://eel.is/iso-c++/testcase/

                    1 Reply Last reply
                    1
                    • V Vil_JL
                      11 Jun 2024, 06:56

                      @JonB What about this?

                      connect(treeView->selectionModel()->selectedRows(), &QItemSelectionModel::selectionChanged, dataTable, &MainWindow::treeChild_clicked);
                      

                      And treeChild_clicked is my Slot function for the QTableView, declared like this:

                      void MainWindow::treeChild_clicked(const QItemSelection &selected, const QItemSelection &deselected) {}
                      

                      But I still don't get how to define the selected and deselected QItemSelection variables.

                      J Offline
                      J Offline
                      JonB
                      wrote on 11 Jun 2024, 08:33 last edited by JonB 6 Nov 2024, 08:34
                      #9

                      @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                      connect(treeView->selectionModel()->selectedRows(), &QItemSelectionModel::selectionChanged, dataTable, &MainWindow::treeChild_clicked);

                      Almost right, but selectedRows()(a QModelIndexList) does not have a QItemSelectionModel::selectionChanged method. That is a member of the QItemSelectionModel itself, so:

                      connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
                              dataTable, &MainWindow::treeChild_selectionChanged);
                      

                      The receiving slot function should then look like

                      void MainWindow::treeChild_selectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
                      {
                      }
                      

                      so it matches the signal parameters. You do not "define" these, the signal passes them to you,

                      In that function you could use the passed-in selected (and deselected if you want that too), or you can just re-query QTreeView::selectedIndexes().

                      That should get you going? I cannot speak for @jeremy_k's suggestion, that's up to you. But note that he uses and connects the same signal, albeit his slot is a lambda rather than another function in MainWindow. That may be "advanced usage" for you at this stage.

                      V 1 Reply Last reply 11 Jun 2024, 22:09
                      1
                      • J JonB
                        11 Jun 2024, 08:33

                        @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                        connect(treeView->selectionModel()->selectedRows(), &QItemSelectionModel::selectionChanged, dataTable, &MainWindow::treeChild_clicked);

                        Almost right, but selectedRows()(a QModelIndexList) does not have a QItemSelectionModel::selectionChanged method. That is a member of the QItemSelectionModel itself, so:

                        connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
                                dataTable, &MainWindow::treeChild_selectionChanged);
                        

                        The receiving slot function should then look like

                        void MainWindow::treeChild_selectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
                        {
                        }
                        

                        so it matches the signal parameters. You do not "define" these, the signal passes them to you,

                        In that function you could use the passed-in selected (and deselected if you want that too), or you can just re-query QTreeView::selectedIndexes().

                        That should get you going? I cannot speak for @jeremy_k's suggestion, that's up to you. But note that he uses and connects the same signal, albeit his slot is a lambda rather than another function in MainWindow. That may be "advanced usage" for you at this stage.

                        V Offline
                        V Offline
                        Vil_JL
                        wrote on 11 Jun 2024, 22:09 last edited by
                        #10

                        @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                        connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
                        dataTable, &MainWindow::treeChild_selectionChanged);

                        @JonB the connect is giving me an error of C2665: 'QObject::connect': no overloaded function could convert all the argument types. I think it comes from the treeView->selectionModel().

                        Also for the slot function, I still having doubts about how to use the &selected parameter correctly. I saw that I can get a list of indexes from it, could that help to populate the tableview?

                        void MainWindow::treeChild_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
                        {
                            QModelIndexList currentIndexes = selected.indexes();
                            foreach(const QModelIndex &index, currentIndexes)
                            {
                                //...
                            }
                        }
                        

                        Thank you again for your help.

                        1 Reply Last reply
                        0
                        • jsulmJ jsulm
                          11 Jun 2024, 07:06

                          @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                          What about this?

                          treeView->selectionModel()->selectedRows() returns a QList which does not have selectionChanged signal - why do you call selectedRows()?

                          "But I still don't get how to define the selected and deselected QItemSelection variables" - what do you mean? The signal will pass both to the slot.

                          V Offline
                          V Offline
                          Vil_JL
                          wrote on 12 Jun 2024, 04:05 last edited by
                          #11

                          @jsulm said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                          why do you call selectedRows()?

                          @jsulm that was a mistake, I was trying to follow another suggestion.

                          What do you think of my last post? I'm lost with those QItemSelection parameters, and the connect is showing an error after following JonB suggestion.

                          Thank you for answering.

                          J 1 Reply Last reply 12 Jun 2024, 09:21
                          0
                          • V Vil_JL
                            12 Jun 2024, 04:05

                            @jsulm said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                            why do you call selectedRows()?

                            @jsulm that was a mistake, I was trying to follow another suggestion.

                            What do you think of my last post? I'm lost with those QItemSelection parameters, and the connect is showing an error after following JonB suggestion.

                            Thank you for answering.

                            J Offline
                            J Offline
                            JonB
                            wrote on 12 Jun 2024, 09:21 last edited by JonB 6 Dec 2024, 09:22
                            #12

                            @Vil_JL
                            I imagine the error you got was because I wrote dataTable where I should have written this (the MainWindow).

                            I think you should start with defining (or changing your mind) about what the right-hand pane should be/show. Your left-hand pane is a tree view. At present you allow multiple selection there. And your right-hand pane is a table view, allowing for multiple rows. Is that indeed what you want? Or, do you really want a standard/common "allow one item in tree view to be selected at left and show detail on that single item at right"? (Which could be done neatly with a QDataWidgetMapper especially if you want to allow editing but maybe you do not.) If it's not, and you do want multiple selection and multiple rows at right in a table view that's fine but want to be sure. Let's get that clear before proceeding.

                            V 1 Reply Last reply 12 Jun 2024, 14:33
                            1
                            • J JonB
                              12 Jun 2024, 09:21

                              @Vil_JL
                              I imagine the error you got was because I wrote dataTable where I should have written this (the MainWindow).

                              I think you should start with defining (or changing your mind) about what the right-hand pane should be/show. Your left-hand pane is a tree view. At present you allow multiple selection there. And your right-hand pane is a table view, allowing for multiple rows. Is that indeed what you want? Or, do you really want a standard/common "allow one item in tree view to be selected at left and show detail on that single item at right"? (Which could be done neatly with a QDataWidgetMapper especially if you want to allow editing but maybe you do not.) If it's not, and you do want multiple selection and multiple rows at right in a table view that's fine but want to be sure. Let's get that clear before proceeding.

                              V Offline
                              V Offline
                              Vil_JL
                              wrote on 12 Jun 2024, 14:33 last edited by Vil_JL 6 Dec 2024, 14:44
                              #13

                              @JonB What I want to do is simply what I shown, to select one element in an tree to show me in a table the values it has stored in that tree element (or use the tree element as a key to look for the data), and to be able to edit those values (I think I already sorted that out with a delegate).

                              I choose a model/view framework because my understanding is that is the best way to achieve it. I thought it would be simpler to do, I didn't expect that would be so complicated to develop. 😅

                              @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                              I imagine the error you got was because I wrote dataTable where I should have written this (the MainWindow).

                              Yes, thank you, that solved that error. Now I have to figure out the function for the slot.

                              Thank you again for answering.

                              J 1 Reply Last reply 12 Jun 2024, 15:18
                              0
                              • V Vil_JL
                                12 Jun 2024, 14:33

                                @JonB What I want to do is simply what I shown, to select one element in an tree to show me in a table the values it has stored in that tree element (or use the tree element as a key to look for the data), and to be able to edit those values (I think I already sorted that out with a delegate).

                                I choose a model/view framework because my understanding is that is the best way to achieve it. I thought it would be simpler to do, I didn't expect that would be so complicated to develop. 😅

                                @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                                I imagine the error you got was because I wrote dataTable where I should have written this (the MainWindow).

                                Yes, thank you, that solved that error. Now I have to figure out the function for the slot.

                                Thank you again for answering.

                                J Offline
                                J Offline
                                JonB
                                wrote on 12 Jun 2024, 15:18 last edited by JonB 6 Dec 2024, 15:21
                                #14

                                @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                                to select one element in an tree to show me in a table the values it has stored in that tree element (or use the tree element as a key to look for the data), and to be able to edit those values (I think I already sorted that out with a delegate).

                                Since it's "one element" you might have been better to use the QDataWidgetMapper which would have produced a bunch of label-editwidget pairs in lines in the right-hand pane, instead of a single row in a generic tableview with columns for each value. But that's up to you and we will stick with the table view.

                                You have chosen to create to keep and create the models/maps for the tree part (namesMap) and the table part (dataMap) separate from each other. That means we cannot somehow use a selection in the tree view/model to directly access an element in the table view/model. We could have done that by index (from the selection) had they been in the same model, but we can't with separate models.

                                I don't have time to write the whole thing for you. What you will need to code is:

                                • You only want to select a single row in the tree. I think QTreeView or its selection model allows you to specify "only allow single selection". You might want to add that. If you don't then your code should check for multiple rows selected and perhaps decide to have the table view show detail on only the first one selected.

                                • You only want to be able to select a leaf in the tree, not a node. Either find a setting for that if there is one, or make your code ignore a selection if it is a node not a leaf, or make the table view show "nothing" if what is selected is not a leaf.

                                • At this point you have a single leaf selected, in namesMap. You want to display information about that from dataMap.

                                • Because you cannot use indexes into namesMap to directly access elements in dataMap, you need to access (from the current selection) the right element in namesMap to retrieve the string of the "Child" item (e.g. Child 1-2) you will need to look up in dataMap.

                                • At this point you have e.g. Child 1-2 string. You want to display that in the table view. Your problem is that your tableModel has all the rows from dataMap. But you're only interested in one. You have about 3 choices:

                                1. Don't pre-populate tableModel with all the rows. Create a new table model or clear out an existing one. When a leaf is selected in the tree populate the table model with just the matching single row from dataMap.

                                2. If you do have all the rows already in the table model/view, would you be prepared to do something like just highlight the selected one and/or "dim" the unselected ones?

                                3. To do it properly (and this is what I would recommend/experienced users would do) where the table model has all the rows but you only want one to be shown you need to filter the model to pick out just the one you want to show in the table view. Qt's QSortFilterProxyModel Class allows for that. You have to interpose that between the underlying source model (tableModel) and the table view, like:

                                QStandardItemModel tableModel;
                                QSortFilterProxyModel tableFilterModel;
                                QTableView tableView;
                                tableFilterModel.setSourceModel(&tableModel);
                                tableView.setModel(&tableFilterModel);
                                

                                Then, armed with the string Child 1-2 from the selected leaf in the tree model you can use tableFilterModel.setFilterFixedString("Child 1-2"), and that will make the table view show only that row.

                                V 1 Reply Last reply 25 Jun 2024, 19:18
                                1
                                • jeremy_kJ Offline
                                  jeremy_kJ Offline
                                  jeremy_k
                                  wrote on 13 Jun 2024, 05:30 last edited by jeremy_k
                                  #15

                                  Some of the confusion may be due to the use of the terminology such as "select", which for QItemSelectionModel is 0 or more items. "current", which means 0 or 1 item, sounds like a better match for the intent.

                                  Reposting my suggestion, this time with all of the boiler plate:

                                  #include <QApplication>
                                  #include <QStandardItemModel>
                                  #include <QTreeView>
                                  #include <QTableView>
                                  #include <QString>
                                  #include <QItemSelectionModel>
                                  #include <QSplitter>
                                  
                                  int main(int argc, char *argv[])
                                  {
                                      QApplication a(argc, argv);
                                      QStandardItemModel model;
                                  
                                      // Populate the model according to the data.
                                      QModelIndex firstIndex;
                                      for (int i=0; i < 3; i++) {
                                          QStandardItem *parent{ new QStandardItem(QString("Parent %1").arg(i))};
                                          QList<QStandardItem *> intermediate{ new QStandardItem(QString("Child %1").arg(i)), new QStandardItem};
                                          QStandardItem *child = new QStandardItem(QString("Data of child %1").arg(i));
                                          // Only children of column 0 are displayed by QTreeView. Children of other rows are hidden.
                                          intermediate[1]->appendRow(child);
                                          parent->appendRow(intermediate);
                                          model.appendRow(parent);
                                          if (!firstIndex.isValid())
                                              firstIndex = model.indexFromItem(intermediate[1]);
                                      }
                                  
                                      QTreeView tree;
                                      tree.setModel(&model);
                                      tree.expandAll();
                                  
                                      QTableView table;
                                      table.setModel(&model);
                                      // Setting the root index of a view hides all items except those rooted at that index.
                                      table.setRootIndex(firstIndex);
                                  
                                      QItemSelectionModel *sm = tree.selectionModel();
                                      QObject::connect(sm, &QItemSelectionModel::currentChanged,
                                                       &table, [&table](const QModelIndex &current) {
                                                          // Reveal children rooted at the second column of the current (usually clicked) row, if the column is valid
                                                          QModelIndex rootIndex = current.siblingAtColumn(1);
                                                          if (rootIndex.isValid())
                                                              table.setRootIndex(rootIndex);
                                                      }
                                      );
                                  
                                      QSplitter splitter;
                                      splitter.addWidget(&tree);
                                      splitter.addWidget(&table);
                                      splitter.show();
                                  
                                      return a.exec();
                                  }
                                  

                                  The program above produces this display:
                                  967da9a9-2ce4-43a5-ba7b-81cbc91f59d3-image.png

                                  Clicking on "Child 0" displays "Data of child 0". Clicking on a parent node does not update the table display. This behavior can be fine tuned. Note that the items are editable, and edits are saved to the model.

                                  Asking a question about code? http://eel.is/iso-c++/testcase/

                                  V 1 Reply Last reply 25 Jun 2024, 20:28
                                  1
                                  • J JonB
                                    12 Jun 2024, 15:18

                                    @Vil_JL said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                                    to select one element in an tree to show me in a table the values it has stored in that tree element (or use the tree element as a key to look for the data), and to be able to edit those values (I think I already sorted that out with a delegate).

                                    Since it's "one element" you might have been better to use the QDataWidgetMapper which would have produced a bunch of label-editwidget pairs in lines in the right-hand pane, instead of a single row in a generic tableview with columns for each value. But that's up to you and we will stick with the table view.

                                    You have chosen to create to keep and create the models/maps for the tree part (namesMap) and the table part (dataMap) separate from each other. That means we cannot somehow use a selection in the tree view/model to directly access an element in the table view/model. We could have done that by index (from the selection) had they been in the same model, but we can't with separate models.

                                    I don't have time to write the whole thing for you. What you will need to code is:

                                    • You only want to select a single row in the tree. I think QTreeView or its selection model allows you to specify "only allow single selection". You might want to add that. If you don't then your code should check for multiple rows selected and perhaps decide to have the table view show detail on only the first one selected.

                                    • You only want to be able to select a leaf in the tree, not a node. Either find a setting for that if there is one, or make your code ignore a selection if it is a node not a leaf, or make the table view show "nothing" if what is selected is not a leaf.

                                    • At this point you have a single leaf selected, in namesMap. You want to display information about that from dataMap.

                                    • Because you cannot use indexes into namesMap to directly access elements in dataMap, you need to access (from the current selection) the right element in namesMap to retrieve the string of the "Child" item (e.g. Child 1-2) you will need to look up in dataMap.

                                    • At this point you have e.g. Child 1-2 string. You want to display that in the table view. Your problem is that your tableModel has all the rows from dataMap. But you're only interested in one. You have about 3 choices:

                                    1. Don't pre-populate tableModel with all the rows. Create a new table model or clear out an existing one. When a leaf is selected in the tree populate the table model with just the matching single row from dataMap.

                                    2. If you do have all the rows already in the table model/view, would you be prepared to do something like just highlight the selected one and/or "dim" the unselected ones?

                                    3. To do it properly (and this is what I would recommend/experienced users would do) where the table model has all the rows but you only want one to be shown you need to filter the model to pick out just the one you want to show in the table view. Qt's QSortFilterProxyModel Class allows for that. You have to interpose that between the underlying source model (tableModel) and the table view, like:

                                    QStandardItemModel tableModel;
                                    QSortFilterProxyModel tableFilterModel;
                                    QTableView tableView;
                                    tableFilterModel.setSourceModel(&tableModel);
                                    tableView.setModel(&tableFilterModel);
                                    

                                    Then, armed with the string Child 1-2 from the selected leaf in the tree model you can use tableFilterModel.setFilterFixedString("Child 1-2"), and that will make the table view show only that row.

                                    V Offline
                                    V Offline
                                    Vil_JL
                                    wrote on 25 Jun 2024, 19:18 last edited by
                                    #16

                                    Thank you very much, @JonB. It took me a while but I finally achieved what I was trying to do, and your suggestions helped me a lot.

                                    @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                                    You only want to select a single row in the tree. I think QTreeView or its selection model allows you to specify "only allow single selection". You might want to add that. If you don't then your code should check for multiple rows selected and perhaps decide to have the table view show detail on only the first one selected.

                                    You only want to be able to select a leaf in the tree, not a node. Either find a setting for that if there is one, or make your code ignore a selection if it is a node not a leaf, or make the table view show "nothing" if what is selected is not a leaf.

                                    Here I coded my slot function to get the selected item on the tree:

                                    // To populate the Tableview with data through the treeView:
                                        QItemSelectionModel *selectionModel = treeView->selectionModel(); // selection changes shall trigger a slot
                                        connect(selectionModel, &QItemSelectionModel::selectionChanged,
                                                this, &MainWindow::treeChild_selection);
                                    
                                    } // end of MainWindow
                                    
                                    void MainWindow::treeChild_selection(const QItemSelection &/*selected*/, 
                                                                         const QItemSelection &/*deselected*/)
                                    {
                                        // to get the text of the selected item
                                        const QModelIndex index = treeView->selectionModel()->currentIndex();
                                        QString selectedLeaf = index.data(Qt::DisplayRole).toString();
                                    

                                    @JonB said in What should I do to use treeView childs to select a subset of data to show in a TableView?:

                                    Don't pre-populate tableModel with all the rows. Create a new table model or clear out an existing one. When a leaf is selected in the tree populate the table model with just the matching single row from dataMap.

                                    I changed the table model to be empty of data so the function will be the one to fill it:

                                    // Setting the table model
                                        tableModel = new QStandardItemModel(3,3,this);
                                        tableModel->setHeaderData(0, Qt::Horizontal, tr("1"));
                                        tableModel->setHeaderData(1, Qt::Horizontal, tr("2"));
                                        tableModel->setHeaderData(2, Qt::Horizontal, tr("3"));
                                    
                                    void MainWindow::treeChild_selection(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
                                    {
                                        // to get the text of the selected item
                                        const QModelIndex index = treeView->selectionModel()->currentIndex();
                                        QString selectedLeaf = index.data(Qt::DisplayRole).toString();
                                    
                                        // iterating through the dataMap to find the rows corresponding to the selected item
                                        QMap<QString,QList<QList<QString>>>::iterator itr;
                                        for (itr = dataMap->begin(); itr != dataMap->end(); ++itr)
                                        {
                                            if (itr.key() == selectedLeaf)
                                            {
                                                // look up code to fill the tableView with the item data from the dataMap
                                            }
                                    
                                            // if a parent item is selected then the table is cleared of any data
                                            if (selectedLeaf == "Parent #1" || selectedLeaf == "Parent #2" || selectedLeaf == "Parent #3")
                                            {
                                                for (int row = 0; row < 3; ++row) {
                                                    for (int col = 0; col < 3; ++col) {
                                                        QModelIndex index2 = tableModel->index(row, col, QModelIndex());
                                                        tableModel->clearItemData(index2);
                                                    }
                                                }
                                            }
                                        }
                                    
                                    }
                                    

                                    So that's it, maybe it looks rough but it works.

                                    (I made some changes in the way I declared and defined the Qmaps initially too, but I don't want to spam this answer with a lot of code. If anyone is curious just ask)

                                    1 Reply Last reply
                                    1
                                    • jeremy_kJ jeremy_k
                                      13 Jun 2024, 05:30

                                      Some of the confusion may be due to the use of the terminology such as "select", which for QItemSelectionModel is 0 or more items. "current", which means 0 or 1 item, sounds like a better match for the intent.

                                      Reposting my suggestion, this time with all of the boiler plate:

                                      #include <QApplication>
                                      #include <QStandardItemModel>
                                      #include <QTreeView>
                                      #include <QTableView>
                                      #include <QString>
                                      #include <QItemSelectionModel>
                                      #include <QSplitter>
                                      
                                      int main(int argc, char *argv[])
                                      {
                                          QApplication a(argc, argv);
                                          QStandardItemModel model;
                                      
                                          // Populate the model according to the data.
                                          QModelIndex firstIndex;
                                          for (int i=0; i < 3; i++) {
                                              QStandardItem *parent{ new QStandardItem(QString("Parent %1").arg(i))};
                                              QList<QStandardItem *> intermediate{ new QStandardItem(QString("Child %1").arg(i)), new QStandardItem};
                                              QStandardItem *child = new QStandardItem(QString("Data of child %1").arg(i));
                                              // Only children of column 0 are displayed by QTreeView. Children of other rows are hidden.
                                              intermediate[1]->appendRow(child);
                                              parent->appendRow(intermediate);
                                              model.appendRow(parent);
                                              if (!firstIndex.isValid())
                                                  firstIndex = model.indexFromItem(intermediate[1]);
                                          }
                                      
                                          QTreeView tree;
                                          tree.setModel(&model);
                                          tree.expandAll();
                                      
                                          QTableView table;
                                          table.setModel(&model);
                                          // Setting the root index of a view hides all items except those rooted at that index.
                                          table.setRootIndex(firstIndex);
                                      
                                          QItemSelectionModel *sm = tree.selectionModel();
                                          QObject::connect(sm, &QItemSelectionModel::currentChanged,
                                                           &table, [&table](const QModelIndex &current) {
                                                              // Reveal children rooted at the second column of the current (usually clicked) row, if the column is valid
                                                              QModelIndex rootIndex = current.siblingAtColumn(1);
                                                              if (rootIndex.isValid())
                                                                  table.setRootIndex(rootIndex);
                                                          }
                                          );
                                      
                                          QSplitter splitter;
                                          splitter.addWidget(&tree);
                                          splitter.addWidget(&table);
                                          splitter.show();
                                      
                                          return a.exec();
                                      }
                                      

                                      The program above produces this display:
                                      967da9a9-2ce4-43a5-ba7b-81cbc91f59d3-image.png

                                      Clicking on "Child 0" displays "Data of child 0". Clicking on a parent node does not update the table display. This behavior can be fine tuned. Note that the items are editable, and edits are saved to the model.

                                      V Offline
                                      V Offline
                                      Vil_JL
                                      wrote on 25 Jun 2024, 20:28 last edited by
                                      #17

                                      Thank you for your code, @jeremy_k. I learned some things about models with it. At first I tried to follow your code but at the time to fill the table with my data, the result table I was getting looked weird and I was getting some index errors, so in the end I went with JonB final answer.

                                      1 Reply Last reply
                                      0
                                      • V Vil_JL has marked this topic as solved on 25 Jun 2024, 20:51
                                      • V Vil_JL has marked this topic as solved on 25 Jun 2024, 20:53

                                      • Login

                                      • Login or register to search.
                                      • First post
                                        Last post
                                      0
                                      • Categories
                                      • Recent
                                      • Tags
                                      • Popular
                                      • Users
                                      • Groups
                                      • Search
                                      • Get Qt Extensions
                                      • Unsolved