Combobox with a TreeView: which model?
-
wrote on 7 Aug 2017, 11:52 last edited by
Hello,
I'm trying to accomplish something like these two posts:
- http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
- 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
-
wrote on 7 Aug 2017, 12:20 last edited by
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?
-
Hello,
I'm trying to accomplish something like these two posts:
- http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
- 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
wrote on 7 Aug 2017, 13:07 last edited byvoid 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); }
-
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?
wrote on 7 Aug 2017, 14:47 last edited by cidadao 8 Jul 2017, 15:03@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. -
wrote on 7 Aug 2017, 16:34 last edited by cidadao 8 Jul 2017, 16:35
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.
-
wrote on 8 Aug 2017, 07:40 last edited by VRonin 8 Aug 2017, 08:04
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; };
- include the private API, and reimplement
-
wrote on 11 Aug 2017, 12:56 last edited byThis post is deleted!
1/8