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

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


  • Lifetime Qt Champion

    Hi,

    What about expending the branch of your programmatically selected items ?



  • @SGaist Sadly that wont do. User may not want to expand the items, Only see which parent groups they in perhaps?


  • Lifetime Qt Champion

    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, uncomment Q_OBJECT


  • @VRonin Wow this is incredible!!!

    Will test it asap!

    Thanks so much @_@



  • 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!


Log in to reply