Qt 2D Drawing?
-
Hello Qt community,
I would like to use Qt for 2D drawing, but I'm not sure which tutorials I should use. (They all look so, so verbose.)
Basically, my project consists in drawing ~200 sprites at every frame. I used to do that with the SFML library, so for those of you who are familiar with this library, I have no idea if Qt works similarly or quite differently.
-
Which class is equivalent to SFML's
sf::Sprite
? I can't think of anything better than drawing aQimage
for the time being. -
Should I draw all my
QImages
on aQMainWindow
at every frame (SFML-style), or should I use aQPäinter
to paint all myQImages
on anotherQImage
, then draw the resultingQImage
on aQMainWindow
?
Thanks in advance for your help.
-
-
This all depends that You need, is there any kind of interaction with those sprites etc.
Few approaches can be choses:- QImage as drawing buffer - use QPainter( &QImage) and draw in any function then i.e. call update (if base is QWidget) and in QWidget::paineEvent() draw that image (convert to pixmap first to gain some speed)
- Draw directly in paintEvent
- use QGraphiscScene approach - scene support several predefined base object types, like pixmaps (images) via QGraphicsPixmapItem, rectangle, paths, etc.
All of above solution can also be hw accelerated i.e. by applying OGL widget to the Scene, using shaders and bind drawing buffer image,
But basically drawing 200sprites at same time is not good approach. Use either scene (see graphics view framework http://doc.qt.io/qt-5/graphicsview.html) or write You own method that will update only portion of image buffer (that what has changed) and display it.
Chip example with 40k sprites: http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-example.html
Another approach would be to go with QtQuick - with basically is GraphicsScene + opengl + java script like object declaration.
-
Are you saying that drawing 200 QImages directly on the window (2), and painting them all on a QImage through a QPainter, then drawing the result on the window (1), take approximately the same amount of time?
Basically I would need to be able to scale the QImages I draw (horizontally, vertically) and rotate them. What's the most convenient drawing method for this?
Also, what's the most convenient method for drawing straight lines?
Edit: after looking through the descriptions of QPainter and QPixmap, I think I'm going to use the 1st method you listed, with these classes.
-
@LuGRU said:
- QImage as drawing buffer - use QPainter( &QImage) and draw in any function then i.e. call update (if base is QWidget) and in QWidget::paineEvent() draw that image (convert to pixmap first to gain some speed)
- Draw directly in paintEvent
- use QGraphiscScene approach - scene support several predefined base object types, like pixmaps (images) via QGraphicsPixmapItem, rectangle, paths, etc.
Option #3 is specifically designed to support drawing many 2D sprites. It also has built-in support for linear transformations (scaling, rotation. etc.)
-
@Pippin said:
Are you saying that drawing 200 QImages directly on the window (2), and painting them all on a QImage through a QPainter, then drawing the result on the window (1), take approximately the same amount of time?
NO it will take longer, but my point here is that:
create, only once, buffer that holds 200 drawn images - i.e. with all transformations, scalings, etc. Then draw only that buffer image on each paint event, so time looks like this:
paintEvent -> initial iteration - draw 200 image onto buffer - draw buffer
paintEvent -> n'th iteration - draw only buffer image
paintEvent -> n'th + 1 iteration - draw only buffer image
...Plus You can offload those drawings into separate threads. If those 200 iamges don't use transformation, then prepare buffer image of size that will holds all those images, then start thread and in thread load and then copy image data into buffer image.
Basically I would need to be able to scale the QImages I draw (horizontally, vertically) and rotate them. What's the most convenient drawing method for this?
Also, what's the most convenient method for drawing straight lines?
Edit: after looking through the descriptions of QPainter and QPixmap, I think I'm going to use the 1st method you listed, with these classes.
QGraphics Framework is for this. See this http://doc.qt.io/qt-4.8/graphicsview.html and 40k chip example: http://doc.qt.io/qt-5/qtwidgets-graphicsview-chip-example.html
-
Are you sure, because if you browse QGraphicsScene's page, CTRL+F "QPixmap" doesn't have that many results, also CTRL+F "rotat" has no results :S
Basically, if you have an std::vector of 10 QPixmaps, an std::vector of 10 angles, an std::vector of 10 horizontal scales, and an std::vector of 10 positions, how would you draw these 10 QPixmaps on the QGraphicsScene, according to the other std::vectors I listed?
-
I see a lot of complicated sounding stuff being talked about here.
To what extent can QML/QSG meet your needs? OpenGL-powered QSG provides all the rendering power you need; if a pure QML-approach ran out of steam for you, I suspect it'd be because the javascript for whatever sprite behaviour is needed becomes too complex and slow, not the rendering. But more complicated behaviour could be moved into C++ without leaving QML & QSG behind, using the usual QML-C++ interop (properties, invokables etc).
Complete qmlscene runnable example below (placeholder icons courtesy of lorempixel):
import QtQuick 2.2 // Silly QML sprites demo; 200 randomly positioned/moving sprites // Sprites toggle color when clicked Rectangle { id: main width: 1024 height: 768 color: 'black' Timer { id: ticker interval: 1000.0/60.0 running: true repeat: true } Repeater { // Number of sprites // Can go up to 1000 happily enough on an old Core2Duo lappy (Intel graphics) // More modern HW probably much more. model: 200 Item { id: sprite x: ox-0.5*width y: oy-0.5*width width: 64 height: 64 rotation: Math.random()*360.0 property real ox: main.width*Math.random() property real oy: main.height*Math.random() property real vx: -1.0+2.0*Math.random() property real vy: -1.0+2.0*Math.random() property real vr: -1.0+2.0*Math.random() Image { anchors.fill: parent opacity: 0.75 source: 'http://www.lorempixum.com/64/64/cats/'+((1+index%10).toString()) smooth: true } Rectangle { id: highlight anchors.fill: parent visible: opacity!=0.0 opacity: 0.0 color: 'red' Behavior on opacity {NumberAnimation {duration: 500}} } MouseArea { anchors.fill: parent onClicked: highlight.opacity=1.0-highlight.opacity } Connections { target: ticker onTriggered: { ox+=vx if (ox<0.0) {ox=0.0;vx=-vx;} else if (ox>main.width) {ox=main.width;vx=-vx;} oy+=vy if (oy<0.0) {oy=0.0;vy=-vy;} else if (oy>main.height) {oy=main.height;vy=-vy;} rotation+=vr } } } } }
-
@Pippin said:
Are you sure, because if you browse QGraphicsScene's page, CTRL+F "QPixmap" doesn't have that many results, also CTRL+F "rotat" has no results :S
Yes, I'm sure.
You don't rotate the QGraphicsScene itself. Instead, you rotate the individual items.
Basically, if you have an std::vector of 10 QPixmaps, an std::vector of 10 angles, an std::vector of 10 horizontal scales, and an std::vector of 10 positions, how would you draw these 10 QPixmaps on the QGraphicsScene, according to the other std::vectors I listed?
- Wrap each QPixmap inside a QGraphicsPixmapItem (this is a subclass of QGraphicsItem)
- Add your QGraphicsPixmapItems to your QGraphicsScene
- Rotate your items using QGraphicsItem::rotate()
- Move your items using QGraphicsItem::setPos()
- Show your QGraphicsScene using a QGraphicsView
EDIT: Note that there are many ways to do what you want in Qt. @LuGRU showed you 3, and @timday showed you another. The QGraphicsScene option requires the least amount of coding, but might not offer the best performance. I suggest you try it first, to familiarize yourself with Qt. If its performance is not satisfactory, then try the other options. Good luck.
-
To sum up my post I will share info about performance of Graphics Framework in Qt on project that I'm involved with.
Running 25 video items - custom libVlc base QGraphicsItems - that plays videos of 624x352 at 23.976fp hits 50% CPU on i7-4770 in debug mode, all items playback is smooth.
For around 230 items, around 1/5 is text with high aliasing rest is, pixmaps, QGraphicsPathItem - panning and zooming view when all those objects are visible - all objects do have transparency - is not smooth, I would say 15fps, going to OGL - smooth.So drawing 200 sprites shouldn't be a big of a problem.
Maximizing performance:
QtQuick 1 - deprecated - used OGL direct mode - each time frame was repainted scene was uploaded to GPU
QtQuick 2 - currently used - uses VBO / IBO - vertex / image buffer object's - sending to GPU new data only when data do change - decrease bandwidth usage increase performance.
QGraphicsView + QGLWidget - I'm not sure but suspecting direct OGL mode, this of course can be customized to use VBO / IBO for item data -
@timday Thank you for your reply but I'm afraid I don't know most of the things you're talking about (QML, QSG...) :S I also don't know in what language the code you wrote is :S
@JKSH Thank you for your reply, the point is, I'm going to completely transform my SMFL project into a Qt project (because I can't make Qt and SFML efficiently work together). This is going to take weeks, if not months, so I'd rather not try out each method then choose which one is best. I'm gathering all the information I can find to choose now. Basically, my project draws (rotable, scalable) QPixmaps onto a window, as well as (movable) QPushButtons. These are the two main things it has to draw at each frame. There are other things, like colored circles, rectangles or numbers, but that shouldn't be a problem I think.
You said that QGraphicsScene stuff may not offer the best performance, but you also said that it was coded for these things. So, what do you think could offer nice performance?
@LuGRU Thank you for your reply, I'm not at all familiar with QtQuick though. I'm not familiar with VBO/IBO either. Actually, I don't even use Qt Creator when I'm coding Qt things. I'm using Ubuntu and am compiling everything through the terminal (
qmake
,make
,./run
). -
@Pippin The language is QML which supposedly stands for Qt Modelling Language or Qt Meta Language. It's part of the QtQuick part of Qt. Basically, instead of writing acres of C++ widget plumbing code, it lets you express UI declaratively with an elegant domain specific language (and the odd bit of javascript where imperative style is needed) and then the only bit of C++ you need is something to create a QQuickView to interpret the QML (and for convenience, the Qt SDKs provide a qmlscene tool which does exactly this). You're missing out on an (increasingly) important - and one of the best - parts of Qt if you're not familiar with QtQuick, IMHO. What sets QtQuick apart from other rapid application development tools is the ease of integration of C++ components into the QML (enabled by the moc-generated metadata) when some heavy lifting is needed (whether specialized OpenGL rendering, or intensive compute which would be too slow in javascript and really needs native code's power).
-
@Pippin said:
I'm going to completely transform my SMFL project into a Qt project (because I can't make Qt and SFML efficiently work together). This is going to take weeks, if not months, so I'd rather not try out each method then choose which one is best. I'm gathering all the information I can find to choose now.
Before you port any large project from one framework to another, it's always wise to spend a few days trying out your target framework. This applies to all frameworks, not just Qt.
Follow some tutorials; write a dummy program that animates 200 sprites, using similar sizes and frame rates as your final program. Does it perform well enough? If not, go try a different framework. But if your dummy program performs well enough, make it animate 400 sprites -- if it still works well, then you're fully confident it can handle your final program.
When trying to work out what's "best", you also need to consider the effort required to use the technology vs. the benefits you gain.
Basically, my project draws (rotable, scalable) QPixmaps onto a window, as well as (movable) QPushButtons. These are the two main things it has to draw at each frame. There are other things, like colored circles, rectangles or numbers, but that shouldn't be a problem I think.
What frame rate do you want? Any special effects? How does your user interact with these objects?
Note: In the Graphics View Framework or QML/Qt Quick, you don't need to manually manage each frame. Just place your items, tell them how to move, and Qt will take care of the frames for you.
You said that QGraphicsScene stuff may not offer the best performance, but you also said that it was coded for these things. So, what do you think could offer nice performance?
Yes, the Graphics View Framework was designed for handling lots of 2D sprites. The API is very easy to use, and allows you to do animations, scaling, rotations. In contrast, QPainter was not designed for animating sprites (even though it can do so, if you're persistent). Your code will become a nightmare to maintain; don't use QPainter for your project.
The Graphics View Framework performs best when your sprites are made of lines, polygons, arcs, texts, etc. Using custom images/pixmaps will incur an overhead. How much overhead? I don't know, I've never measured. It also really depends on how big your sprites are and what you want them to do -- I'm just giving you the insight you need to measure it yourself. (Alternatively, hire a consultant)
QML/Qt Quick was designed for creating fluid, modern, mobile-phone-style GUIs. It's a much newer technology than Graphics View, so it performs better in some cases, thanks to GPU acceleration. Does it perform better for your project? I don't know, I've never measured. Again, it's up to you to measure it if you want to know.
The bottom line is: the Graphics View Framework is definitely easier to use, but QML/Qt Quick might perform better (no guarantees), and it requires you to learn a new language. If it were my project, I'd do a quick test on the Graphics View Framework first. If that's not good enough, try QML/Qt Quick. If that's still not good enough, use raw OpenGL. (I'm reasonably confident that Graphics View meets your requirements though)
-
Okay thanks everyone! I'll give the Graphics View Framework a try then. About scaling though, may I ask how one can scale an item horizontally or vertically (not both) ?
QGraphicsItem::setScale()
seems to scale both dimensions. -
Use transformation i.e.:
QGraphicsItem item; item.setTransform( QTransform().scale( 1., 0.));
should do the trick.
-
@LuGRU Thanks for the tip.
Now, about OpenGL acceleration:
To enable OpenGL support, you can set a QGLWidget as the viewport by calling QGraphicsView::setViewport().
Source: http://doc.qt.io/qt-5/graphicsview.html
I'm not sure I understand this correctly. To trigger OpenGL acceleration, I would only have to do something like that?
QGLWidget foo; MyQGraphicsView.setViewport(&foo);
Or is it more complicated than this?
-
Also, how can I re-arrange the order of the
QGraphicsItems
in myQGraphicsScene
? I couldn't find the answer to this question inQGraphicsScene
's page, and it's quite important to me. Sorry for making this thread last forever. -
@Pippin
Hi
you mean zorder ?
setZValue, isObscured and isObscuredBy
And as side note.
Did you check out the 40000 sample ? -
@Pippin
OGL - Yes, just make sure QGlWidget don't go out of the scope.
You can also fiddle with ogl format settings, write You own shaders - i.e. as part of pain() in QGraphicsItem etc.