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.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.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #6

    One thing that looks out of place in your code is the call to painter->end(); in the middle.

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

      @SGaist I have to be honest and say that I don't know what I'm doing there. Notice that end() comes before the paint command and begin() comes after it's finished. I'm following the recipe in the link I provided in the question. Besides, I tried all kinds of combinations. I couldn't understand the painting mechanism from the manual, and I'd appreciate pointing me in the right direction, or assisting me in anyway to get this to work.

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

        I noticed that and it doesn't make sense, the painter is already active and your just stopping it. Did you confuse these method with the save and restore methods ?

        In any case, did you already saw the Star Delegate Example ? It shows a fully customised paint method.

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

          @SGaist I succeeded in painting polygons a long time ago. That's easy. In fact, like I said in the question, the selection painting works fine (in addition to polygons). The guy here got +9 for this way. He explains that the reason for stopping is that "2 painters cannot work at the same time". Check it here:

          https://stackoverflow.com/questions/6452838/render-qwidget-in-paint-method-of-qwidgetdelegate-for-a-qlistview/18983353#18983353

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

            I would like to point out that the problem is still there, and I'd appreciate the help of an expert.

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

              you can use this as a base class, it's far from perfect (it does not support stylesheet and it's quite slow) but does its job, it just requires you to reimplement the setEditorData method, the paint is handled already:

              #include <QStyledItemDelegate>
              #include <QPainter>
              template <class T>
              class WidgetDelegate : public QStyledItemDelegate{
              #ifdef Q_COMPILER_STATIC_ASSERT
                  static_assert(std::is_base_of<QWidget,T>::value,"Template argument must be a QWidget");
              #endif
                  Q_DISABLE_COPY(WidgetDelegate)
              public:
                  explicit WidgetDelegate(QObject* parent = Q_NULLPTR)
                      :QStyledItemDelegate(parent)
                      , m_baseWid(new T)
                  {}
                  virtual ~WidgetDelegate(){
                      delete m_baseWid;
                  }
                  virtual void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE{
                      setEditorData(m_baseWid,index);
                      m_baseWid->resize(option.rect.size());
                      QPixmap pixmap(option.rect.size());
                      m_baseWid->render(&pixmap);
                      painter->drawPixmap(option.rect,pixmap);
                  }
                  virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{
                      Q_UNUSED(option);
                      setEditorData(m_baseWid,index);
                      return m_baseWid->sizeHint();
                  }
                  virtual void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE =0;
              private:
                  T* m_baseWid;
              };
              

              An example usage would be:

              class TestDelegate : public WidgetDelegate<QLabel>{
                  Q_OBJECT
                  Q_DISABLE_COPY(TestDelegate)
              public:
                  explicit TestDelegate(QObject* parent=Q_NULLPTR)
                      :WidgetDelegate<QLabel>(parent)
                  {}
                  virtual void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
                      QLabel* const lab = qobject_cast<QLabel*>(editor);
                      Q_ASSERT(lab);
                      lab->setText("Content: <b>"+index.data().toString()+"</b>");
                  }
              };
              
              int main(int argc, char *argv[])
              {
              
                  QApplication app(argc,argv);
                  QListWidget w;
                  w.model()->insertColumns(0,2);
                  w.model()->insertRows(0,2);
                  w.model()->setData(w.model()->index(0,0),"0,0");
                  w.model()->setData(w.model()->index(1,0),"1,0");
                  TestDelegate* tempDelegate = new TestDelegate(&w);
                  w.setItemDelegate(tempDelegate);
                  w.show();
                  return app.exec();
              }
              

              "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
              3
              • 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