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. Painting Widget with QPainter
QtWS25 Last Chance

Painting Widget with QPainter

Scheduled Pinned Locked Moved General and Desktop
14 Posts 3 Posters 16.7k 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.
  • M Offline
    M Offline
    maximus
    wrote on last edited by
    #1

    Hi guys,

    I'm trying to paint a Widget in my custom Delegate, basically I just want to show a widget inside a cell of a tableView.

    I found multiple way to do that, but it's not working as I want.

    Solution 1:
    Use the .render method of QWidget to render it
    Problem : Nothing is painted (see "screenshot":https://www.dropbox.com/s/qeiisgzw7e5t1m1/solution0_render.png)
    @
    EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget();
    widgetTarget->targetStart->setText("Test");
    widgetTarget->render(painter, option.rect.center(), option.rect, QWidget::DrawChildren );
    @

    Solution 2:
    Convert the QWidget to a pixmap and then paint the pixmap with the painter
    problem : Color and background all messed up (see "screenshot":https://www.dropbox.com/s/mr92gcibngvir6x/solution2_pixmap.png)

    @
    EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget();
    QPixmap pix = QPixmap::grabWidget(widgetTarget);
    painter->drawPixmap(option.rect, pix);
    @

    Solution 3:
    Try to replicate the Widget and draw the stuff manually using drawText
    Problem : Ok to display one line of data, but I don't know how to replicate my GridLayout in here. (see "screenshot":https://www.dropbox.com/s/3m8eh5sc174iyxd/solution3_drawText.png)

    @
    std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));

        int targetStepPower = interval->getPowerStepType();
        qDebug() << "TARGET STEP!" << targetStepPower;
        QString str_Step = Interval::getStepTypeFromInt( targetStepPower );
        double startFTP = interval->getFTP_start() * 100;
        double endFTP = interval->getFTP_end() * 100;
        int range = interval->getFTP_range();
    
        painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, str_Step); //Draw all other data also.. with good layout?@
    

    If someone has figured how to use the render() method of QWidget, or fix the QPixmap display problem, I think it would be a better solution than using drawText... thank you!


    Free Indoor Cycling Software - https://maximumtrainer.com

    1 Reply Last reply
    0
    • raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      ad solution 1:
      make sure that your painter has not been translated and paints to the correct location on the viewport. "option.rect.center()" doesn't seem correct here...
      Please post the whole painting code of your delegate.

      i wouldn't suggest solution 3... for sake of simplicity.

      --- 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
      • N Offline
        N Offline
        NicuPopescu
        wrote on last edited by
        #3

        I think the solution 3 could be used with some "tricks", in order to have texts arranged in "subcells" in a "cell" ... what is a "cell"?: it is thought as successive standard(qt) table items in a row

        so take any successive items in a table row, in the amount of your grid layout columns and then controls the drawText as follow:

        1. for drawing text in a certain column(subcell) with index.column()
        2. for drawing text in certain subcell's row with new line character "\n"
          i.e.
          @ painter->drawText(option.rect.adjusted(4,4,0,0),QString("\n")+your text@

        I used this and it worked, and for changing text font or color by QSS I used dummy/hidden widgets styled externally and using their values in delegate's painter

        1 Reply Last reply
        0
        • M Offline
          M Offline
          maximus
          wrote on last edited by
          #4

          Thanks for your help,

          For the render method of QWidget, I don't really understand the arguments that I need to pass. Usually I use painter and option.rect (where to display) in order to paint, so i'm kind of lost with those 4 parameters

          Link to function :
          http://qt-project.org/doc/qt-5.0/qtwidgets/qwidget.html#render-2

          Here is the full paint method of my delegate:
          @//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
          void SpinBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

          if (option.state & QStyle::State_Selected) {
              painter->setPen(QPen( Qt::red, 5 ));
              painter->drawRect(option.rect);
              return;
          }
          
          painter->setPen(QPen( Qt::white, 1 ));
          
          /// Type
          if (index.column() == 0) {
              int value = index.model()->data(index, Qt::DisplayRole).toInt();
              QString intervalType = Interval::getTypeFromInt(value);
          
              painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, intervalType);
          }
          
          /// Duration
          else if (index.column() == 1) {
              QTime time = index.model()->data(index, Qt::DisplayRole).toTime();
              QString toDisplay = Util::showQTimeAsString(time);
              painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, toDisplay );
          }
          
          /// Display Message
          else if (index.column() == 2) {
              QString msg = index.model()->data(index, Qt::DisplayRole).toString();
              painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, msg);
          }
          
          /// Target Power
          else if (index.column() == 3) {
          
          
          
              //// SOLUTION 1, NOTHING IS DRAW
              EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget();
              widgetTarget->targetStart->setText("Test");
              widgetTarget->render(painter, QPoint(), option.rect, QWidget::DrawChildren );
          

          // widgetTarget->paintEngine()

              /// SOLUTION 2 - NOT GOOD LOOKING (Color messed up)
              //        EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget();
              //        QPixmap pix = QPixmap::grabWidget(widgetTarget);
              //        painter->drawPixmap(option.rect, pix);
          
          
              /// SOLUTION 3, DRAW IT BY HAND - WORKS BUT HARD TO REPLICATE WIDGET WITH TEXT
              //        std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));
          
              //        int targetStepPower = interval->getPowerStepType();
              //        qDebug() << "TARGET STEP!" << targetStepPower;
              //        QString str_Step = Interval::getStepTypeFromInt( targetStepPower );
              //        double startFTP = interval->getFTP_start() * 100;
              //        double endFTP = interval->getFTP_end() * 100;
              //        int range = interval->getFTP_range();
          
              //        painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, str_Step);
          
          
          }
          
          
          /// Standard delegate display - Display Message
          else {
              QStyleOptionViewItem viewOption(option);
              viewOption.palette.setColor(QPalette::Text, QColor(Qt::white));
              QStyledItemDelegate::paint(painter, viewOption, index);
          }
          

          }@

          I will try to fix #1 and then use #3 as last resort.
          Thanks!


          Free Indoor Cycling Software - https://maximumtrainer.com

          1 Reply Last reply
          0
          • raven-worxR Offline
            raven-worxR Offline
            raven-worx
            Moderators
            wrote on last edited by
            #5

            try this:
            @
            painter->save();
            EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget();
            widgetTarget->resize( option.rect.size() );
            widgetTarget->targetStart->setText("Test");
            painter->translate(option.rect.topLeft());
            widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
            painter->restore();
            @

            And another suggestion:
            Don't create a new widget on every time your paint() method is called. No need to allocate new memory every time, beside you never delete this widget, thus it leaks.
            Rather hold a single instance in your delegate class and reuse it when needed.

            --- 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
            • M Offline
              M Offline
              maximus
              wrote on last edited by
              #6

              Thanks Raven it's working now!

              https://www.dropbox.com/s/no0edjn08ei66ap/workingPaint.png

              One last thing, I'll check more but if you know already, Inside this custom widget, I have QLabels but the main application stylesheet doesn't get applied to those label it seems (they should be white)
              Or i can just use .setStylesheet but I like all my presentation code to be in the same place.

              thanks again!


              Free Indoor Cycling Software - https://maximumtrainer.com

              1 Reply Last reply
              0
              • raven-worxR Offline
                raven-worxR Offline
                raven-worx
                Moderators
                wrote on last edited by
                #7

                set the stylesheet on the QApplication instance, so even parent-less widgets get the style, or

                set a parent-widget, so that i derives the stylesheet style from a widget which already has got it set in the hierarchy

                --- 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
                • M Offline
                  M Offline
                  maximus
                  wrote on last edited by
                  #8

                  Oh I totally forgot to give the widget a parent, there is the problem.
                  Thanks again!


                  Free Indoor Cycling Software - https://maximumtrainer.com

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    maximus
                    wrote on last edited by
                    #9

                    The display is working fine now, but my delegate has a memory leak in the paint function now.

                    I made a video showing the problem in action :
                    https://www.youtube.com/watch?v=b882e4cEREE&feature=youtu.be

                    I have noted that it is in the column that I used QWidget.render(). So in the code below, if I comment column 3,4 and 5, I don't have a memory leak.
                    Sorry I know I asked a lot of question already, hopefully I can give back to Qt..

                    Do you think that the line
                    "widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );"
                    could trigger an infinite loop? I'm failing to see where all that memory goes.. I should probably learn to use the debugger also -_-

                    @//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    void SpinBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

                    qDebug() << "paintNow...";
                    if (option.state & QStyle::State_Selected) {
                        painter->setPen(QPen( Qt::red, 5 ));
                        painter->drawRect(option.rect);
                        return;
                    }
                    
                    painter->setPen(QPen( Qt::white, 1 ));
                    
                    /// Type
                    if (index.column() == 0) {
                        int value = index.model()->data(index, Qt::DisplayRole).toInt();
                        QString intervalType = Interval::getTypeFromInt(value);
                    
                        painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, intervalType);
                    }
                    
                    /// Duration
                    else if (index.column() == 1) {
                        QTime time = index.model()->data(index, Qt::DisplayRole).toTime();
                        QString toDisplay = Util::showQTimeAsString(time);
                        painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, toDisplay );
                    }
                    
                    /// Display Message
                    else if (index.column() == 2) {
                        QString msg = index.model()->data(index, Qt::DisplayRole).toString();
                        painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter, msg);
                    }
                    
                    /// Target Power
                    else if (index.column() == 3) {
                    
                    
                        EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget(ptrParent, "POWER");
                        std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));
                    
                        int targetStepPower = interval->getPowerStepType();
                        double startFTP = interval->getFTP_start() * 100;
                        double endFTP = interval->getFTP_end() * 100;
                        int range = interval->getFTP_range();
                    
                        widgetTarget->stepComboBox->setCurrentIndex(targetStepPower);
                        widgetTarget->targetStartValue->setValue(startFTP);
                        widgetTarget->targetEndValue->setValue(endFTP);
                        widgetTarget->targetRangeValue->setValue(range);
                    
                        painter->save();
                        widgetTarget->resize( option.rect.size() );
                        painter->translate(option.rect.topLeft());
                        widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
                        painter->restore();
                    
                    }
                    
                    /// Target Cadence
                    else if (index.column() == 4) {
                    
                    
                        EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget(ptrParent, "CADENCE");
                        std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));
                    
                        int targetStepCadence = interval->getCadenceStepType();
                        int startCadence = interval->getCadence_start();
                        int endCadence = interval->getCadence_end();
                        int range = interval->getCadence_range();
                    
                        widgetTarget->stepComboBox->setCurrentIndex(targetStepCadence);
                        widgetTarget->targetStartValue->setValue(startCadence);
                        widgetTarget->targetEndValue->setValue(endCadence);
                        widgetTarget->targetRangeValue->setValue(range);
                    
                        painter->save();
                        widgetTarget->resize( option.rect.size() );
                        painter->translate(option.rect.topLeft());
                        widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
                        painter->restore();
                    
                    }
                    
                    /// Target Cadence
                    else if (index.column() == 5) {
                    
                    
                        EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget(ptrParent, "HR");
                        std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));
                    
                        int targetStepHR = interval->getHRStepType();
                        double startHR = interval->getHR_start() *100;
                        double endHR = interval->getHR_end() *100;
                        int range = interval->getHR_range();
                    
                        widgetTarget->stepComboBox->setCurrentIndex(targetStepHR);
                        widgetTarget->targetStartValue->setValue(startHR);
                        widgetTarget->targetEndValue->setValue(endHR);
                        widgetTarget->targetRangeValue->setValue(range);
                    
                        painter->save();
                        widgetTarget->resize( option.rect.size() );
                        painter->translate(option.rect.topLeft());
                        widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
                        painter->restore();
                    
                    }
                    
                    
                    /// Standard delegate display - Display Message
                    else {
                        QStyleOptionViewItem viewOption(option);
                        viewOption.palette.setColor(QPalette::Text, QColor(Qt::white));
                        QStyledItemDelegate::paint(painter, viewOption, index);
                    }
                    

                    }@


                    Free Indoor Cycling Software - https://maximumtrainer.com

                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      maximus
                      wrote on last edited by
                      #10

                      something wrong with the line
                      @widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );@
                      i comment this and don't get memory leak.. i'll post if I find a solution

                      [Edit: The function paint of my delegate is being called non-stop when the TableView is active.. still investigating why]


                      Free Indoor Cycling Software - https://maximumtrainer.com

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        maximus
                        wrote on last edited by
                        #11

                        Finally I used drawText as it is easier and more versatile, doesn't have the memory problem I had with QWidget.render()
                        needs more coding but at least what you code is what you get
                        Thanks for your help again! :)

                        @ else if (index.column() == 3 && paint) {

                            painter->save();
                            EditTargetPowerWidget *widgetTarget = new EditTargetPowerWidget(ptrParent, "POWER");
                            std::shared_ptr<Interval> interval(qvariant_cast<std::shared_ptr<Interval>>( index.model()->data(index, Qt::DisplayRole) ));
                        
                            int targetStepPower = interval->getPowerStepType();
                            double startFTP = interval->getFTP_start() * 100;
                            double endFTP = interval->getFTP_end() * 100;
                            int range = interval->getFTP_range();
                        
                            widgetTarget->stepComboBox->setCurrentIndex(targetStepPower);
                            widgetTarget->targetStartValue->setValue(startFTP);
                            widgetTarget->targetEndValue->setValue(endFTP);
                            widgetTarget->targetRangeValue->setValue(range);
                        

                        // widgetTarget->resize( option.rect.size() );
                        // painter->translate(option.rect.topLeft());
                        // widgetTarget->render(painter, QPoint(), QRegion(), QWidget::DrawWindowBackground );

                            QString targetStep_str = Interval::getStepTypeFromInt(targetStepPower);
                            QString target_str = "["+ QString::number(startFTP)  + " - " + QString::number(endFTP) + "]";
                            QString range_str = "±" + QString::number(range);
                            QString target1 = tr("Target: ");
                            QString range1 = tr("Range: ");
                            QString watts = tr(" Watts");
                            QString pFtp = tr(" % FTP");
                        
                        
                            painter->drawText(option.rect, Qt::AlignLeft | Qt::AlignVCenter,
                                              targetStep_str + "\n" +
                                              target1 +  target_str + pFtp + "\n" +
                                              range1 + range_str + watts);
                        
                        
                            painter->restore();
                        
                        
                        }@
                        

                        Free Indoor Cycling Software - https://maximumtrainer.com

                        1 Reply Last reply
                        0
                        • M Offline
                          M Offline
                          maximus
                          wrote on last edited by
                          #12

                          I modified my code to improve memory and find where is the problem. I think it is a memory leak with the createEditor() that doesn't get deleted after each use.

                          I would like to know if there is an example of this technique
                          "source":http://qt-project.org/doc/qt-5.0/qtwidgets/itemviews-spinboxdelegate.html
                          Furthermore it is also possible to reuse (and avoid deleting) the editor widget by reimplementing the destroyEditor() function

                          I tried but the createEditor and setEditorData are "const" function and I can't store any pointer there..
                          [EDIT: I removed the const in createEditor but now the function is now longer called, I wanted to create all my editor in the Constructor and reuse the same.. this technique doesn't work -_-]

                          I changed the way I change my model using a pointer in Qvariant rather than passing the whole object in a Qvariant, it helps but the memory used is still too high,

                          @//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                          void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {

                          /// Type
                          if (index.column() == 0) {
                              IntervalComboBox *comboBox = static_cast<IntervalComboBox*>(editor);
                              int value = comboBox->currentIndex();
                              model->setData(index, value, Qt::EditRole);
                          }
                          
                          /// Duration
                          else if (index.column() == 1) {
                              QTimeEdit *timeEdit = static_cast<QTimeEdit*>(editor);
                              QTime time1 = timeEdit->time();
                              model->setData(index, time1, Qt::EditRole);
                          }
                          
                          /// Display Message
                          else if (index.column() == 2) {
                              QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
                              QString msg = lineEdit->text();
                              model->setData(index, msg, Qt::EditRole);
                          }
                          
                          /// Target Power
                          else if (index.column() == 3) {
                              
                              EditTargetPowerWidget *targetWidget = static_cast<EditTargetPowerWidget*>(editor);
                              
                              int targetStepPower = targetWidget->stepComboBox->currentIndex();
                              Interval::StepType stepType = static_cast<Interval::StepType>( targetStepPower );
                              double startFTP = targetWidget->targetStartValue->value()/100;
                              double endFTP = targetWidget->targetEndValue->value()/100;
                              int range = targetWidget->targetRangeValue->value();
                              
                              
                              Interval *interval = (Interval*) index.model()->data(index, Qt::DisplayRole).value<void *>();
                              interval->setPowerData(stepType, startFTP, endFTP, range);
                              
                              /// MEMORY STILL INCREASE BY 2MB EACH EDIT, pointer *targetWidget not deleted?
                              
                              /// Break model/view architecture here...
                              ///        model->setData(index, variant, Qt::EditRole);
                          }
                          

                          @


                          Free Indoor Cycling Software - https://maximumtrainer.com

                          1 Reply Last reply
                          0
                          • M Offline
                            M Offline
                            maximus
                            wrote on last edited by
                            #13

                            Found the problem!

                            In the paint(), I was using a Widget and not deleting it after using it..

                            @ else if (index.column() == 3 && paint) {

                                painter->save();
                                EditTargetPowerWidget *targetWidget = new EditTargetPowerWidget(ptrParent, "POWER");
                            
                            
                                Interval *interval = (Interval*) index.model()->data(index, Qt::DisplayRole).value<void *>();
                            
                                int targetStepPower = interval->getPowerStepType();
                                double startFTP = interval->getFTP_start() * 100;
                                double endFTP = interval->getFTP_end() * 100;
                                int range = interval->getFTP_range();
                            
                                targetWidget->stepComboBox->setCurrentIndex(targetStepPower);
                                targetWidget->targetStartValue->setValue(startFTP);
                                targetWidget->targetEndValue->setValue(endFTP);
                                targetWidget->targetRangeValue->setValue(range);
                            
                            
                                targetWidget->resize( option.rect.size() );
                                painter->translate(option.rect.topLeft());
                                targetWidget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
                            
                            
                                painter->restore();
                                delete targetWidget;
                            
                            
                            }@
                            

                            Free Indoor Cycling Software - https://maximumtrainer.com

                            1 Reply Last reply
                            0
                            • M Offline
                              M Offline
                              maximus
                              wrote on last edited by
                              #14

                              Found the problem!

                              I forgot to delete a pointer of my QWidget in the paint() function. this was increasing the memory usage each time.

                              @
                              else if (index.column() == 3 && paint) {

                                  painter->save();
                                  EditTargetPowerWidget *targetWidget = new EditTargetPowerWidget(ptrParent, "POWER");
                              
                                  Interval *interval = (Interval*) index.model()->data(index, Qt::DisplayRole).value<void *>();
                              
                                  int targetStepPower = interval->getPowerStepType();
                                  double startFTP = interval->getFTP_start() * 100;
                                  double endFTP = interval->getFTP_end() * 100;
                                  int range = interval->getFTP_range();
                              
                                  targetWidget->stepComboBox->setCurrentIndex(targetStepPower);
                                  targetWidget->targetStartValue->setValue(startFTP);
                                  targetWidget->targetEndValue->setValue(endFTP);
                                  targetWidget->targetRangeValue->setValue(range);
                              
                                  targetWidget->resize( option.rect.size() );
                                  painter->translate(option.rect.topLeft());
                                  targetWidget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
                              
                              
                                  painter->restore();
                                  delete targetWidget;
                              
                              }@
                              

                              Free Indoor Cycling Software - https://maximumtrainer.com

                              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