How to repaint QWidget encapsulated in QDeclarativeItem in QML?
-
@Chillax Usually
paintmethod is used to draw something. Theupdatewill force the Item to re-paint by calling thepaintmethod.@p3c0 The problem is that I am new to QtQuick and I probably misunderstand something essential. This is how I think things work:
- The
customPlot->replot();updates the underlying QWidget - The
QGraphicsProxyWidgetdoes the painting of theQDeclarativeItemeverytimeupdateis called. But if I call thepaintmethod instead ofupdateI get a compile error, becauseQDeclarativeItem::paint()is virtual
- The
-
@p3c0 The problem is that I am new to QtQuick and I probably misunderstand something essential. This is how I think things work:
- The
customPlot->replot();updates the underlying QWidget - The
QGraphicsProxyWidgetdoes the painting of theQDeclarativeItemeverytimeupdateis called. But if I call thepaintmethod instead ofupdateI get a compile error, becauseQDeclarativeItem::paint()is virtual
But if I call the paint method instead of update I get a compile error, because QDeclarativeItem::paint() is virtual
Yeah it is not supposed to be called by user. It is called when a re-paint is requested using update. But since
QGraphicsProxyWidgetis in picture here the re-implementingpaintis not required.Have you tried to call
addFlowfrom the QML ? This will ensure that theaddFlowis called when theFlowGrafikItem is ready. - The
-
But if I call the paint method instead of update I get a compile error, because QDeclarativeItem::paint() is virtual
Yeah it is not supposed to be called by user. It is called when a re-paint is requested using update. But since
QGraphicsProxyWidgetis in picture here the re-implementingpaintis not required.Have you tried to call
addFlowfrom the QML ? This will ensure that theaddFlowis called when theFlowGrafikItem is ready.@p3c0 I created a timer in my QML item, which calls this function periodically:
void FlowGrafik::refresh() { qDebug() << "refreshed"; customPlot->replot(); customPlot->repaint(); this->update(); }addFlowstill gets called from the C++ code periodically and changes the plot values.The weird thing is that whatever I write in the constructor of
FlowGrafikgets displayed perfectly. I even tried to delete all internal objects ofFlowGrafikin theaddFlowfunction, likeQGraphicsProxyWidgetandcustomPlot, and I created new ones, but the results are the same. Only the code I write in the constructor gets displayed, and my QML Item never gets updated.Could the root of the problem be the fact that I load the QML code from a
QmlApplicationViewer?main.cpp:
QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml"));FlowView.qml:
import QtQuick 1.1 import FlowGrafik 1.0 Rectangle { id: flowGrafik objectName: "FlowGrafikRect" x: 0 y: 0 width: 200 height: 200 radius: 10 Timer { interval: 500; running: true; repeat: true onTriggered: flowGrafikItem.refresh() } FlowGrafik { id: flowGrafikItem objectName: "FlowGrafik" } } -
@p3c0 I created a timer in my QML item, which calls this function periodically:
void FlowGrafik::refresh() { qDebug() << "refreshed"; customPlot->replot(); customPlot->repaint(); this->update(); }addFlowstill gets called from the C++ code periodically and changes the plot values.The weird thing is that whatever I write in the constructor of
FlowGrafikgets displayed perfectly. I even tried to delete all internal objects ofFlowGrafikin theaddFlowfunction, likeQGraphicsProxyWidgetandcustomPlot, and I created new ones, but the results are the same. Only the code I write in the constructor gets displayed, and my QML Item never gets updated.Could the root of the problem be the fact that I load the QML code from a
QmlApplicationViewer?main.cpp:
QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml"));FlowView.qml:
import QtQuick 1.1 import FlowGrafik 1.0 Rectangle { id: flowGrafik objectName: "FlowGrafikRect" x: 0 y: 0 width: 200 height: 200 radius: 10 Timer { interval: 500; running: true; repeat: true onTriggered: flowGrafikItem.refresh() } FlowGrafik { id: flowGrafikItem objectName: "FlowGrafik" } } -
@p3c0 I created a timer in my QML item, which calls this function periodically:
void FlowGrafik::refresh() { qDebug() << "refreshed"; customPlot->replot(); customPlot->repaint(); this->update(); }addFlowstill gets called from the C++ code periodically and changes the plot values.The weird thing is that whatever I write in the constructor of
FlowGrafikgets displayed perfectly. I even tried to delete all internal objects ofFlowGrafikin theaddFlowfunction, likeQGraphicsProxyWidgetandcustomPlot, and I created new ones, but the results are the same. Only the code I write in the constructor gets displayed, and my QML Item never gets updated.Could the root of the problem be the fact that I load the QML code from a
QmlApplicationViewer?main.cpp:
QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml"));FlowView.qml:
import QtQuick 1.1 import FlowGrafik 1.0 Rectangle { id: flowGrafik objectName: "FlowGrafikRect" x: 0 y: 0 width: 200 height: 200 radius: 10 Timer { interval: 500; running: true; repeat: true onTriggered: flowGrafikItem.refresh() } FlowGrafik { id: flowGrafikItem objectName: "FlowGrafik" } }@Chillax After having a quick look at the
QCustomPlot's API, I foundQCustomPlothas toPainter method which accepts a QCPPainter as an argument. ThisQCPPainterin turn accepts aQPaintDeviceargument which can be aQImageorQPixmapwhich means theQCustomPlotcan be rendered onto an image or pixmap.
So after rendering thisQCustomPlot's data into an image/pixmap you can periodically call update and re-paint this image/pixmap inside the re-implementedpaintmethod and which will definitely updated on the QML side. -
@Chillax After having a quick look at the
QCustomPlot's API, I foundQCustomPlothas toPainter method which accepts a QCPPainter as an argument. ThisQCPPainterin turn accepts aQPaintDeviceargument which can be aQImageorQPixmapwhich means theQCustomPlotcan be rendered onto an image or pixmap.
So after rendering thisQCustomPlot's data into an image/pixmap you can periodically call update and re-paint this image/pixmap inside the re-implementedpaintmethod and which will definitely updated on the QML side.@p3c0 Thank you so much for the tip! We are getting really close!
So I reimplemented thepaintmethod of my customQDeclarativeItem,FlowGrafik, like this:void FlowGrafik::paint(QPainter* painter) { if (customPlot) { qDebug() << "paint"; QPixmap picture(200,200); QCPPainter qcpPainter(&picture); customPlot->replot(); customPlot->toPainter(&qcpPainter); painter->drawPixmap(QPoint(), picture); } }Unfortunately the results are the same, but I found out that the
paintmethod never gets called! (I didn't see the "paint" at the application output). I tried calling it in therefresh()function like this:this->paint(new QPainter);, and then I saw the "paint" at the application output, but the graph still did not replot. Any ideas on how to move on? -
@p3c0 Thank you so much for the tip! We are getting really close!
So I reimplemented thepaintmethod of my customQDeclarativeItem,FlowGrafik, like this:void FlowGrafik::paint(QPainter* painter) { if (customPlot) { qDebug() << "paint"; QPixmap picture(200,200); QCPPainter qcpPainter(&picture); customPlot->replot(); customPlot->toPainter(&qcpPainter); painter->drawPixmap(QPoint(), picture); } }Unfortunately the results are the same, but I found out that the
paintmethod never gets called! (I didn't see the "paint" at the application output). I tried calling it in therefresh()function like this:this->paint(new QPainter);, and then I saw the "paint" at the application output, but the graph still did not replot. Any ideas on how to move on? -
@Chillax As said earlier to call
paintyou need to callupdate. So what you can do is call a C++ function from QML periodically which will update the plot values and then callupdate.@p3c0 Yes, I call
updateperiodically, but that does not callpaintvoid FlowGrafik::refresh() { qDebug() << "refresh"; customPlot->replot(); customPlot->repaint(); this->update(); }I see "refresh" at the application output, but I don't see "paint". The question is why doesn't
updatecallpaint? -
@p3c0 Yes, I call
updateperiodically, but that does not callpaintvoid FlowGrafik::refresh() { qDebug() << "refresh"; customPlot->replot(); customPlot->repaint(); this->update(); }I see "refresh" at the application output, but I don't see "paint". The question is why doesn't
updatecallpaint?@Chillax It should actually.
http://doc.qt.io/qt-4.8/qgraphicsitem.html#paintingDepending on whether or not the item is visible in a view, the item may or may not be repainted
Well I guess the Item is visible.
-
@Chillax It should actually.
http://doc.qt.io/qt-4.8/qgraphicsitem.html#paintingDepending on whether or not the item is visible in a view, the item may or may not be repainted
Well I guess the Item is visible.
@p3c0 FML :D The item is indeed visible. But it didn't help to hide it before the update and then show it again.
I saved the pixmap to a jpg file, and it looks just as I expect it to look like. So the problem is that the QML won't repaint the
QDeclaretiveItem, because it is visible... Can you think of a better solution for displaying myQCustomPlot Qwidget? I am pretty sure this is possibe in QML, and it shouldn't be this hard. -
@p3c0 FML :D The item is indeed visible. But it didn't help to hide it before the update and then show it again.
I saved the pixmap to a jpg file, and it looks just as I expect it to look like. So the problem is that the QML won't repaint the
QDeclaretiveItem, because it is visible... Can you think of a better solution for displaying myQCustomPlot Qwidget? I am pretty sure this is possibe in QML, and it shouldn't be this hard.So the problem is that the QML won't repaint the QDeclaretiveItem, because it is visible...
No. It's useless painting invisible objects.
Can you try following ?
-
On the QML side specify
widthandheightforFlowGrafikitem. May be this could be the reason the item is not painted i.e havingwidthandheightas0is close to having an invisible item. -
Create a very minimal project with
QDeclarativeItemand without usingQCustomPlotand keeping rest the same. This is to make sure ifpaintmethod is invoked atleast in this case. I dont have Qt 4.8 at hand so I can't test. Have moved to Qt5 long time back :)
-
-
So the problem is that the QML won't repaint the QDeclaretiveItem, because it is visible...
No. It's useless painting invisible objects.
Can you try following ?
-
On the QML side specify
widthandheightforFlowGrafikitem. May be this could be the reason the item is not painted i.e havingwidthandheightas0is close to having an invisible item. -
Create a very minimal project with
QDeclarativeItemand without usingQCustomPlotand keeping rest the same. This is to make sure ifpaintmethod is invoked atleast in this case. I dont have Qt 4.8 at hand so I can't test. Have moved to Qt5 long time back :)
@p3c0 Unfortunately specifying with and height did not solve the issue.
I created a minimal project (with QtQuick Application template). It is using QCustomPlot and everything works fine. But this is a Desktop version, and if I am not mistaken it is using Qt 5.3, but I need it to work on my embedded linux Qt 4.8 version.
The embedded linux version usesqmlapplicationviewerto load QML files with this comment on top:# This file was generated by the Qt Quick Application wizard of Qt Creator. # The code below adds the QmlApplicationViewer to the project and handles the # activation of QML debugging.the desktop version uses
qtquickapplicationviewer:# This file was generated by the Qt Quick 1 Application wizard of Qt Creator. # The code below adds the QtQuick1ApplicationViewer to the project.I'm not sure if the applicationviewer or the qt version difference causes my problem.
-
-
@p3c0 Unfortunately specifying with and height did not solve the issue.
I created a minimal project (with QtQuick Application template). It is using QCustomPlot and everything works fine. But this is a Desktop version, and if I am not mistaken it is using Qt 5.3, but I need it to work on my embedded linux Qt 4.8 version.
The embedded linux version usesqmlapplicationviewerto load QML files with this comment on top:# This file was generated by the Qt Quick Application wizard of Qt Creator. # The code below adds the QmlApplicationViewer to the project and handles the # activation of QML debugging.the desktop version uses
qtquickapplicationviewer:# This file was generated by the Qt Quick 1 Application wizard of Qt Creator. # The code below adds the QtQuick1ApplicationViewer to the project.I'm not sure if the applicationviewer or the qt version difference causes my problem.
@Chillax
qtquickapplicationviewerwas a helper class added back then which calls other in-built functions to load QML files. It also provided some extra functions for ease. You can also directly useQQuickVieworQQmlApplicationEngineto load the QML files depending upon the root object. This is all Qt 5.x related.
Similarlyqmlapplicationvieweris an helper class. If you look into its source you can see it is actually subclassed fromQDeclarativeViewwhich actually loads and displays the QML files.I'm not sure if the applicationviewer or the qt version difference causes my problem.
AFAIK definitely not
applicationviewerbut may be Qt version.Also did you try running same application with Qt 4.8 on desktop ? I think you should try that too to rule out the system problem if any.
-
@Chillax
qtquickapplicationviewerwas a helper class added back then which calls other in-built functions to load QML files. It also provided some extra functions for ease. You can also directly useQQuickVieworQQmlApplicationEngineto load the QML files depending upon the root object. This is all Qt 5.x related.
Similarlyqmlapplicationvieweris an helper class. If you look into its source you can see it is actually subclassed fromQDeclarativeViewwhich actually loads and displays the QML files.I'm not sure if the applicationviewer or the qt version difference causes my problem.
AFAIK definitely not
applicationviewerbut may be Qt version.Also did you try running same application with Qt 4.8 on desktop ? I think you should try that too to rule out the system problem if any.
@p3c0 So I spent the last couple of hours trying to find out how to install Qt version 4.8 on Ubuntu, but it turns out that only the versions newer than 5.0 support linux. So I installed Qt Creator and Qt version 4.8 on Windows and it works there as well :)
-
@Chillax
qtquickapplicationviewerwas a helper class added back then which calls other in-built functions to load QML files. It also provided some extra functions for ease. You can also directly useQQuickVieworQQmlApplicationEngineto load the QML files depending upon the root object. This is all Qt 5.x related.
Similarlyqmlapplicationvieweris an helper class. If you look into its source you can see it is actually subclassed fromQDeclarativeViewwhich actually loads and displays the QML files.I'm not sure if the applicationviewer or the qt version difference causes my problem.
AFAIK definitely not
applicationviewerbut may be Qt version.Also did you try running same application with Qt 4.8 on desktop ? I think you should try that too to rule out the system problem if any.
@p3c0 Interesting news: Until now, my
QmlApplicationViewer flowViewwas just a new window on top of the GUI I was using until now. But if I show onlyflowViewin fullscreen and nothing else, it works (updates/repaints).qmlRegisterType<FlowGrafik>("FlowGrafik",1,0,"FlowGrafik"); QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml")); flowView.showFullScreen(); -
@p3c0 Interesting news: Until now, my
QmlApplicationViewer flowViewwas just a new window on top of the GUI I was using until now. But if I show onlyflowViewin fullscreen and nothing else, it works (updates/repaints).qmlRegisterType<FlowGrafik>("FlowGrafik",1,0,"FlowGrafik"); QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml")); flowView.showFullScreen();@p3c0 What's more interesting: If I call
addFlowfrom QML, it works (replots). But if I call it from C++, it updates theQWidget, but not the QML Item. Even if I callrefresh()from QML right after it, which should replot and repaint the QML Item too. Any explanation to this? Why is there a difference between changing the QWidget in the C++ code and asking for a replot from QML and doing everything in QML? As if the QML Item and the C++ QDeclarativeItem were two independent objects.So now my plan is to give the flow value from C++ to the
FlowGrafikQML item, and then calladdFlowfrom QML. That should work. -
@p3c0 What's more interesting: If I call
addFlowfrom QML, it works (replots). But if I call it from C++, it updates theQWidget, but not the QML Item. Even if I callrefresh()from QML right after it, which should replot and repaint the QML Item too. Any explanation to this? Why is there a difference between changing the QWidget in the C++ code and asking for a replot from QML and doing everything in QML? As if the QML Item and the C++ QDeclarativeItem were two independent objects.So now my plan is to give the flow value from C++ to the
FlowGrafikQML item, and then calladdFlowfrom QML. That should work.@Chillax Unfortunately I too dont understand this behavior. The fact that it works in desktop environment and not on embedded only points that there should be some bug in that environment.
So now my plan is to give the flow value from C++ to the FlowGrafik QML item, and then call addFlow from QML.
Try. I had suggested the same earlier :)
Let us know if it works or if possible the exact reason so that it may help others too. -
@Chillax Unfortunately I too dont understand this behavior. The fact that it works in desktop environment and not on embedded only points that there should be some bug in that environment.
So now my plan is to give the flow value from C++ to the FlowGrafik QML item, and then call addFlow from QML.
Try. I had suggested the same earlier :)
Let us know if it works or if possible the exact reason so that it may help others too.@p3c0 So thanks a lot for the help! I would have given up without you :) The problem was that I created two instances of
FlowGrafik. One in the QML and one in C++. I changed the C++ one and expected the QML one to refresh.Now I created a pointer in the C++ code that points to the Item in the QML.
QmlApplicationViewer flowView; flowView.setSource(QUrl("qrc:///qml/qml/FlowView.qml")); QObject * flowViewObject = flowView.rootObject(); FlowGrafik * flowGrafik = flowViewObject->findChild<FlowGrafik *>(QString("FlowGrafik"));But now if I want to use this pointer (like
flowGrafik->addFlow(...)) I get a segmentation fault. Do you know why?