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. QTreeView: Highlighting siblings of selected item
Forum Updated to NodeBB v4.3 + New Features

QTreeView: Highlighting siblings of selected item

Scheduled Pinned Locked Moved Unsolved General and Desktop
4 Posts 2 Posters 3.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.
  • C Offline
    C Offline
    Cutenator
    wrote on last edited by Cutenator
    #1

    Hi,
    in a QTreeView, I'd like to highlight every item that is a sibling of the currently selected item with a special color, like so:
    0_1521479924710_problem.png

    Two questions:

    1. How do I properly set the background color of the sibling items in the view depending on the selection?
      Currently I set the color in a very hacky way with a custom QStyledItemDelegate with the following paint method:
    	virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
    
    		const QColor highlight = QColor(200, 255, 200);
    
    		// Check if not selected but on same level as selection
    		if (selectionModel &&
    			!(option.state & QStyle::State_Selected) &&
    			treeModel->getItem(index)->parent == treeModel->getItem(selectionModel->currentIndex())->parent)
    		{
    			painter->fillRect(option.rect, highlight);
    		
    			// Change background color
    			QStyleOptionViewItem optionNew = option;
    			optionNew.palette.setColor(QPalette::Base, highlight);
    			optionNew.palette.setColor(QPalette::AlternateBase, highlight);
    
    			QStyledItemDelegate::paint(painter, optionNew, index);
    		}
    		else {
    			QStyledItemDelegate::paint(painter, option, index);
    		}
    	}
    

    Is there a more elegant way? (setting the palette color or drawing the background rect alone did not work with alternating row colors)
    I do not want to set the color in the model, since it has nothing to do with the actual data and could be different for a second view.

    1. Using this approach, I have a problem with updates.
      When I select a different item, the view does not get redrawn and the selection highlights are not updated.
      How can I force a redraw of the view (without resetting which items are expanded)?
    VRoninV 1 Reply Last reply
    0
    • C Cutenator

      Hi,
      in a QTreeView, I'd like to highlight every item that is a sibling of the currently selected item with a special color, like so:
      0_1521479924710_problem.png

      Two questions:

      1. How do I properly set the background color of the sibling items in the view depending on the selection?
        Currently I set the color in a very hacky way with a custom QStyledItemDelegate with the following paint method:
      	virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
      
      		const QColor highlight = QColor(200, 255, 200);
      
      		// Check if not selected but on same level as selection
      		if (selectionModel &&
      			!(option.state & QStyle::State_Selected) &&
      			treeModel->getItem(index)->parent == treeModel->getItem(selectionModel->currentIndex())->parent)
      		{
      			painter->fillRect(option.rect, highlight);
      		
      			// Change background color
      			QStyleOptionViewItem optionNew = option;
      			optionNew.palette.setColor(QPalette::Base, highlight);
      			optionNew.palette.setColor(QPalette::AlternateBase, highlight);
      
      			QStyledItemDelegate::paint(painter, optionNew, index);
      		}
      		else {
      			QStyledItemDelegate::paint(painter, option, index);
      		}
      	}
      

      Is there a more elegant way? (setting the palette color or drawing the background rect alone did not work with alternating row colors)
      I do not want to set the color in the model, since it has nothing to do with the actual data and could be different for a second view.

      1. Using this approach, I have a problem with updates.
        When I select a different item, the view does not get redrawn and the selection highlights are not updated.
        How can I force a redraw of the view (without resetting which items are expanded)?
      VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by VRonin
      #2

      @Cutenator said in QTreeView: Highlighting siblings of selected item:

      selectionModel->currentIndex()

      currentIndex() ≠ selected. Basically if you deselect an item it will be deselected but currentItem will point to that item.

      The solution is not super elegant but it's doable without hacking the delegate.

      I'm assuming the model is editable

      QObject::connect(treeView->selectionModel(),&QItemSelectionModel::selectionChanged,treeView,[treeView](const QItemSelection &selected, const QItemSelection &deselected)->void{
      	const auto setBackGround= [](QAbstactItemModel* mdl, const QItemSelection& sele, const QVariant& backgData)->void{
      		const auto selectedIndexes = selected.indexes();
      		QSet<QModelIndex> alreadyDone;
      		int rowCount=0;
      		int colCount=0;
      		for(const QModelIndex& singleIdx : selectedIndexes){
      			Q_ASSERT(singleIdx.isValid());
      			Q_ASSERT(singleIdx.model() == mdl);
      			const QModelIndex parentIdx = singleIdx.parent();
      			if(alreadyDone.contains(parentIdx)) 
      				continue;
      			alreadyDone.insert(parentIdx);
      			rowCount=mdl->rowCount(parentIdx);
      			colCount=mdl->columnCount(parentIdx);
      			for(int i=0;i<rowCount;++i){
      				for(int j=0;j<colCount;++j)
      					mdl->setData(mdl->index(i,j,parentIdx),backgData,Qt::BackGroundRole);
      			}
      		}
      	}
      	setBackGround(treeView->model(),selected,QBrush(QColor(153,217,234)));
      	setBackGround(treeView->model(),deselected,QVariant());
      });
      

      I put a lambda inside your lambda

      "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
      2
      • VRoninV VRonin

        @Cutenator said in QTreeView: Highlighting siblings of selected item:

        selectionModel->currentIndex()

        currentIndex() ≠ selected. Basically if you deselect an item it will be deselected but currentItem will point to that item.

        The solution is not super elegant but it's doable without hacking the delegate.

        I'm assuming the model is editable

        QObject::connect(treeView->selectionModel(),&QItemSelectionModel::selectionChanged,treeView,[treeView](const QItemSelection &selected, const QItemSelection &deselected)->void{
        	const auto setBackGround= [](QAbstactItemModel* mdl, const QItemSelection& sele, const QVariant& backgData)->void{
        		const auto selectedIndexes = selected.indexes();
        		QSet<QModelIndex> alreadyDone;
        		int rowCount=0;
        		int colCount=0;
        		for(const QModelIndex& singleIdx : selectedIndexes){
        			Q_ASSERT(singleIdx.isValid());
        			Q_ASSERT(singleIdx.model() == mdl);
        			const QModelIndex parentIdx = singleIdx.parent();
        			if(alreadyDone.contains(parentIdx)) 
        				continue;
        			alreadyDone.insert(parentIdx);
        			rowCount=mdl->rowCount(parentIdx);
        			colCount=mdl->columnCount(parentIdx);
        			for(int i=0;i<rowCount;++i){
        				for(int j=0;j<colCount;++j)
        					mdl->setData(mdl->index(i,j,parentIdx),backgData,Qt::BackGroundRole);
        			}
        		}
        	}
        	setBackGround(treeView->model(),selected,QBrush(QColor(153,217,234)));
        	setBackGround(treeView->model(),deselected,QVariant());
        });
        

        I put a lambda inside your lambda

        C Offline
        C Offline
        Cutenator
        wrote on last edited by
        #3

        @VRonin
        Thanks for the answer and the clarification with selected != current.
        As for your proposed solution
        "I do not want to set the color in the model, since it has nothing to do with the actual data and could be different for a second view."
        If it turns out to be the only possible way, I'd do it that way, but I consider it an even worse hack since selection has nothing to do with the model.

        Are there any other options to update the view with the delegate approach?

        1 Reply Last reply
        1
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #4

          I see your point. To avoid your issues, you could use a proxy model like this one (just commited, untested) to act as a mask for Qt::BackgroundRole

          The problem of the delegate is that it does not and should not interact with items other than the one passed to its methods.

          "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
          1

          • Login

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