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. QTreeWidget: how to implement custom properties for stylesheet?
Forum Updated to NodeBB v4.3 + New Features

QTreeWidget: how to implement custom properties for stylesheet?

Scheduled Pinned Locked Moved Solved General and Desktop
9 Posts 3 Posters 6.5k Views 2 Watching
  • 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.
  • D Offline
    D Offline
    Diracsbracket
    wrote on 19 Oct 2017, 07:45 last edited by Diracsbracket
    #1

    Hi.
    I have a QTreeWidget with a list of speech files. When a file is selected, the style for the select property is applied, which is a blue background gradient and white text.

    When the file is being played, the gradient changes to green, and when being recorded, the gradient changes to red.

    The way I implemented this was by simply using the setStylesheet() method on the QTreeWidget object, with a different styleSheet string depending on the playing/recording/selected state. However, as I found out, this incurs a performance penalty which translates into a brief but noticeable delay each time the setStyleSheet() method is invoked. I guess all the items in the tree are being updated somehow, which for large trees may cause the perceptible delay?

    My question is now: how can I define custom CSS properties like playing or recording, so that I only need to set the styleSheet once, and then set the desired property only on a single item (or the current + previous selected item) at a time? Is this at all possible? I haven't seen a setProperty() method in the QTreeWidgetItem class, so I wonder how I can do this.

    I found some info about dynamic properties, and some other one roles, but it is not clear to me if I can use them to achieve the desired formatting without the performance penalty of setting the stylesheet on the whole tree.

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mrjj
      Lifetime Qt Champion
      wrote on 19 Oct 2017, 07:57 last edited by mrjj
      #2

      Hi
      Yes you can use properties
      https://wiki.qt.io/Dynamic_Properties_and_Stylesheets

      But when we talk about Items in a TreeWidget, it sounds like you be more happy with a
      ItemDelegate to do custom drawing.

      http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html

      Anyway, you can start with my basic test delegate and expand to draw as you want.

      #include <QApplication>
      #include <QPainter>
      #include <QStyledItemDelegate>
      struct Delegate: public QStyledItemDelegate {
        Delegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
        void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
          auto o = option;
          initStyleOption(&o, index);
          o.decorationSize.setWidth(o.rect.width());
          auto style =  o.widget ? o.widget->style() : QApplication::style();
          style->drawControl(QStyle::CE_ItemViewItem, &o, painter, o.widget);
          painter->drawRect(o.rect);
        }
      };
      

      and just
      ui->treeWidget->setItemDelegate( new Delegate);

      It just draw the Item as normally and then a rect.
      (yes its ugly :)
      alt text

      D 1 Reply Last reply 19 Oct 2017, 10:07
      3
      • M mrjj
        19 Oct 2017, 07:57

        Hi
        Yes you can use properties
        https://wiki.qt.io/Dynamic_Properties_and_Stylesheets

        But when we talk about Items in a TreeWidget, it sounds like you be more happy with a
        ItemDelegate to do custom drawing.

        http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html

        Anyway, you can start with my basic test delegate and expand to draw as you want.

        #include <QApplication>
        #include <QPainter>
        #include <QStyledItemDelegate>
        struct Delegate: public QStyledItemDelegate {
          Delegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
          void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
            auto o = option;
            initStyleOption(&o, index);
            o.decorationSize.setWidth(o.rect.width());
            auto style =  o.widget ? o.widget->style() : QApplication::style();
            style->drawControl(QStyle::CE_ItemViewItem, &o, painter, o.widget);
            painter->drawRect(o.rect);
          }
        };
        

        and just
        ui->treeWidget->setItemDelegate( new Delegate);

        It just draw the Item as normally and then a rect.
        (yes its ugly :)
        alt text

        D Offline
        D Offline
        Diracsbracket
        wrote on 19 Oct 2017, 10:07 last edited by Diracsbracket
        #3

        @mrjj
        Thanks! I tried the dynamic property method, because the delegate method is a bit too advanced for my current Qt skills, I'm affraid. I know that eventually, I need to use a model-based implementation, but that's still some time away.

        As for the dynamic property method, I'm not sure I understand how to combine it with the QTreeWidget::item:selected selector.

        If I use a stylesheet string like

        const QString myStyle = "QTreeWidget:[rowstate=\"select\"] {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADFF, stop: 0.7 #4444FF, stop: 1.0 #0000FF); color: white;}";
        

        and then

        tree->setStyleSheet(myStyle);
        tree->setProperty("rowstate", "select")
        

        (I'm omitting the unpolish()/polish() part here) then of course the styling is applied to the whole tree.

        What I am trying to achieve is to have the style applied to the selected item, which I am currently doing like this:

        static const QString UM_STYLESHEET_SELECT = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADFF, stop: 0.7 #4444FF, stop: 1.0 #0000FF); color: white;}";
        static const QString UM_STYLESHEET_PLAY   = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #AFE4AA, stop: 0.7 #5DC256, stop: 1.0 #0BA102); color: white;}";
        static const QString UM_STYLESHEET_RECORD = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFADAD, stop: 0.7 #FF4444, stop: 1.0 #FF0000); color: white;}";
        static const QString UM_STYLESHEET_NOREC  = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFDD99, stop: 0.7 #FFAA33, stop: 1.0 #FF8400); color: white;}";
        static const QString UM_STYLESHEET_VOID   = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADAD, stop: 0.7 #939393, stop: 1.0 #545454); color: white;}";
        

        I tried combining my dynamic property rowstate with the built-in property selected as follows:

        "QTreeWidget::item:selected:[rowstate=\"select\"] {....}
         QTreeWidget::item:selected:[rowstate=\"play\"] {....}
        ..."
        

        but that doesn't work. Maybe because the QWidgetItem class has no setProperty() method.

        Is it possible to apply the property on the selected item only?

        R 1 Reply Last reply 19 Oct 2017, 10:16
        0
        • D Diracsbracket
          19 Oct 2017, 10:07

          @mrjj
          Thanks! I tried the dynamic property method, because the delegate method is a bit too advanced for my current Qt skills, I'm affraid. I know that eventually, I need to use a model-based implementation, but that's still some time away.

          As for the dynamic property method, I'm not sure I understand how to combine it with the QTreeWidget::item:selected selector.

          If I use a stylesheet string like

          const QString myStyle = "QTreeWidget:[rowstate=\"select\"] {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADFF, stop: 0.7 #4444FF, stop: 1.0 #0000FF); color: white;}";
          

          and then

          tree->setStyleSheet(myStyle);
          tree->setProperty("rowstate", "select")
          

          (I'm omitting the unpolish()/polish() part here) then of course the styling is applied to the whole tree.

          What I am trying to achieve is to have the style applied to the selected item, which I am currently doing like this:

          static const QString UM_STYLESHEET_SELECT = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADFF, stop: 0.7 #4444FF, stop: 1.0 #0000FF); color: white;}";
          static const QString UM_STYLESHEET_PLAY   = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #AFE4AA, stop: 0.7 #5DC256, stop: 1.0 #0BA102); color: white;}";
          static const QString UM_STYLESHEET_RECORD = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFADAD, stop: 0.7 #FF4444, stop: 1.0 #FF0000); color: white;}";
          static const QString UM_STYLESHEET_NOREC  = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFDD99, stop: 0.7 #FFAA33, stop: 1.0 #FF8400); color: white;}";
          static const QString UM_STYLESHEET_VOID   = "QTreeWidget::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADAD, stop: 0.7 #939393, stop: 1.0 #545454); color: white;}";
          

          I tried combining my dynamic property rowstate with the built-in property selected as follows:

          "QTreeWidget::item:selected:[rowstate=\"select\"] {....}
           QTreeWidget::item:selected:[rowstate=\"play\"] {....}
          ..."
          

          but that doesn't work. Maybe because the QWidgetItem class has no setProperty() method.

          Is it possible to apply the property on the selected item only?

          R Offline
          R Offline
          raven-worx
          Moderators
          wrote on 19 Oct 2017, 10:16 last edited by
          #4

          @Diracsbracket said in QTreeWidget: how to implement custom properties for stylesheet?:

          "QTreeWidget::item:selected:[rowstate="select"] {....}
          QTreeWidget::item:selected:[rowstate="play"] {....}
          ..."

          but that doesn't work. Maybe because the QWidgetItem class has no setProperty() method.

          exactly. Dynamic properties can only be applied to QWidgets!
          Qt stylesheets do not support to add custom pseudo-states and pseudo-elements. Thus it won't work what you are trying.
          QTreeWidget::item:selected:[rowstate=\"play\"] is not valid QSS --> QTreeWidget[rowstate=\"play\"]::item:selected: would be though.

          Is it possible to apply the property on the selected item only?

          no, not directly.
          Unless you subclass a custom QStyledItemDelegate and initialize the StyleOption for the painted index according to your widget's property.

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          D 2 Replies Last reply 19 Oct 2017, 10:30
          3
          • R raven-worx
            19 Oct 2017, 10:16

            @Diracsbracket said in QTreeWidget: how to implement custom properties for stylesheet?:

            "QTreeWidget::item:selected:[rowstate="select"] {....}
            QTreeWidget::item:selected:[rowstate="play"] {....}
            ..."

            but that doesn't work. Maybe because the QWidgetItem class has no setProperty() method.

            exactly. Dynamic properties can only be applied to QWidgets!
            Qt stylesheets do not support to add custom pseudo-states and pseudo-elements. Thus it won't work what you are trying.
            QTreeWidget::item:selected:[rowstate=\"play\"] is not valid QSS --> QTreeWidget[rowstate=\"play\"]::item:selected: would be though.

            Is it possible to apply the property on the selected item only?

            no, not directly.
            Unless you subclass a custom QStyledItemDelegate and initialize the StyleOption for the painted index according to your widget's property.

            D Offline
            D Offline
            Diracsbracket
            wrote on 19 Oct 2017, 10:30 last edited by
            #5

            @raven-worx
            OK then. It seems that I have no other choice then than to use the method outlined by @mrjj.

            In my current implementation which is setting a different stylesheet for the selected state every time a new item is selected, I can reduce the apparent overhead of setting a new stylesheet by first checking if the current stylesheet needs to be replaced or not:

            static inline void setTreeStyleSheet(QTreeWidget* tree, const QString& stylesheet)
            {
                if (tree->styleSheet() != stylesheet)
                    tree->setStyleSheet(stylesheet);
            }
            

            Although this in itself is a long string compare, it cuts out most of the perceived delay. Only when a different stylesheet is effectively set, the item selection change is a bit slower, but I can live with that (for now.)

            Can anyone confirm this delay overhead in setting a stylesheet? If so, does this delay grow larger as the tree grows? I would hope that the implementation would only update the items currently in the viewport, so the overhead would be independent of the tree size?

            1 Reply Last reply
            0
            • M Offline
              M Offline
              mrjj
              Lifetime Qt Champion
              wrote on 19 Oct 2017, 10:33 last edited by
              #6

              Hi
              Just as a note. the delegate i shown also works on
              an item based. The sample is a not viewtype.

              ui->treeWidget->setItemDelegate( new Delegate);

              D 1 Reply Last reply 19 Oct 2017, 10:36
              1
              • M mrjj
                19 Oct 2017, 10:33

                Hi
                Just as a note. the delegate i shown also works on
                an item based. The sample is a not viewtype.

                ui->treeWidget->setItemDelegate( new Delegate);

                D Offline
                D Offline
                Diracsbracket
                wrote on 19 Oct 2017, 10:36 last edited by
                #7

                @mrjj
                Thanks!

                M 1 Reply Last reply 19 Oct 2017, 10:42
                0
                • D Diracsbracket
                  19 Oct 2017, 10:36

                  @mrjj
                  Thanks!

                  M Offline
                  M Offline
                  mrjj
                  Lifetime Qt Champion
                  wrote on 19 Oct 2017, 10:42 last edited by mrjj
                  #8

                  @Diracsbracket
                  np :)
                  so "all" you still need to do is draw it differently when selected and playing.
                  for that you just use painter and the same bitmap as used in stylesheet.
                  should not be overly complicated.

                  1 Reply Last reply
                  1
                  • R raven-worx
                    19 Oct 2017, 10:16

                    @Diracsbracket said in QTreeWidget: how to implement custom properties for stylesheet?:

                    "QTreeWidget::item:selected:[rowstate="select"] {....}
                    QTreeWidget::item:selected:[rowstate="play"] {....}
                    ..."

                    but that doesn't work. Maybe because the QWidgetItem class has no setProperty() method.

                    exactly. Dynamic properties can only be applied to QWidgets!
                    Qt stylesheets do not support to add custom pseudo-states and pseudo-elements. Thus it won't work what you are trying.
                    QTreeWidget::item:selected:[rowstate=\"play\"] is not valid QSS --> QTreeWidget[rowstate=\"play\"]::item:selected: would be though.

                    Is it possible to apply the property on the selected item only?

                    no, not directly.
                    Unless you subclass a custom QStyledItemDelegate and initialize the StyleOption for the painted index according to your widget's property.

                    D Offline
                    D Offline
                    Diracsbracket
                    wrote on 31 Oct 2017, 06:31 last edited by
                    #9

                    @raven-worx said in QTreeWidget: how to implement custom properties for stylesheet?:

                    QTreeWidget::item:selected:[rowstate="play"] is not valid QSS --> QTreeWidget[rowstate="play"]::item:selected: would be though.

                    My problem seems to be solved indeed by using the @raven-worx solution.
                    I created a single long stylesheet string with custom properties as follows:

                    static const QString UM_STYLESHEET_SCRIPT = "QTreeView::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADAD, stop: 0.7 #939393, stop: 1.0 #545454); color: white;}"
                                                                "QTreeView[scriptState=\"play\"]::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #AFE4AA, stop: 0.7 #5DC256, stop: 1.0 #0BA102); color: white;}"
                                                                "QTreeView[scriptState=\"select\"]::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ADADFF, stop: 0.7 #4444FF, stop: 1.0 #0000FF); color: white;}"
                                                                "QTreeView[scriptState=\"record\"]::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFADAD, stop: 0.7 #FF4444, stop: 1.0 #FF0000); color: white;}"
                                                                "QTreeView[scriptState=\"norec\"]::item:selected {background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFDD99, stop: 0.7 #FFAA33, stop: 1.0 #FF8400); color: white;}";
                    
                    

                    Then, instead of replacing the stylesheet as I did before upon a state change, I just set the custom property, for example:

                    UM_SET_PROPERTY(ui->scriptTree, "scriptState", "play");
                    

                    where I have defined the convenience macro UM_SET_PROPERTY as follows:

                    #define UM_SET_PROPERTY(obj, property, value) (obj)->setProperty(property, value);\
                                                                  (obj)->style()->unpolish(obj);\
                                                                  (obj)->style()->polish(obj)
                    

                    Doing it like this seems to eliminate the delay I perceived with the previous method I used (the one where I used to replace the stylesheet)!
                    Thanks again.

                    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