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. Combobox with a TreeView: which model?
Forum Updated to NodeBB v4.3 + New Features

Combobox with a TreeView: which model?

Scheduled Pinned Locked Moved Solved General and Desktop
8 Posts 3 Posters 8.5k Views 1 Watching
  • 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.
  • C Offline
    C Offline
    cidadao
    wrote on 7 Aug 2017, 11:52 last edited by
    #1

    Hello,

    I'm trying to accomplish something like these two posts:

    1. http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
    2. https://stackoverflow.com/questions/27172160/how-to-implement-a-tree-based-qcombobox

    Ie. creating a combobox with a treeview (2) and importing and representing data as in (1).
    However when the user click on a child, in the treeview, I want the combobox to display all the text from the root until that child. So if for example we have this data:

    Parent1
        Child1
        Child2
             Child2.1
    

    If the user clicks Child2.1, the combobox's text should display something like this:

    Parent1 - Child2 - Child2.1
    

    The problem is: I'm completely lost between Models & Views. Can you provide me with a few shortcuts on how to implement this? How QComboBox gets the text that is clicked on the view?

    Thanks

    T 1 Reply Last reply 7 Aug 2017, 13:07
    0
    • V Offline
      V Offline
      VRonin
      wrote on 7 Aug 2017, 12:20 last edited by
      #2

      Did you already implement the tree? for me that's the hard part.

      Did you manage to achieve a combobox that lets you click on Child2.1?

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      C 1 Reply Last reply 7 Aug 2017, 14:47
      0
      • C cidadao
        7 Aug 2017, 11:52

        Hello,

        I'm trying to accomplish something like these two posts:

        1. http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
        2. https://stackoverflow.com/questions/27172160/how-to-implement-a-tree-based-qcombobox

        Ie. creating a combobox with a treeview (2) and importing and representing data as in (1).
        However when the user click on a child, in the treeview, I want the combobox to display all the text from the root until that child. So if for example we have this data:

        Parent1
            Child1
            Child2
                 Child2.1
        

        If the user clicks Child2.1, the combobox's text should display something like this:

        Parent1 - Child2 - Child2.1
        

        The problem is: I'm completely lost between Models & Views. Can you provide me with a few shortcuts on how to implement this? How QComboBox gets the text that is clicked on the view?

        Thanks

        T Offline
        T Offline
        Taz742
        wrote on 7 Aug 2017, 13:07 last edited by
        #3

        @cidadao

        void wdgMyTree::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
        {
            std::vector<QString> txt;
            std::queue<QTreeWidgetItem*> Q;
            Q.push(item);
            while(!Q.empty()){
                QTreeWidgetItem *now = Q.front(); Q.pop();
                if (now->parent() != NULL) {
                    qDebug() << now->text(column);
                    txt.push_back(now->text(column));
                    Q.push(now->parent());
                }
            }
        
            QString returned_string = "";
        
            for(int i = txt.size() - 1; i >= 0; i--){
            	returned_string += txt.at(i);	
            }
        
            emit(returned_string);
        }
        

        Do what you want.

        1 Reply Last reply
        0
        • V VRonin
          7 Aug 2017, 12:20

          Did you already implement the tree? for me that's the hard part.

          Did you manage to achieve a combobox that lets you click on Child2.1?

          C Offline
          C Offline
          cidadao
          wrote on 7 Aug 2017, 14:47 last edited by cidadao 8 Jul 2017, 15:03
          #4

          @VRonin yes. Using (2) - on my original post, you can achieve that.
          Quick demo:

          Using this code to build the tree's data:

          QStandardItem *root = new QStandardItem("Item1");
          QStandardItem *l1 = new QStandardItem("Leaf1");
          QStandardItem *l2 = new QStandardItem("Leaf2");
           root->appendRow(l1);
           l1->appendRow(l2);;
              root->appendRow(l1);
              QStandardItemModel *model = new QStandardItemModel();
              model->appendRow(root);
              ui->combo->setModel(model);
          

          @Taz7422 whats the difference of using QTreeWidget instead of QTreeView on QComboBox?
          Also, when using it as (2), can't catch the itemClicked() / clicked() signal since by doing setView() the QComboBox takes ownership of the tree widget.

          1 Reply Last reply
          0
          • C Offline
            C Offline
            cidadao
            wrote on 7 Aug 2017, 16:34 last edited by cidadao 8 Jul 2017, 16:35
            #5

            Maybe this can't definitely be done with QComboBox + QTreeView?
            I created a custom TreeModel based on (1) and with the data() method returning a string with all item's and its parents' text (as described above) I managed to get the ComboBox displaying something like:

            Parent1 - Child2 - Child2.1
            

            But by modifying this method on the Model, the view on the Tree also gets modified (not just on the ComboBox)

            I'm now considering just having two separate widgets, a button and a tree widget. The button when pressed opens the tree widget and when an item in the tree is pressed it sets the text on the button.

            Any feedback on this is greatly appreciated.

            1 Reply Last reply
            0
            • V Offline
              V Offline
              VRonin
              wrote on 8 Aug 2017, 07:40 last edited by VRonin 8 Aug 2017, 08:04
              #6

              Sorry for the delay.

              Had a look at the sources of QComboBox and unfortunately the model index dies inside the private part of the class so you have 2 solutions, none of them ideal:

              • include the private API, and reimplement QComboBoxPrivate::emitActivated to emit a signal including the index
                • this breaks binary compatibility (and hence LGPL on certain platforms), as well as not giving you any assurance Qt won't change parts of the API at the next update)
              • hack it via a direct connection in the view
                • this is untested but if it works it should be good enough as long as you have a non-editable QComboBox (i.e. you don't allow typing directly in the combobox) and you don't have multiple columns

              for the second solution:

              #include <QComboBox>
              #include <QPersistentModelIndex>
              #include <QAbstractItemView>
              #include <QItemSelectionModel>
              #include <QAbstractItemModel>
              #include <QStylePainter>
              class TreeCombo : public QComboBox {
                  Q_OBJECT
                  Q_DISABLE_COPY(TreeCombo)
              public:
                  TreeCombo(QWidget* parent=Q_NULLPTR)
                      : QComboBox(parent)
                  {
                      connectView();
                      connectModel();
                  }
                  void setView(QAbstractItemView *itemView){
                      QComboBox::setView(itemView);
                      connectView();
                  }
                  void setModel(QAbstractItemModel *model){
                      QComboBox::setModel(model);
                      connectModel();
                  }
              
              protected:
                  Q_SLOT void updateModelIndex(const QModelIndex& idx){
                      if(idx.isValid())
                          m_index=idx;
                  }
                  Q_SLOT void updateComboString(){
                      m_comboString.clear();
                      for(QModelIndex idx = m_index;idx.isValid();idx=idx.parent()){
                          if(!m_comboString.isEmpty())
                              m_comboString.prepend(" - ");
                          m_comboString.prepend(idx.data().toString());
                      }
                      update();
                  }
                  virtual void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE
                  {
                      Q_UNUSED(event);
                      QStylePainter painter(this);
                      painter.setPen(palette().color(QPalette::Text));
              
                      QStyleOptionComboBox opt;
                      initStyleOption(&opt);
                      opt.currentText = m_comboString;
                      
                      painter.drawComplexControl(QStyle::CC_ComboBox, opt);
                      painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
                  }
              private:
                  void connectView(){
                      connect(view()->selectionModel(),&QItemSelectionModel::currentChanged,this,&TreeCombo::updateModelIndex);
                  }
                  void connectModel(){
                      connect(model(),&QAbstractItemModel::dataChanged,this,&TreeCombo::updateComboString);
                      connect(model(),&QAbstractItemModel::rowsRemoved,this,&TreeCombo::updateComboString);
                      connect(model(),&QAbstractItemModel::rowsInserted,this,&TreeCombo::updateComboString);
                      connect(model(),&QAbstractItemModel::rowsMoved,this,&TreeCombo::updateComboString);
                  }
              
                  QPersistentModelIndex m_index;
                  QString m_comboString;
              };
              

              "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
              ~Napoleon Bonaparte

              On a crusade to banish setIndexWidget() from the holy land of Qt

              1 Reply Last reply
              4
              • C Offline
                C Offline
                cidadao
                wrote on 9 Aug 2017, 12:09 last edited by
                #7

                @VRonin thanks for the help!

                I ended up implementing it with a QPushButton and a QTreeWidget (with Qt::Popup) and by connecting signals&slots between them. This is how it looks:

                It serves the purpose!

                1 Reply Last reply
                2
                • C Offline
                  C Offline
                  cidadao
                  wrote on 11 Aug 2017, 12:56 last edited by
                  #8
                  This post is deleted!
                  1 Reply Last reply
                  0

                  1/8

                  7 Aug 2017, 11:52

                  • Login

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