The best procedure (workflow) to create own QGraphicsScene/s
-
I'm developing a 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 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 it seems to be the best way. But I don't know the correct procedure / wokrflow to do it. Below are two flows 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 I did 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() (or update())
- 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 :-)
-
Hi and welcome to devnet,
One source of inspiration that you can use for your software is the Mandelbrot example in Qt's sources. Basically you offload the fractal calculation in a secondary thread and handle only the update in the main thread. That should be a good starting point.
Hope it helps
-
Hi, thanks for both, welcoming and your answer :-) ),
I'll definitely use Qt's source as an inspiration. Using threads is very good idea, but what about my rubber band type of zooming? I'd like to use this type of zooming, not clicking.
So could you help me, please, with layered scene question? Should I use widget and override paint event (as in Qt's Mandelbrot example) - but what about rubber band? Is possible to create two widgets as two layers and the top one transparent? Or should I use graphics view and override some event? Or graphics view with custom graphics items?
I really have to implement more fractals and switch among them on demand.
Thank you again.
-
Tricky.
If we say that each fractal is a QGraphicsItem, you can simply hide all but one fractal any time. You retain the current zoom position when you switch fractals (is this what you want? - not sure from your description)
You can also use the cache mode of QGraphicsItem (I would recommend DeviceCoordinateCache in this case), so once you painted a fractal, it doesn't need to be painted again until you change the zoom level.
The catch is that if a QGraphicsItem is the whole fractal, the paint event of that item also needs to paint the whole fractal. There are some ways to optimize this - e.g. using the "exposedRect" and "levelOfDetailFromTransform" of QStyleOptionGraphicsItem which you receive with the paint event (don't forget to set the flag QGraphicsItem::ItemUsesExtendedStyleOption on that item to make it work).
But in any case, the bottom line is that you need to paint at least the whole screen in one go, the first time a zoom is done.So how about this:
- You render tiles of the fractal into QImages in multiple threads
- When paint is called, you convert the necessary tiles into QPixmaps and paint them. That, at least, should be fast enough.
- You can further improve performance by doing some predictive rendering - e.g. you render the hidden fractals in the current zoom level in the background, so switching fractals will be instantaneously.
-
@Asperamanca Wow, thank you, sounds great (difficult but great). I'll think about it and try to implement. And if it won't be too complicated, I'll definitely use it, it could be exactly what I'm looking for. Thanks again.