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