QGraphicsItemGroup to OpenGL texture - qt

I'm working on an application that is using map tiles in a QGraphicsItemGroup to render a map and I want to make the switch to OpenGL so I can add 3D shapes on the map.
In order to do so as smoothly as possible I would need to get a texture from this QGraphicsItemGroup.

In case anyone has this issue I managed to get something I could work with. Rather than using the QGraphicsItemGroup I took the whole scene and made an image from it that I use as a texture.
Code extract : (map is my QGraphicsScene)
QImage img(ui->map->width(), ui->map->height(),QImage::Format_ARGB32_Premultiplied);
QPainter p(&img);
ui->map->fitInView(0,0,scene.width(),scene.height(),Qt::IgnoreAspectRatio);
ui->map->viewport()->render(&p);
p.end();
img.save("tmp.png");

Related

How can I add multiple QPixMap item to a widget?

I have video analysis c++ code and porting to the QT Creator for gui requirements.
We are detecting certain shapes and items from the video stream and adjust their size ie 100x100 and want to display on the screen. We can display through Label but only one QpixMap item.
What is the best way to show detected images automatically on a widget such as last 20 one that autosized?
Is there any a small code sample to start with it to show us the direction?
Thanks.
You could subclass QWidget and reimplement paintEvent to paint whatever you need using QPainter.
See Basic Drawing Example for details:
http://doc.qt.io/qt-5/qtwidgets-painting-basicdrawing-example.html

Qt: The best procedure to create own QGraphicsScene/s

I'm making fractal creator software. So I need one scene per fractal and these scenes need to be "layered" because of zooming rubber band.
I've already tried to write it the "widget" way, so I had one custom widget called "canvas". In my canvas class I overrided paintEvent, and inside this event I rendered current fractal. Everytime when somebody clicked to menu with another fractal, I called update() method and new fractal was rendered. To zooming I used overriding of mouse events and update() of canvas. At the first time I repainted the whole canvas, but it was very very slow. After that I repainted only the part under the rubber band, but still slow when I'd like to select some bigger area and other problems with repainting.
So I've looked for another way to do it. Layers. I've found the QStackedWidget, but I didn't find way how to make visible both of my layers and the top one to be transparent. After that, I've found QGraphicsScene and this seems to be the best way to do it. But I don't know the correct procedure to do it. Below are two procedures I'm thinking about:
Create QGraphicsView
Instead of the widget, the canvas will be QGraphicsScene
I'll override some QGraphicsScene event (but I don't know which one - drawItems() is obsolete and override update() seems wrong to me, but maybe...)
When other fractal will be chosen, I'll repaint canvas by calling update() the same way as in my "widget" solution
In the foreground layer will be zooming rubber band
or:
Create QGraphicsView
Instead of the widget, the canvas will be QGraphicsScene
Every fractal will be the child of QGraphicsItem
When other fractal will be chosen, I'll remove the old one fractal
item and replace it by new one and probably call invalidate()
In the foreground layer will be zooming rubber band - I think, that
it's common behaviour of the QGraphicsScene isn't it?
Is one of my reasonings correct? Do you suggest anything else? Fractals are complicated in the calculations and It's very important to repaint only if it is necessary. Could you help me, please?
Thank you :-)
Edit: "zooming rubber band" explanation:
I'm sorry for my expression "zooming rubber band". It means scale (zoom) the area below the selection made by the rubber band - zooming the same way as in Photoshop CS5 (for example). And I'd like to know what part of the scene is repainted while selecting this way. If there is repainted whole scene, or the part of the scene below selected area, or there is nothing repainted and rubber band selection is done in separate layer.
I hope my explanation helped :-).
In Qt, a QGraphicsScene can be thought of as a world of items, with a QGraphicsView as a window into that world. Therefore, you should be adding items to the QGraphicsScene, based on QGraphicsItem (or QGraphicsObject if you want signals and slots).
In your situation, I'd create a Fractal class that inherits from QGraphicsItem and add that to the scene. Ensure to override the necessary pure virtual functions such as boundingRect and paint.
Do not calculate the fractal code in the paint function. I suggest the Fractal class stores a QPixmap (or QImage if you're drawing at the pixel level) and render the fractal to this. Then perodically, in the paint function, the Fractal class would render the contents of the QPixmap with a call to painter->drawImage or painter->drawPixmap; whichever is relevant in this case.
As for zooming, your Fractal class can then response to being scaled, appropriately changing the rendering on the internal representation.

How to place a QPainter element in QML Rectangle?

There is a diagram which I have drawn with QPainter.
How do I diaplay it in QML's Rectangle?
That diagram will get updated on the runtime.
The question is a bit vague.
Do you have the diagram in a QImage or a QPixmap? Then you can use image providers to expose the diagram to QML, and draw it into a Image element.
Do you have the code that uses a QPainter to draw it, and want to make a QML element that uses such code? In that case, subclass QDeclarativeItem (in Qt Quick 1) or QQuickPaintedItem (Qt Quick 2), override their paint() methods and do the drawing there. You can then register the new type in the QML engine, so you can use a Diagram type in your QML files.

how to render a map defined by a matrix of cells using QT Gui?

I have to render using QT a map defined by a matrix of cells in overview mode (think GPS map). I'd like to be able to zoom on it and each cell is defined by its color and some properties (bitmaps to put on the cell). I also need, like in a GPS, to be able to move around (using the directional arrows) in the map.
Right now, I'm thinking about drawing a matrix of QImages on my screen, and loading each one of them with the info of the cells I need, but it doesn't seem like a very good solution.
Thank you for every possibility you might provide.
Your initial idea is a reasonable one, but put all the QImages and info you need into a custom QGraphicsItem and add them to a QGraphicsScene (and fix their position) - then you only need a QGraphicsView to visualise everything. This way you get the BSP painting and selection optimisations, view transformations, and nice animations (if required!) for free.

Qt & double buffering - are there any neat tricks to capture pixels or manipulate the back buffer?

I'm migrating an application to Qt from MFC.
The MFC app would use GDI calls to construct the window (a graph plot, basically). It would draw to a memory bitmap back buffer, and then BitBlt that to the screen. Qt, however, already does double buffering.
When the user clicks and drags in the graph, I'd like that section of the window to be inverted.
I'd like to find the best way to do this. Is there a way to do something like grabWindow() that will grab from the widget's back buffer, not the screen? ... maybe a BitBlt(..., DST_INVERT) equivalent?
I saw setCompositionMode() in QPainter, but the docs say that only works on painters operating on QImage. (Otherwise I could composite a solid rectangle image onto my widget with a fancy composition mode to get something like the invert effect)
I could do the same thing as MFC, painting to a QImage back buffer... but I read that hardware acceleration may not work this way. It seems like it'd be a waste to reimplement the double buffering already provided to you in Qt. I'm also not so sure what the side effects of turning off the widget's double-buffering may be (to avoid triple-buffering).
At one point, I had a convoluted QPixmap::grabWidget() call with recursion-preventing flags protecting it, but that rendered everything twice and is obviously worse than just drawing to a QImage. (and it's specifically warned against in the docs)
Should I give up and draw everything to a QImage doing it basically like I did in MFC?
EDIT:
Okay, a QPixmap painter runs at approximately the same speed as direct now. So, using a QPixmap back-buffer seems to be the best way to do this.
The solution was not obvious to me, but possibly if I looked at more examples (like Ariya's Monster demo) I would have just coded it the way it was expected to be done and it would have worked just fine.
Here's the difference. I saw help system demos using this:
QPainter painter(this)
in the start of paintEvent(). So, it seemed to naturally follow to me that to double buffer to a QPixmap then paint on the screen, you needed to do this:
QPainter painter(&pixmap);
QPainter painterWidget(this);
... draw using 'painter' ...
painterWidget.drawPixmap(QPoint(0,0), pixmap);
when in fact you are apparently supposed to do this:
QPainter painter;
painter.begin(&pixmap);
... draw using 'painter' ...
painter.end();
painter.begin(this);
painter.drawPixmap(QPoint(0,0), pixmap);
painter.end();
I can see that my way had two active painters at the same time. I'm not entirely sure why it's faster, but intuitively I like the latter one better. It's a single QPainter object, and it's only doing one thing at a time. Maybe someone can explain why the first method is bad? (In terms of broken assumptions in the Qt rendering engine)
Assuming you don't really want to pixel values from your offscreen buffer (but rather, just drawing something again on top of it and blit again to the screen), you should use QPixmap as the buffer, not QImage. Using the latter disables all painting acceleration as Qt falls back using its software raster engine, hence the use QPixmap. If you use OpenGL graphics system, you can still benefit from it.
For an example on how to do this, check my last code on running the Monster demo, it needs to have an offscreen pixmap to due the motion blur effect via repeated painting with source over composition mode.
To disable Qt's backing store (which is generally not a good idea), use the Qt::WA_PaintOnScreen for your top-level widget.
A bit unrelated, but you might want to have a look QRubberBand widget.
Drawing on top of the graph area you should be able to use composition modes to invert. Draw white using the Difference composition mode. The following example is a subclass of a QLabel showing a pixmap:
void Widget::paintEvent(QPaintEvent *pe)
{
// make sure we paint background
QLabel::paintEvent(pe);
// paint the overlay
if (!selectionRect.isNull()) {
QPainter p(this);
p.setCompositionMode(QPainter::CompositionMode_Difference);
p.fillRect(selectionRect,QColor("#FFFFFF"));
}
}
alt text http://chaos.troll.no/~hhartz/yesManInverted.png
The simplist, most straightforward answer I know of is to do it like you were doing before, to a QImage, and use the QImage as the source for your widget on the screen.
Another option might be to add a transparent widget over your graph, which only draws the inverted part of the graph. I don't think this will optimize the drawing at all, however. It will likely cause the underlying graph to be drawn, and then the overlay of the inverted part.

Resources