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. [QStyledItemDelegate - qss] render custom widget and its stylesheet
QtWS25 Last Chance

[QStyledItemDelegate - qss] render custom widget and its stylesheet

Scheduled Pinned Locked Moved Unsolved General and Desktop
item delegateqssmouse event
6 Posts 3 Posters 3.1k 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.
  • N Offline
    N Offline
    nico88desmo
    wrote on last edited by
    #1

    Dear all,

    this is my firts post on this forum, even if I'm following this for 2 years.
    I've decided writing this post because I'm not able to find any solution right now.

    This is the problem:
    I need to show some values using a listview and I'd like to use QStyledItemDelegate for data viewing.
    I create AlertItemDelegate class that inheritance QStyledItemDelegate and I've rewritten paint method as follow:

    // header
    class AlertItemDelegate : public QStyledItemDelegate {
        Q_OBJECT
    
    public:
        using Ptr = AlertItemDelegate*;
        using ConstPtr = const AlertItemDelegate*;
    
    public:
        explicit AlertItemDelegate(QObject* parent = Q_NULLPTR);
        ~AlertItemDelegate();
    
    public:
        void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
        QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
    };
    
    // implementation
    void AlertItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
    
        QVariant data = index.data();
    
        if (data.canConvert<Error>()) {
    
            QString test = this->getTest();
    
            auto&& styleOption = QStyleOptionViewItem(option);
    
            ErrorItemWidget widget;
            widget.resize(option.rect.width(), option.rect.height());
            widget.setError(qvariant_cast<Error>(data));
            painter->save();
            painter->translate(option.rect.topLeft());
            widget.render(painter);
            painter->restore();
    
        } else {
            QStyledItemDelegate::paint(painter, option, index);
        }
    
    }
    

    As you can see in code, during paintEvent I create a custom widget (ErrorItemWidget), set widget values and finally I render it.
    This is paintEvent method, called when I render the widget;

    void ErrorItemWidget::paintEvent(QPaintEvent* event) {
    
        QWidget::paintEvent(event);
        QStyleOption opt;
        opt.initFrom(this);
        QStylePainter p(this);
    
        int testWidth = size().width();
        int testheight = size().height();
    
        p.save();
        p.drawPrimitive(QStyle::PE_Widget, opt); 
    
        /*
          *** PROBLEM !!! MOUSE HOVER DOES NOT WORK IN DELEGATE
          * QStyleOption opt seems not correctly initialized
        int value = opt.state;
        if (opt.state & QStyle::State_MouseOver)
            this->setStyleSheet("color: red");
        else
            this->setStyleSheet("color: white");
        QString test = this->styleSheet();
        */
    
        QRect r = opt.rect;
        p.setRenderHint(QPainter::Antialiasing);
    
        QPixmap icon;
        switch (this->errorType) {
            case ErrorType::INFO : icon.load(infoIcon); break;
            case ErrorType::WARNING : icon.load(warningIcon); break;
            case ErrorType::ERROR : icon.load(errorIcon); break;
            case ErrorType::FATAL : icon.load(fatalIcon); break;
        }
    
        int iconTopPosition = /*contenctRect.y() +*/ (r.height() - iconHeight)/2;
        icon = icon.scaled(iconWidth, iconHeight, Qt::AspectRatioMode::KeepAspectRatio, Qt::SmoothTransformation);
        p.drawPixmap(iconLeft, iconTopPosition, iconWidth, iconHeight, icon);
    
        QFont font = this->font();
        p.setFont(font);
    
        QFontMetrics fMetrics(font);
        int capHeight = fMetrics.capHeight();
    
        int yText = /*contenctRect.y()*/ + (r.height() - capHeight)/2 + capHeight;
        QString deviceKeyStr = Utils::getStringFromDeviceKey(static_cast<DeviceKey>(deviceKey));
    
        p.drawText(deviceKeyLeft, yText, deviceKeyStr);
        p.drawText(errorIdLeft, yText, QString::number(errorId));
        p.drawText(errorDescriptionLeft, yText, errorDescription);
    
        p.restore();
    
    }
    

    This works well, however there is something that I don't like.

    1. using render tecnique, during widget paintEvent seems that QStyleOption has not the same values of QStyleOption of delegate; how can I pass it from delegate to widget? Is it possible? This should be useful for rendering in case of mouseHover case.
    2. to draw widget correctly, I have to set sizeHint of the widget; in this case, I set minHeight = maxHeight using qss as follow:
    .ErrorItemWidget {
        min-width: 30px;
        min-height: 46px;
        max-height: 46px;
        padding: 0px 16px;
        border-bottom-width: 2px;
        border-style: solid;
        font-size: 16px;
        qproperty-iconWidth: 24;
        qproperty-iconHeight: 24;
        qproperty-iconLeft: 16;
        qproperty-deviceKeyLeft: 70;
        qproperty-errorIdLeft: 160;
        qproperty-errorDescriptionLeft: 216;
    }
    

    Is possible to get min-height or max-height values from qss? Or do I need to hardly write the same values onto code?

    Thanks all in advance!
    Nicola

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      Do you mean parse the style sheet somewhere to get the value ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • N Offline
        N Offline
        nico88desmo
        wrote on last edited by
        #3

        Hi, yes I mean something similar or, if possible, use qt parsing stylesheet system to take some qss values.
        What I'd like to do is completely separate styles attribute from C++ code: set size, color, border, etc. in qss stylesheets and using C++ code for logic only.

        Is this possible using delegate?

        My ideal process would be the following:

        1. inherit delegate class
        2. delegate::size-hint: create local widget and call its sizeHint method
        3. widget::size-hint: get min-height/max-height and others values from qss stylesheet
        4. delegate::paint: create local widget and render it (this will call widget paintEvent method)
        5. widget::paintEvent: get QStyleOption from delegate and render widget correctly (for hover cases for example)

        Is this feasible?

        Thanks,
        Nicola

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

          Sorry if it sounds dumb but why are you creating a whole widget if you then implement the painting manually. Can't you just do in AlertItemDelegate::paint everything you do inside ErrorItemWidget::paintEvent and solve all your problems?

          "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
          0
          • N Offline
            N Offline
            nico88desmo
            wrote on last edited by
            #5

            From the beginning, I use this widget in another context so I'd like (if possible) use the same code for both purpose.
            Anyway, does it exist a way to load stylesheet file (.qss) inside AlertItemDelegate::paint method?

            If so, I can paint everything in paint delegate method without creating temporary widget object.

            1 Reply Last reply
            0
            • N Offline
              N Offline
              nico88desmo
              wrote on last edited by nico88desmo
              #6

              This is what I've done right now.
              Using the same code for painting and widget, I'm able to use a custom widget as usual; moreover, for listview I'm able to render the same widget correctly (because calling render method of widget, the widget is able to read .qss stylesheet)

              Here is the example:

              this is a single widget
              0_1562332451778_widget.png

              and this is the rendered widget inside the listview
              0_1562332527764_listview-render.png

              in listview, as you can see, I'm able to render widget correctly and, in case of focus, I can change focus style using a custom property

              this is the code:

              void AlertItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
              
                  QVariant data = index.data();
              
                  if (data.canConvert<Error>()) {
                      auto&& styleOption = QStyleOptionViewItem(option);
                      ErrorItemWidget widget;
                      if (styleOption.state & QStyle::State_HasFocus)
                          widget.setProperty("Test", true); // <-- custom focus property
                      else
                          widget.setProperty("Test", QVariant::Invalid); // <-- remove property for normal state
              
                      widget.resize(option.rect.width(), option.rect.height());
                      widget.setError(qvariant_cast<Error>(data));
                      painter->save();
                      painter->translate(option.rect.topLeft());
                      widget.render(painter);
                      painter->restore();
              
                  } else {
                      QStyledItemDelegate::paint(painter, option, index);
                  }
              
              }
              

              The drawback of this solution is that I need to create a widget for each model item (however, I've render 2000 elements and I don't see any performance hit)
              The other drawback is that the height of each item is hardly code inside size-hint method, that is:

              QSize AlertItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
              
                  int width = option.rect.width();
                  int height = 48; // <--- if I can get this from stylesheet, I'll be happy :)
                  return QSize(width, height);
              
              }
              

              The good thing is that I can change widget stylesheet using .qss file; inside C++ code there isn't any style detail.

              Any suggestion to get height value from stylesheet?

              Thanks all!
              Nicola

              1 Reply Last reply
              0

              • Login

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