Solved Performance of QGraphicView / QGraphicScene
-
Hello Qt Community,
I am working with Qt for three months now and I am developing a runtime plotting solution on my own. Everything is working fine except of the performance. Does anyone have some experiences with QGraphicsView and runtime plotting?
The fastest way I was able to code was:Samplevector.append(device>getSample()); if(Samplevector.size()>800) { scene->removeItem(graphlinevector.first()); graphlinevector.removeFirst(); Samplevector.removeFirst(); } if(Samplevector.size()>1) { item = scene-> addLine(QLineF( Samplevector.at(Samplevector.size()-2).getX(), Samplevector.at(Samplevector.size()-2).getY(), Samplevector.at(Samplevector.size()-1).getX(), Samplevector.at(Samplevector.size()-1).getY() )); graphlinevector.append(item); }
QRect a; if(Samplevector.size()!=0) { a.setRect(Samplevector.at(Samplevector.size()-1).getX()-850,0,900,200); ui->LinegraphView->setSceneRect(a); }
It does not suit that much to my project, because I Would need to draw the Coordination system again and again.
With a framerate of 20 pictures per Second and a samplerate of 100 Hz from the Device:I achieve with 800 Lines in my view a Samplerate of 36 Samples per Second...
With 400 Lines a samplerate of 80 Samples per SecondSo is there a better / faster / more resource friendly way to plot a Graph?
Kind regards
Konrad -
Hi,
maybe it helps to draw unchangeable parts as background image of the view. You would have to overwrite the virtual functionQGraphicsView::drawBackground
-Michael. -
You mean to draw the Coordinationsystem as unchangeable?
Because the Graph should be fluent, as a oscilloscope its easy, but it should show me a Trend of the last seconds. -
Yes. Whether it helps or not depends on how much trouble (lines + texts) the coordinate system consists of. You can check by drawing an empty image in the background and removing the coordinate system for the test. If that is faster you can change it with the real thing.
But even so, whether as a whole it's fast enough for you, is still another question. You may need to draw everything using GPU accelerated OpenGL.
-Michael.
-
@m.sue
Thanks Michael, I guess it is the last possible way to do it. Its weird because QCustomPlot is able to do exactly what I want, but they are proficients :D In the end I definetly need minimum 30 Hz as my samplerate. I hope its possible. I also think that I have something in my code which is using too much performance beside the drawings.
I will update this Thread as soon as I have a reliable solution. -
A few points to explore:
- Do you know where your bottleneck is? Rather item insertion or painting?
- These are costly functions: scene->removeItem and scene-> addLine You can try to avoid the costs by hiding and re-using items, instead of destroying and creating them
- Instead of adding thousands of single lines, consider creating and updating a single polyline, if that makes sense for your application
- Keep in mind that QGraphicsView normally does not benefit from graphics hardware acceleration, and runs purely in the main thread.
- If there are parts in your scene that remain the same from one frame to the next, you might consider putting them into a custom QGraphicsItem, then setting the cache mode to DeviceCoordinateCaching. Even moving these items around on the scene should be much quicker than re-creating them on each frame. That way, you could e.g. divide your scene in vertical slices, and on each frame, you discard the leftmost slice and create a new rightmost slice, moving all the other slices from right to left. Whether that works for you depends on your exact use case
-
I suppose this is a multithreaded approach?
If not, start each updating of the view in an own thread (set mutex or dont allow multiple of those updating threads).
This couldimprove the experienced reaction time / performance -
@John_A._Myer said in Performance of QGraphicView / QGraphicScene:
I suppose this is a multithreaded approach?
If not, start each updating of the view in an own thread (set mutex or dont allow multiple of those updating threads).
This couldimprove the experienced reaction time / performanceMost GUI classes have to run in the main thread. If there are any CPU-heavy calculation, it might make sense to put them in separate threads. However, if the bottleneck concerns calls to QGraphicsView-related classes, it's probably not possible to do so.
-
-
I guess its both after my update today, I normaly have a triangle Chart, now I have a triangle chart with steps :D but just in runtime, the data is looking almost good. I am thinking to use a Thread for Datasaving from the Sensor, I definetly dont want to loose a sample.
-
I know that these are my dangerous parts. Right now I am adding / setting Lines every 10ms. But I dont need to remove the items instead. Just updating their x and y pos is not possible because of the fact that they are lines with different slopes. In the way I introduced the thread its super economical. Just one add and one remove, thats definetly super fast. From my first view after the Threadupdate, over 10 min still 100Hz.
You are right with the Idea to hide the last item in the graphlinevector. That should be better, and to remove the last item of the samplevector should be unneccesary as well? -
Never heard of the polyline, I need to read more about it. On the first look, it looks like I can add a QMap or QList or QVector and it is building a fluent line by itself? I just need a line which is drawing QPoint 1->QPoint 2->QPoint3 etc...
-
Your last point is great! just great, I need to try that. I have never used the cache mode. Can you help me with an starting point, what I should search or read to accomplish this task.
I am really have nightmares to be able to resize this Chart dynamicaly ://
THANKS A LOT!
I found one problem, I guess its my major problem. I had a Simulation class for my Datainput. (The sensor is not ready now)
This Simulationclass was waiting for the data to draw, so I put this class into a Thread so I have a samplerate from 100 Hz right now. The Chart in my main Programm is looking horrible, because its just showing some of the incoming samples, but its realtime :DUsing the attemp I introduced here works perfectly, so I guess I am taking it. I will try another attemp as well. The eye is just able to update in 20-30 Hz. so I collecting samples for this amount of time and draw them all every 20-30 Hz. I would prefer saving them directly as a QLineItem, in a QVector. I had some problems with it, so I saved it as a normal QLineF and put it in the QLineItem Vector later. I dont know why :/
@John_A._Myer
It was also my first step, I will figure out, how this will increase the saving and preparing for the scene step of the programm. -
-
I develop a similar curve display. While it does not display real-time data, it contains a lot of curves and curve points, and scrolls fluently.
Here is how I do it (maybe that's a starting point for you):
1 I have a custom QGraphicsItem in the background that paints the grid. If the grid stays the same from frame to frame (as I imagine it would in an oscilloscope application), simply call the following in the constructor:setCacheMode(QGraphicsItem::DeviceCoordinateCache);
This means the output of the 'paint' method will be cached pixel by pixel. As long as there are no changes to the transformation, and no explicit call to update() on the item, your paint() method is (normally) not called again.
- Then I have a custom QGraphicsItem for each line plot. Simply put, the plot contains a QPolygonF (which is basically a glorified QList<QPointF>), with the points already mapped the way I want to paint them. I append new points to the list, and occasionally clean out points that fell off the left edge of the display.
In my paint() routine, I just set a pen (warning: Don't use cosmetic pens of width >0! Bad performance! Either use non-cosmetic pens or cosmetic pens of width 0), then I optionally set another transformation to the painter, and then I go
painter.drawPolyline(m_CachedPolyline);
Note: I do NOT use the cacheMode on the line plots. That would actually degrade performance, because they change too often.
I would try that, and see where it brings you performance-wise. Anything more elaborate (like slicing the curves into multiple sections which use caching) - I would keep that when you see the simple drawPolyline approach is not sufficient.
- Then I have a custom QGraphicsItem for each line plot. Simply put, the plot contains a QPolygonF (which is basically a glorified QList<QPointF>), with the points already mapped the way I want to paint them. I append new points to the list, and occasionally clean out points that fell off the left edge of the display.
-
Thanks for your idea.
I gonna test that today.I just want to update this thread for everyone else:
My idea above with just drawing one point is NOT the fastest way.
My astonishing results of a long term measurement. around 10 min. My data should be valid for aminimum of 35 min result in a really really bad performance of this idea. I really still dont get the syntax behind, but clearing the scene and drawing every line every 50 ms again is way, way,way faster!! I am really surprised. So clearing the scene and adding 800 Lines every 50 ms is faster and more stable than adding and removing one line every 10 ms and setting the viewrectangle every 50 ms. I have some minimal problems at the minima and maxima, its not always completly right, but it looks like just one sample get lost. The other method lost minimum. 20 samples every period.33 min of testing and still the same good result. I guess I found a solution :)
Thanks @Asperamanca
- I tried the painter already but I was just able to paint something in the constructor. Its a few months ago, when I was new to qt :D so I decided to use graphics view and scene.
Using an oscilloscope application is quite easy with qt. Doing it the way like a snake is harder :) - The painter.drawpolyline way sounds faster to me, If I am struggeling again, I will use this way.
- I tried the painter already but I was just able to paint something in the constructor. Its a few months ago, when I was new to qt :D so I decided to use graphics view and scene.
-
!!Use QPainterPath!!
The Solution is not to add Lines one by one.
Use QPainterPath and just add it once and add these lines as points to the QPainterPath before.
@Asperamanca i recognized your idea and I started a new search. I found it and it is obvious now, where the problem was.Thanks everybody!