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
Forum Updated to NodeBB v4.3 + New Features

Painting Widget with QPainter

Scheduled Pinned Locked Moved General and Desktop
14 Posts 3 Posters 16.7k Views 1 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.
  • 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