QTreeView ghost select parent if child selected?
-
Hey
I'd like to implement the behavior in the topic... any idea? at all? I'm lost O_O
Right now if I select items programmatically, I would not know that they are selected because if their parent is not expanded there is no indication of any selection... What can I do?
TIA
-
Hi,
What about expending the branch of your programmatically selected items ?
-
Can you explain how such a selection may happen ?
-
@SGaist said in QTreeView ghost select parent if child selected?:
Can you explain how such a selection may happen ?
Hey
Its implemented in quite few 3d apps. One of them Autodesk Maya > https://youtu.be/8gRBwGafHfg?t=96
You can see if child is selected the parent is then "half" selected...
I have some rough ideas on how to handle it but they all sound pretty bad so far...
-
#include <QApplication> #include <QTreeWidget> #include <QStyledItemDelegate> #include <QItemSelectionModel> class ChildSelectDelegate : public QStyledItemDelegate{ //Q_OBJECT Q_DISABLE_COPY(ChildSelectDelegate) public: explicit ChildSelectDelegate(QObject *parent = Q_NULLPTR) :QStyledItemDelegate(parent) ,m_selModel(Q_NULLPTR) {} void setSelectionModel(QItemSelectionModel* selModel){m_selModel=selModel;} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ Q_ASSERT(index.isValid()); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; if(!(opt.state & QStyle::State_Selected) && m_selModel && m_selModel->model() == index.model()){ if(childrenSelected(index)){ const QPalette::ColorGroup cg = (widget ? widget->isEnabled() : (opt.state & QStyle::State_Enabled)) ? QPalette::Normal : QPalette::Disabled; QBrush darkHighLight = opt.palette.brush(cg, QPalette::Highlight); const QColor oldCol = darkHighLight.color(); darkHighLight.setColor(QColor(oldCol.red()*3/4,oldCol.green()*3/4,oldCol.blue()*3/4,oldCol.alpha())); opt.backgroundBrush = darkHighLight; } } QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); } private: QItemSelectionModel* m_selModel; bool childrenSelected(const QModelIndex &index) const{ Q_ASSERT(m_selModel); Q_ASSERT(m_selModel->model() == index.model()); for(int i=0, maxRow = index.model()->rowCount(index);i<maxRow;++i){ for(int j=0, maxCol = index.model()->columnCount(index);j<maxCol;++j){ const QModelIndex currIdx = index.model()->index(i,j,index); if(m_selModel->isSelected(currIdx)) return true; if(index.model()->hasChildren(currIdx)){ if(childrenSelected(currIdx)) return true; } } } return false; } }; int main(int argc, char **argv) { QApplication app(argc,argv); QTreeWidget wid; wid.model()->insertRows(0,2); const QModelIndex parentIdx = wid.model()->index(0,0); wid.model()->setData(parentIdx,"Thomas"); wid.model()->insertRows(0,2,parentIdx); wid.model()->setData(wid.model()->index(0,0,parentIdx),"Bob"); wid.model()->setData(wid.model()->index(1,0,parentIdx),"Alice"); wid.model()->setData(wid.model()->index(1,0),"Richard"); ChildSelectDelegate* deleg = new ChildSelectDelegate(&wid); deleg->setSelectionModel(wid.selectionModel()); wid.setItemDelegate(deleg); QObject::connect(wid.selectionModel(),&QItemSelectionModel::selectionChanged,&wid,[&wid](const QItemSelection &selected,const QItemSelection &deselected)->void{ for (const QItemSelection &itemSelection : {selected,deselected} ){ const QModelIndexList itemSelectionIdx = itemSelection.indexes(); for(const QModelIndex& idx : itemSelectionIdx){ for (QModelIndex parIdx = idx.parent();parIdx.isValid();parIdx=parIdx.parent()) wid.update(parIdx); } } }); wid.show(); return app.exec(); }
2 notes:
- the line
darkHighLight.setColor
... is the one that determines the color of the half-selection - You should move
ChildSelectDelegate
in its own header/source files. if you do, uncommentQ_OBJECT
- the line
-
easier but requires your model to support multiple roles:
#include <QApplication> #include <QTreeWidget> #include <QItemSelectionModel> int main(int argc, char **argv) { QApplication app(argc,argv); QTreeWidget wid; wid.model()->insertRows(0,2); const QModelIndex parentIdx = wid.model()->index(0,0); wid.model()->setData(parentIdx,"Thomas"); wid.model()->insertRows(0,2,parentIdx); wid.model()->setData(wid.model()->index(0,0,parentIdx),"Bob"); wid.model()->setData(wid.model()->index(1,0,parentIdx),"Alice"); wid.model()->setData(wid.model()->index(1,0),"Richard"); QObject::connect(wid.selectionModel(),&QItemSelectionModel::selectionChanged,&wid,[&wid](const QItemSelection &selected,const QItemSelection &deselected)->void{ const QBrush ghostBrush(Qt::lightGray); QModelIndexList itemSelectionIdx = selected.indexes(); for(const QModelIndex& idx : qAsConst(itemSelectionIdx)){ for (QModelIndex parIdx = idx.parent();parIdx.isValid();parIdx=parIdx.parent()){ if(!wid.selectionModel()->isSelected(parIdx)) wid.model()->setData(parIdx,ghostBrush,Qt::BackgroundRole); } } itemSelectionIdx = deselected.indexes(); for(const QModelIndex& idx : qAsConst(itemSelectionIdx)){ for (QModelIndex parIdx = idx.parent();parIdx.isValid();parIdx=parIdx.parent()) wid.model()->setData(parIdx,QVariant(),Qt::BackgroundRole); } }); wid.show(); return app.exec(); }
-
Well my ASAP took a lot longer than I thought... anyway I went with Method 1 and it works like charm! Thank you so much!
The only thing I had to tweak was to call update() at the end of mouse release, as apparently, the selection flag in my scenario was happening after selection, so when the painter was painting items the child was not marked selected... after that it all tick!
TIA!