Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Paint widget directly on QListView with QStyledItemDelegate::paint()
Qt 6.12 LTS is out! See what's new in the release blog

Paint widget directly on QListView with QStyledItemDelegate::paint()

Scheduled Pinned Locked Moved Solved General and Desktop
21 Posts 3 Posters 15.4k 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.
  • S Offline
    S Offline
    SamerAfach
    wrote on last edited by
    #12

    @VRonin First, sorry for the late response. I moved to another city and things were messy.

    Thanks for working out a viable solution, but I don't understand how this is an improvement to what I provided already. I provided in my question a solution that uses a pixmap without needing an editor, and I was complaining that a pixmap was needed. Your solution needs a pixmap + editor.

    A pixmap makes the widget look ugly, and dynamic widgets (like progress bar that glows on Windows from left to right) is always rendered at the starting time, making the glow show only at one place repeatedly. If using a pixmap is the only way to go, could you please explain how to make the rendering time-dependent? (so that a glowing progress bar will glow over time correctly).

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

      You are right in pointing out this is not the ultimate solution but just a "make-it-work" one.

      If, for example, you want a glowing progressbar you'd need something like:

      #include <QStyledItemDelegate>
      #include <QPainter>
      #include <QVariantAnimation>
      #include <QLinearGradient>
      class TestDelegate : public QStyledItemDelegate {
          Q_OBJECT
          Q_DISABLE_COPY(TestDelegate)
      public:
          TestDelegate(QObject* parent=Q_NULLPTR)
              : QStyledItemDelegate(parent)
              , m_glowAnimation(new QVariantAnimation(this))
          {
              m_glowAnimation->setEasingCurve(QEasingCurve(QEasingCurve::Linear));
              m_glowAnimation->setDuration(1000);
              m_glowAnimation->setLoopCount(-1);
              m_glowAnimation->setStartValue(0.0);
              m_glowAnimation->setKeyValueAt(0.5,1.0);
              m_glowAnimation->setEndValue(0.0);
              connect(m_glowAnimation,&QVariantAnimation::valueChanged,this,&TestDelegate::requestRepaint,Qt::QueuedConnection);
              m_glowAnimation->start();
          }
          Q_SIGNAL void requestRepaint();
      
          virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{
              painter->save();
              QRect progressRect(
                          option.rect.topLeft()
                          ,QSize(static_cast<double>(option.rect.width()) *index.data(Qt::UserRole).toDouble() /100.0,option.rect.height())
                          );
              QLinearGradient glowGradient;
              const double currGlow =m_glowAnimation->currentValue().toDouble();
              glowGradient.setColorAt(0.0,Qt::green);
              glowGradient.setColorAt(1.0,Qt::green);
              glowGradient.setColorAt(currGlow,Qt::white);
              glowGradient.setStart(progressRect.topLeft().x(),progressRect.topLeft().y()/2.0);
              glowGradient.setFinalStop(progressRect.bottomRight().x(),progressRect.topLeft().y()/2.0);
              painter->fillRect(progressRect,glowGradient);
              painter->restore();
              QStyledItemDelegate::paint(painter,option,index);
          }
      
      private:
          QVariantAnimation* m_glowAnimation;
          double m_glowPosition;
      };
      

      "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
      • S Offline
        S Offline
        SamerAfach
        wrote on last edited by
        #14

        @VRonin Thank you for the another example. I have tried for a few hours to get this to work, but it wouldn't work. There are many problems in getting this to work. The first problem is that, even if this works, it'll be generic and all progress bars will be glowing at the same vertical position, since the delegate uses the same glowing position for all list widgets. Now assuming we're OK with that last issue, the example you provided draws directly on the rectangle of the widget, not the progress bar, which takes us back to square-one, where I completely fail at painting without a pixmap. So the next attempt for me was to draw this on the pixmap, which means that I have to do the math to calculate the position of the progress bar with respect to the widget, and given that I'm not an expert in QPainter, there's no feedback mechanism to know whether what I'm doing makes sense, except to just compile and try again and again, until I gave up.

        So, could you please provide the same code with something compatible with the working version of my code that uses a QPixmap from my original question? I provide the code below again for convenience. As this is incomplete, just assume that itemWidget has a member progressBar of type QProgressBar* that we have to make glow.

        void FileQueueItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
            QPaintDevice* original_pdev_ptr = painter->device();
        
            FileQueueListItem* itemWidget = reinterpret_cast<FileQueueListItem*>(index.data(Qt::UserRole).value<void*>());
        
            itemWidget->setGeometry(option.rect);
            painter->end();
        
            QPixmap pixmap(itemWidget->size());
            if (option.state & QStyle::State_Selected)
                pixmap.fill(option.palette.highlight().color());
            else
                pixmap.fill(option.palette.background().color());
            itemWidget->render(&pixmap,QPoint(),QRegion(),QWidget::RenderFlag::DrawChildren);
        
            painter->begin(original_pdev_ptr);
            painter->drawPixmap(option.rect, pixmap);
        }
        
        VRoninV 1 Reply Last reply
        0
        • S SamerAfach

          @VRonin Thank you for the another example. I have tried for a few hours to get this to work, but it wouldn't work. There are many problems in getting this to work. The first problem is that, even if this works, it'll be generic and all progress bars will be glowing at the same vertical position, since the delegate uses the same glowing position for all list widgets. Now assuming we're OK with that last issue, the example you provided draws directly on the rectangle of the widget, not the progress bar, which takes us back to square-one, where I completely fail at painting without a pixmap. So the next attempt for me was to draw this on the pixmap, which means that I have to do the math to calculate the position of the progress bar with respect to the widget, and given that I'm not an expert in QPainter, there's no feedback mechanism to know whether what I'm doing makes sense, except to just compile and try again and again, until I gave up.

          So, could you please provide the same code with something compatible with the working version of my code that uses a QPixmap from my original question? I provide the code below again for convenience. As this is incomplete, just assume that itemWidget has a member progressBar of type QProgressBar* that we have to make glow.

          void FileQueueItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
          {
              QPaintDevice* original_pdev_ptr = painter->device();
          
              FileQueueListItem* itemWidget = reinterpret_cast<FileQueueListItem*>(index.data(Qt::UserRole).value<void*>());
          
              itemWidget->setGeometry(option.rect);
              painter->end();
          
              QPixmap pixmap(itemWidget->size());
              if (option.state & QStyle::State_Selected)
                  pixmap.fill(option.palette.highlight().color());
              else
                  pixmap.fill(option.palette.background().color());
              itemWidget->render(&pixmap,QPoint(),QRegion(),QWidget::RenderFlag::DrawChildren);
          
              painter->begin(original_pdev_ptr);
              painter->drawPixmap(option.rect, pixmap);
          }
          
          VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #15

          @SamerAfach said in Paint widget directly on QListView with QStyledItemDelegate::paint():

          The first problem is that, even if this works, it'll be generic and all progress bars will be glowing at the same vertical position

          This is easily solved by just adding the glow position as another role in your model.

          @SamerAfach said in Paint widget directly on QListView with QStyledItemDelegate::paint():

          So the next attempt for me was to draw this on the pixmap

          The problem is that you are trying to take an existing widget and paint it. What you should do is, in the case of a progressbar, is fill a QStyleOptionProgressBar struct with what you want to draw, then call qApp->style()->drawControl(QStyle::CE_ProgressBar, optionProgressBar, painter)

          "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
          • S Offline
            S Offline
            SamerAfach
            wrote on last edited by
            #16

            @VRonin Thanks for the response, and I'd like to say that I really appreciate your patience and helpfulness.

            Actually, I know this call/solution (QStyleOptionProgressBar) from the torrent example in Qt. However, this has the problem that it paints a progress bar only. Is it possible to use this and draw a whole widget that includes a progress bar as a part of it? I would appreciate an example on that.

            VRoninV 1 Reply Last reply
            0
            • S SamerAfach

              @VRonin Thanks for the response, and I'd like to say that I really appreciate your patience and helpfulness.

              Actually, I know this call/solution (QStyleOptionProgressBar) from the torrent example in Qt. However, this has the problem that it paints a progress bar only. Is it possible to use this and draw a whole widget that includes a progress bar as a part of it? I would appreciate an example on that.

              VRoninV Offline
              VRoninV Offline
              VRonin
              wrote on last edited by
              #17

              @SamerAfach said in Paint widget directly on QListView with QStyledItemDelegate::paint():

              Is it possible to use this and draw a whole widget that includes a progress bar as a part of it?

              It is, if you lay out things vertically or horizontally it's very easy. if you go to grid or anything more complex it gets hard really fast.

              The trick btw is just to set the rect member of the relevant QStyleOption to the rectangle where you want the widget to be printed

              "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
              • S Offline
                S Offline
                SamerAfach
                wrote on last edited by SamerAfach
                #18

                @VRonin
                Well, if I lay everything horizontally, then I could just use a QTableView :-)

                I assume you're saying that things get hard really fast because one has to calculate the xy coordinates of the controls before painting them. If this is correct, it tells me that it's possible to use drawControl() multiple times in the same paint() to draw in different positions. Is there an example that shows how to do this and draw different controls in different positions (rects)? If such an example exists, I can imagine a viable solution of getting the rect of every control in the widget, and drawing it myself in the delegate's paint() using drawControl(). What do you think?

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

                  All QStyleOption have a rect member that determines where the control is printed

                  "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
                  • S Offline
                    S Offline
                    SamerAfach
                    wrote on last edited by
                    #20

                    @VRonin Thanks. I'll try to paint everything with drawControl() and provide feedback.

                    1 Reply Last reply
                    0
                    • S Offline
                      S Offline
                      SamerAfach
                      wrote on last edited by
                      #21

                      @VRonin I managed to paint everything manually... I had to calculate the coordinates of every widget and draw it, and it worked. I have to say it's quite depressing that Qt doesn't have a solution to paint a widget as is.

                      Thanks for the help, and have a nice day :-)

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