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.9k 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.
  • 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