Painting Widget with QPainter
-
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. -
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!
-
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
-
Oh I totally forgot to give the widget a parent, there is the problem.
Thanks again! -
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.beI 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); }
}@
-
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]
-
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(); }@
-
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() functionI 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); }
@
-
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; }@
-
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; }@