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. QListViewDelegate paint issue
QtWS25 Last Chance

QListViewDelegate paint issue

Scheduled Pinned Locked Moved Solved General and Desktop
delegatepainterqlistview
14 Posts 3 Posters 7.8k 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.
  • R Offline
    R Offline
    Rory_1
    wrote on 19 Dec 2015, 16:49 last edited by
    #1

    I am working on an image viewer app. I am using a QListView called thumbview and a delegate to customize the iconview. When I click on a thumb the item is highlighted with a white border. When I click on another thumb its border turns white, but the previous selection sometimes leaves white border remnants, as illustrated by the red arrow.

    Alt text

    If I move the mouse outside the listview area the thumbs redraw correctly, cleaning up the remnants.

    void ThumbViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                               const QModelIndex &index) const
     {
        painter->save();
    
        QFont font = qvariant_cast<QFont>(index.data(Qt::FontRole));
        font.setPixelSize(10);
        QFontMetrics fm(font);
    
        QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
        QSize iconsize = icon.actualSize(option.decorationSize);
    
        QString filenameText = qvariant_cast<QString>(index.data(Qt::EditRole));
    
        QRect item = option.rect;
        QRect iconRect(item.left()+4, item.top()+4, iconsize.width(), iconsize.height());
        QPainterPath iconPath;
        iconPath.addRoundRect(iconRect, 12, 12);
    
        QRect textRect = item;
        textRect.setTop(item.top()+item.height()-fm.height()-10);
        QPainterPath textPath;
        textPath.addRoundedRect(textRect, 8, 8);
    
        painter->setRenderHint(QPainter::Antialiasing, true);
        painter->setRenderHint(QPainter::TextAntialiasing, true);
    
        painter->setFont(font);
    //    painter->fillPath(textPath, Qt::gray);
    //    painter->drawRoundedRect(textRect, 6, 6);
        painter->drawText(textRect, Qt::AlignCenter, filenameText);
    
        painter->setPen(Qt::darkGray);
        painter->setClipping(true);
        painter->setClipPath(iconPath);
        painter->drawPixmap(iconRect, icon.pixmap(iconsize.width(),iconsize.height()));
        painter->setClipping(false);
    
        painter->setPen(Qt::darkGray);
        painter->drawPath(iconPath);
    
        painter->setPen(Qt::darkGray);
        if (option.state & QStyle::State_Selected) {
            QPen pen(Qt::white);
            pen.setWidth(2);
            painter->setPen(pen);
        }
        painter->drawRoundedRect(item, 8, 8);
    
        painter->restore();
    }
    

    Any help much appreciated.

    Rory

    1 Reply Last reply
    0
    • K Offline
      K Offline
      kshegunov
      Moderators
      wrote on 19 Dec 2015, 18:19 last edited by kshegunov
      #2

      @Rory_1
      Hello,
      I think this might be a problem:

      painter->setPen(Qt::darkGray);
      if (option.state & QStyle::State_Selected) {
          QPen pen(Qt::white);
          pen.setWidth(2);
          painter->setPen(pen);
      }
      

      You seem to draw in 2px width with the white pen, but then use only the default 1px gray pen. What happens if you disable the antialiasing option (for debugging purposes), is a gray line visible next to the white one?

      Kind regards.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      0
      • R Offline
        R Offline
        Rory_1
        wrote on 19 Dec 2015, 21:07 last edited by Rory_1
        #3

        I tried that with/without the aliasing. The pen width and aliasing does not make any difference. I want to know what is happening when I move the mouse away as it is triggering a refresh of the listview that clears up the problem. I tried creating a signal/slot back to my listview to update the model but that did not help.

        K 1 Reply Last reply 20 Dec 2015, 11:03
        0
        • R Rory_1
          19 Dec 2015, 21:07

          I tried that with/without the aliasing. The pen width and aliasing does not make any difference. I want to know what is happening when I move the mouse away as it is triggering a refresh of the listview that clears up the problem. I tried creating a signal/slot back to my listview to update the model but that did not help.

          K Offline
          K Offline
          kshegunov
          Moderators
          wrote on 20 Dec 2015, 11:03 last edited by
          #4

          @Rory_1

          I want to know what is happening when I move the mouse away as it is triggering a refresh of the listview that clears up the problem.

          Hello,
          You probably are getting a full repaint request, and that will erase the background, and when your code runs after that it will display properly. I still think that there is some strange problem with the painting over the white line, when the mouse leaves your item. I believe that is the case, because it is partially erased. I suggest using a red line for the inactive items, setting the width of the pen to 2px for the inactive items and disabling antialiasing all at the same time. When you get the partially erased border, take a look for a red line next to the white one. If you see such a thing, that simply means that you're not fully erasing the active item border.

          Kind regards.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          0
          • R Offline
            R Offline
            Rory_1
            wrote on 20 Dec 2015, 15:48 last edited by
            #5

            I eliminated everything except the border. The top-left item is the previously selected item and the top-right is the currently selected item.

            Alt text
            and then after I mouse-over the scrollbar to the right:
            Alt text

            Here is the code:

            void ThumbViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                       const QModelIndex &index) const
             {
                painter->save();
            
                QRect itemRect = option.rect;
                if (option.state & QStyle::State_Selected) {
                    QPen selectedPen(Qt::white);
                    selectedPen.setWidth(2);
                    painter->setPen(selectedPen);
                } else {
                    QPen activePen(Qt::red);
                    activePen.setWidth(2);
                    painter->setPen(activePen);
                }
                painter->drawRoundedRect(itemRect, 8, 8);
            
                painter->restore();
            }
            
            1 Reply Last reply
            0
            • K Offline
              K Offline
              kshegunov
              Moderators
              wrote on 20 Dec 2015, 16:21 last edited by kshegunov
              #6

              Hello,
              Now this is clearer to diagnose. Try erasing the background before painting, like this:

              painter->fillRect(option.rect, option.backgroundBrush);
              // ... Rest of your painting code
              

              or

              painter->eraseRect(option.rect);
              

              As a side note, I also advise you to use the test function to check for a flag, like this:

              if (option.state.testFlag(QStyle::State_Selected))  {
                  // ... Flag is set
              }
              

              Kind regards.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              0
              • R Offline
                R Offline
                Rory_1
                wrote on 20 Dec 2015, 17:18 last edited by
                #7

                I tried all of the above: erasing rect and testFlag. No change what-so-ever to behavior. The weird thing is this behavior does not always occur. Sometimes the previous and/or currently selected items paint correctly.

                I am going to track which events are firing when I mouse-over leaving the listview. Maybe I can get a work-around using a signal/slot.

                K 1 Reply Last reply 20 Dec 2015, 17:26
                0
                • R Rory_1
                  20 Dec 2015, 17:18

                  I tried all of the above: erasing rect and testFlag. No change what-so-ever to behavior. The weird thing is this behavior does not always occur. Sometimes the previous and/or currently selected items paint correctly.

                  I am going to track which events are firing when I mouse-over leaving the listview. Maybe I can get a work-around using a signal/slot.

                  K Offline
                  K Offline
                  kshegunov
                  Moderators
                  wrote on 20 Dec 2015, 17:26 last edited by
                  #8

                  @Rory_1
                  Take note that your highlighted border is not properly drawn as well. It seems half the drawing occurs at the old item, and the other half at the new item. This is pretty strange. Do you mind sharing a bit more of your code, for example how you set up your delegate and any event overrides you might have in your list view?

                  Kind regards.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • R Offline
                    R Offline
                    Rory_1
                    wrote on 20 Dec 2015, 17:44 last edited by
                    #9

                    Thanks very much for your help! I have to go out for the afternoon, but I will follow-up later.

                    1 Reply Last reply
                    0
                    • R Offline
                      R Offline
                      Rory_1
                      wrote on 21 Dec 2015, 14:35 last edited by
                      #10

                      I have created a small project that shows the problem. It contains:

                      • testdelegate. pro
                      • main.cpp
                      • mw.h
                      • mw.cpp
                      • testdelegate.h
                      • testdelegate.cpp

                      testDelegate. pro:

                      QT       += core gui
                      
                      greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
                      
                      TARGET = testDelegate
                      TEMPLATE = app
                      
                      
                      SOURCES += main.cpp\
                              mw.cpp \
                          thumbviewdelegate.cpp
                      
                      HEADERS  += mw.h \
                          thumbviewdelegate.h
                      

                      main.cpp:

                      #include "mw.h"
                      #include <QApplication>
                      
                      int main(int argc, char *argv[])
                      {
                          QApplication a(argc, argv);
                          MW w;
                          w.show();
                      
                          return a.exec();
                      }
                      

                      mw.h: (mainwindow)

                      #ifndef MW_H
                      #define MW_H
                      
                      #include <QMainWindow>
                      #include <QtWidgets>
                      #include "thumbviewdelegate.h"
                      
                      class MW : public QMainWindow
                      {
                          Q_OBJECT
                      
                      public:
                          explicit MW(QWidget *parent = 0);
                      
                      private:
                          QHBoxLayout *mainLayout;
                          QListView *thumbView;
                          QStandardItemModel *thumbViewModel;
                          QStandardItem *thumbIitem;
                          ThumbViewDelegate *thumbviewDelegate;
                      };
                      
                      #endif // MW_H
                      

                      mw.cpp:

                      #include "mw.h"
                      
                      MW::MW(QWidget *parent) :
                          QMainWindow(parent)
                      {
                          resize(600, 600);
                          thumbView = new QListView;
                          mainLayout = new QHBoxLayout;
                          mainLayout->setContentsMargins(0, 0, 0, 0);
                          mainLayout->setSpacing(0);
                          mainLayout->addWidget(thumbView);
                          QWidget *centralWidget = new QWidget;
                          centralWidget->setLayout(mainLayout);
                          setCentralWidget(centralWidget);
                      
                          thumbView->setSelectionMode(QAbstractItemView::ExtendedSelection);
                          thumbView->setViewMode(QListView::IconMode);
                          thumbView->setResizeMode(QListView::Adjust);
                          thumbView->setWordWrap(true);
                          thumbView->setDragEnabled(true);
                          thumbView->setEditTriggers(QAbstractItemView::NoEditTriggers);
                          thumbView->setUniformItemSizes(false);
                      
                          thumbViewModel = new QStandardItemModel();
                          thumbView->setModel(thumbViewModel);
                          thumbViewModel->clear();
                          thumbviewDelegate = new ThumbViewDelegate(this);
                          thumbView->setItemDelegate(thumbviewDelegate);
                          thumbView->setSpacing(10);
                      
                          for (int i = 0; i < 10; ++i) {
                              thumbIitem = new QStandardItem;
                              thumbViewModel->appendRow(thumbIitem);
                          }
                      }
                      

                      testdelegate.h:

                      #ifndef THUMBVIEWDELEGATE_H
                      #define THUMBVIEWDELEGATE_H
                      
                      #include <QtWidgets>
                      
                      class ThumbViewDelegate : public QStyledItemDelegate
                      {
                          Q_OBJECT
                      
                      public:
                          ThumbViewDelegate(QObject *parent = 0);
                      
                          void paint(QPainter *painter, const QStyleOptionViewItem &option,
                                     const QModelIndex &index) const;
                      
                          QSize sizeHint(const QStyleOptionViewItem &option,
                                         const QModelIndex &index ) const;
                      };
                      
                      #endif // THUMBVIEWDELEGATE_H
                      

                      testdelegate.cpp:

                      #include "thumbviewdelegate.h"
                      
                      ThumbViewDelegate::ThumbViewDelegate(QObject *parent)
                      {
                      }
                      
                      QSize ThumbViewDelegate::sizeHint(const QStyleOptionViewItem &  option ,
                                                    const QModelIndex & index) const
                      {
                          return(QSize(200,200));
                      }
                      
                      void ThumbViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                                 const QModelIndex &index) const
                       {
                          painter->save();
                      
                          if (option.state.testFlag(QStyle::State_Selected)) {
                              QPen selectedPen(Qt::red);
                              selectedPen.setWidth(5);
                              painter->setPen(selectedPen);
                          } else {
                              QPen activePen(Qt::black);
                              activePen.setWidth(5);
                              painter->setPen(activePen);
                          }
                          painter->drawRect(option.rect);
                      
                          painter->restore();
                      }
                      
                      1 Reply Last reply
                      0
                      • K Offline
                        K Offline
                        kshegunov
                        Moderators
                        wrote on 21 Dec 2015, 19:04 last edited by
                        #11

                        Hello,
                        Right, It took me a few minutes, but I've found the problem - you're painting outside of your allotted space. This is why the behavior you're getting is so erratic - you depend on when the list view will (or will not) erase it's contents. Brace yourself ;), here is the problematic fragment:

                        painter->drawRect(option.rect);
                        

                        When you draw a stroked rectangle the area you provide is for the insides (the pen requires additional space).
                        This means that you draw your border in actuality outside of the item rectangle. This is also the reason why the rounded corners actually get erased (they happen to be inside option.rect). The fix is quite simple, here is an example how you could solve your problem:

                        painter->save();
                        
                        // Make the rect smaller to accommodate the border.
                        static const qint32 penSize = 4;
                        QPoint strokeOffset(penSize, penSize);
                        QRect borderRect(option.rect.topLeft() + strokeOffset, option.rect.bottomRight() - strokeOffset);
                        
                        // ... Rest of your code
                        
                        painter->drawRect(borderRect);    // Draw the border properly
                        

                        I hope this helps.
                        Kind regards.

                        Read and abide by the Qt Code of Conduct

                        1 Reply Last reply
                        1
                        • R Offline
                          R Offline
                          Rory_1
                          wrote on 21 Dec 2015, 19:35 last edited by
                          #12

                          Thank you so much! I don't think I would have figured that out. My first Christmas present :*)

                          K 1 Reply Last reply 21 Dec 2015, 19:42
                          0
                          • R Rory_1
                            21 Dec 2015, 19:35

                            Thank you so much! I don't think I would have figured that out. My first Christmas present :*)

                            K Offline
                            K Offline
                            kshegunov
                            Moderators
                            wrote on 21 Dec 2015, 19:42 last edited by
                            #13

                            @Rory_1
                            You're welcome. Good luck with your application!

                            Kind regards.

                            Read and abide by the Qt Code of Conduct

                            1 Reply Last reply
                            0
                            • raven-worxR Offline
                              raven-worxR Offline
                              raven-worx
                              Moderators
                              wrote on 22 Dec 2015, 08:56 last edited by
                              #14

                              just for addition: see this
                              So i ncase you draw anti-aliased, the border-offset would just be the half of the border-width.

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

                              1 Reply Last reply
                              0

                              5/14

                              20 Dec 2015, 15:48

                              9 unread
                              • Login

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