title: How to highlight some items in a QTreeView (without inheriting)
body: <p>I am looking for a way to highlight rows of a QTreeView without reimplementing a QTreeView subclass.</p>
  
  <p>I have seen similar questions:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/23625069/highlight-specific-substrings-in-a-qtreeview">highlight-specific-substrings-in-a-qtreeview</a></li>
  <li><a href="https://stackoverflow.com/questions/1956542/how-to-make-item-view-render-rich-html-text-in-qt/5443112#5443112">how-to-make-item-view-render-rich-html-text-in-qt</a></li>
  <li><a href="https://stackoverflow.com/questions/2696733/set-bold-rows-in-a-qtreeview">set-bold-rows-in-a-qtreeview</a></li>
  </ul>
  
  <p>But they all use delegate. The reason is that I am creating a <em>search text in widgets</em> tool which browse all widgets and find and highlights text in them. Hence, I cannot use delegate.</p>
  
  <p>Is there any possible solution?
  Painting a semi-transparent item above it?</p>
  
  <hr>
  
  <p>Here is the code of the tool which stores the widget and the way to search for text in them.</p>
  
  <pre><code>class GUI_EXPORT QgsSearchHighlightOptionWidget : public QObject
  {
      Q_OBJECT
    public:
  
      /**
       * Constructor
       * \param widget the widget used to search text into
       */
      explicit QgsSearchHighlightOptionWidget( QWidget *widget = nullptr );
  
      /**
       * Returns if it valid: if the widget type is handled and if the widget is not still available
       */
      bool isValid() { return mWidget &amp;&amp; mValid; }
  
      /**
       * search for a text pattern and highlight the widget if the text is found
       * \returns true if the text pattern is found
       */
      bool searchHighlight( const QString &amp;searchText );
  
      /**
       *  reset the style to the original state
       */
      void reset();
  
      /**
       * return the widget
       */
      QWidget *widget() { return mWidget; }
  
      bool eventFilter( QObject *obj, QEvent *event ) override;
  
    private slots:
      void widgetDestroyed();
  
    private:
      QPointer&lt; QWidget &gt; mWidget;
      QString mStyleSheet;
      bool mValid = true;
      bool mChangedStyle = false;
      std::function &lt; bool( QString )&gt; mTextFound = []( QString searchText ) {Q_UNUSED( searchText ); return false;};
      bool mInstalledFilter = false;
  };
  
  QgsSearchHighlightOptionWidget::QgsSearchHighlightOptionWidget( QWidget *widget )
    : QObject( widget )
    , mWidget( widget )
  {
    if ( qobject_cast&lt;QLabel *&gt;( widget ) )
    {
      mStyleSheet = QStringLiteral( "QLabel { background-color: yellow; color: blue;}" );
      mTextFound = [ = ]( QString searchText ) {return qobject_cast&lt;QLabel *&gt;( mWidget )-&gt;text().contains( searchText, Qt::CaseInsensitive );};
    }
    else if ( qobject_cast&lt;QCheckBox *&gt;( widget ) )
    {
      mStyleSheet = QStringLiteral( "QCheckBox { background-color: yellow; color: blue;}" );
      mTextFound = [ = ]( QString searchText ) {return qobject_cast&lt;QCheckBox *&gt;( mWidget )-&gt;text().contains( searchText, Qt::CaseInsensitive );};
    }
    else if ( qobject_cast&lt;QAbstractButton *&gt;( widget ) )
    {
      mStyleSheet = QStringLiteral( "QAbstractButton { background-color: yellow; color: blue;}" );
      mTextFound = [ = ]( QString searchText ) {return qobject_cast&lt;QAbstractButton *&gt;( mWidget )-&gt;text().contains( searchText, Qt::CaseInsensitive );};
    }
    else if ( qobject_cast&lt;QGroupBox *&gt;( widget ) )
    {
      mStyleSheet = QStringLiteral( "QGroupBox::title { background-color: yellow; color: blue;}" );
      mTextFound = [ = ]( QString searchText ) {return qobject_cast&lt;QGroupBox *&gt;( mWidget )-&gt;title().contains( searchText, Qt::CaseInsensitive );};
    }
    else if ( qobject_cast&lt;QTreeView *&gt;( widget ) )
    {
      // TODO - style individual matching items
      mTextFound = [ = ]( QString searchText )
      {
        QTreeView *tree = qobject_cast&lt;QTreeView *&gt;( mWidget );
        if ( !tree )
          return false;
        QModelIndexList hits = tree-&gt;model()-&gt;match( tree-&gt;model()-&gt;index( 0, 0 ), Qt::DisplayRole, searchText, 1, Qt::MatchContains | Qt::MatchRecursive );
        return !hits.isEmpty();
      };
    }
    else
    {
      mValid = false;
    }
    if ( mValid )
    {
      mStyleSheet.prepend( "/*!search!*/" ).append( "/*!search!*/" );
      QgsDebugMsgLevel( mStyleSheet, 4 );
      connect( mWidget, &amp;QWidget::destroyed, this, &amp;QgsSearchHighlightOptionWidget::widgetDestroyed );
    }
  }
  
  bool QgsSearchHighlightOptionWidget::searchHighlight( const QString &amp;searchText )
  {
    bool found = false;
    if ( !mWidget )
      return found;
  
    if ( !searchText.isEmpty() )
    {
      found = mTextFound( searchText );
    }
  
    if ( found &amp;&amp; !mChangedStyle )
    {
      if ( !mWidget-&gt;isVisible() )
      {
        // show the widget to get initial stylesheet in case it's modified
        QgsDebugMsg( QString( "installing event filter on: %1 (%2)" )
                     .arg( mWidget-&gt;objectName() )
                     .arg( qobject_cast&lt;QLabel *&gt;( mWidget ) ? qobject_cast&lt;QLabel *&gt;( mWidget )-&gt;text() : QString() ) );
        mWidget-&gt;installEventFilter( this );
        mInstalledFilter = true;
      }
      else
      {
        mWidget-&gt;setStyleSheet( mWidget-&gt;styleSheet() + mStyleSheet );
        mChangedStyle = true;
      }
    }
  
    return found;
  }
  
  bool QgsSearchHighlightOptionWidget::eventFilter( QObject *obj, QEvent *event )
  {
    if ( mInstalledFilter &amp;&amp; event-&gt;type() == QEvent::Show &amp;&amp; obj == mWidget )
    {
      mWidget-&gt;removeEventFilter( this );
      mInstalledFilter = false;
      // instead of catching the event and calling show again
      // it might be better to use a timer to change the style
      // after the widget is shown
  #if 1
      mWidget-&gt;show();
      mWidget-&gt;setStyleSheet( mWidget-&gt;styleSheet() + mStyleSheet );
      return true;
  #else
      QTimer::singleShot( 500, this, [ = ]
      {
        mWidget-&gt;setStyleSheet( mWidget-&gt;styleSheet() + mStyleSheet );
        mChangedStyle = true;
      } );
  #endif
    }
    return QObject::eventFilter( obj, event );
  }
  
  void QgsSearchHighlightOptionWidget::reset()
  {
    if ( mWidget &amp;&amp; mValid )
    {
      if ( mChangedStyle )
      {
        QString ss = mWidget-&gt;styleSheet();
        ss.remove( mStyleSheet );
        mWidget-&gt;setStyleSheet( ss );
        mChangedStyle = false;
      }
      else if ( mInstalledFilter )
      {
        mWidget-&gt;removeEventFilter( this );
        mInstalledFilter = false;
      }
    }
  }
  
  void QgsSearchHighlightOptionWidget::widgetDestroyed()
  {
    mWidget = nullptr;
    mValid = false;
  }
  </code></pre>
  
  <p>And here is the code to actually register the widgets from the dialog:</p>
  
  <pre><code>void QgsOptionsDialogBase::registerTextSearchWidgets()
  {
    mRegisteredSearchWidgets.clear();
  
    for ( int i = 0; i &lt; mOptStackedWidget-&gt;count(); i++ )
    {
      Q_FOREACH ( QWidget *w, mOptStackedWidget-&gt;widget( i )-&gt;findChildren&lt;QWidget *&gt;() )
      {
        QgsSearchHighlightOptionWidget *shw = new QgsSearchHighlightOptionWidget( w );
        if ( shw-&gt;isValid() )
        {
          QgsDebugMsgLevel( QString( "Registering: %1" ).arg( w-&gt;objectName() ), 4 );
          mRegisteredSearchWidgets.append( qMakePair( shw, i ) );
        }
        else
        {
          delete shw;
        }
      }
    }
  }
  </code></pre>