I am using QGraphicsScene to draw millions of polygons. I plan to use Qt+OpenGL later, but for now I am not drawing more than 1 million polygons and Qt handles it just fine. The problem arises when I try to display text inside the polygons.
Each polygon (a custom class inheriting QGraphicsPolygonItem) is a visual representation of an object, and has pointer to that object associated with it. Each object has a std::string identifier. If I could simply display that string inside the polygons, it should be fine memory-wise. However, Qt seems to need a QString instead, and it takes a lot of time and space to convert every string. I am creating a QGraphicsTextObject for each polygon and each of those needs a QString copy of the std::string. Is there any way to bypass this copy using just Qt?
Cropping the scene is not desirable. There are some polygons that are too small for any text to fit inside them, and one can only see them by zooming in the scene. These polygons (and their text) need not be displayed (unless the user zooms in), but I don't think that would help without creating various other issues first.
P.S.: Displaying the text on-demand (as the user hovers the mouse over each polygon, for instance) is possible, however it'd be ideal if the text was readily displayed.
Have you profiled it to see if the conversion is actually your bottleneck? Font rendering isn't exactly fast, and if actually tries to render millions of texts, that will be slow. You won't get around the string conversions, the only thing I could think of is to optimize when to do them, and how often.
First, I'd consider to use a custom item either painting the text manually by reimplementing QGraphicsItem::paint, or deriving from e.g.
QGraphicsSimpleTextItem, which allows more tweaking than using QGraphicsSimpleTextItem or even QGraphicsTextItem directly, both which require you to call setText() and thus convert the string upfront.
One thing to be conscious about is when the conversion would be done.
With a custom item, you wouldn't need to do the std::string to QString conversion upfront (when calling setText()), but you could store the std::string and do it on demand only, in your paint() implementation, i.e. convert on first paint() call and then cache.
Another potentially expensive calculation is boundingRect(). This could be tweaked by returning a less precise approximation of the actual text shape. It doesn't hurt if the returned rectangle is somewhat bigger than the actually painted rectangle, it just shouldn't be smaller. So one could use a hardcoded height * approx. letter width + some padding.
Then, text not drawn at all is even cheaper. If the view is so zoomed out, that 10^6 items are drawn (and only then paint() is called), one will have a hard time to read any text. I'd reimplement paint() and make use of QGraphicsView's level-of-detail mechanism (see here) and just not convert the string nor paint anything below a certain level of detail/zoom level.
If you can use Qt 5.4, QGraphicsScene::minimumRenderSize might also come handy. But that alone won't avoid the string conversions nor the boundingRect() when not combined with the other suggestions above.
Related
I am building an editor using C++/Qt which has a click-and-drag feel to it. The behavour is similar to schematic editors (Eagle, KiCAD, etc), Microsoft Visio, or other programs where you drag objects from a toolbar into a central editing area.
My problem is that when the user clicks inside the custom widget I want to be able to select the instance of the box-like object and manipulate it. There will also be lines connecting the boxes together. However, I can't decide on an efficient method for selecting those objects.
I have two main thoughts on how to do the programming for this: The first is that the widget which is drawing the entire editor would simply encapsulate every one of the instances of the box. The other is to have each instance of the box (which is in my Model) carry with it an instance of a QWidget which would handle rendering the box (which would be in my View...but it would end up being strongly attached to the model). As for the lines connecting them, since they don't have a square bounding boxes they will have to be rendered by the containing widget.
So here is the summary of how I see this being done:
The editor widget turns into a container which holds the widgets and the widgets process their own click events. The potential issues here are that I don't know how to make the custom widget turn into a layout which lets click-and-drag functionality.
The editor widget takes care of all the rendering and processes the mouse clicks (the easier way in that I don't have to worry about layout...its just selecting the instances efficiently that I don't know what would be best).
So, now that there is a bit of background, for the 2nd method I plan on having each box-like instance having a bounding rectangle and the lines being represented by 3-4 pixel wide bounding rectangle segments (they are at 90 degree angles). I could iterate through every single box and line, but that seems really inefficient.
The big question: Is there some sort of data structure I can hold rectangles in and link them to widgets (or anything else for that matter) and then give it two coordinates (such as mouse coordinates) and have it spit me out the bounding box or linked object that those coordinates are inside of?
It sounds like your real question is about finding a good way to implement your editor, not the specifics of rectangle intersection performance.
You may be interested in Qt's "Diagram Scene" example project, which demonstrates the QGraphicsScene API. It sounds like a good fit for the scenario you describe. (The full source for the example ships with Qt.)
The best part is that you still don't have to implement hit testing yourself, because the API already provides what you are looking for (e.g., QGraphicsScene::itemAt()).
It's worth noting that internally, QGraphicsScene uses a simple iterative method to perform hit tests. As others have pointed out, this isn't going to be a serious bottleneck unless your scenes have a lot of individual items.
I am trying to display several images in the MainWindow and have the ability to perform certain actions when these images receive mousePressEvent.
For this, I thought I would create a new class, derived from QWidget, which had a QImage private member. With this, I as able to override the paintEvent and the mousePressEvent to both display the image and catch mouse input.
However, the problem is that the image drawn in the MainWindow has the widget size and its form (rectangular), and the image does not possess the same form (not even a regular form). This causes some problems because I am able to catch the mousePressEvent on parts that do not belong to my Image, but do belong to the Widget area.
Does anyone have any ideas on how to solve this?
I am still very new to QT, so don't please don't mind any huge mistake :)
Update:
Ok, I tried another approach.
I now have a graphicView on my MainWindow, with a graphicScene linked to it. To this scene, I added some graphicItems.
To implement my graphicItems I had to overload the boundingRect and paint method. As far as the documentation goes, my understanding is that QT uses shape method (which calls boundingRect) to determine the shape of the object to draw. This could possibly be the solution to my problem, or at least one solution to improve the accuracy of the drawn shape.
As of now, i am returning a square with my image size in boundingRect. Does anyone know how I can "adapt" the shape to my image (which resembles a cylinder)?
If you want to use QWidget you should take look at the mask concept to refine the space occupied by your images (be careful about the performance of this implementation).
If you decide to go with the QGraphicsItems you can re-implement the virtual method QPainterPath QGraphicsItem::shape () const to return a path representing as well as possible the boundary of your images.
The Qt documentation says :
The shape is used for many things, including collision detection, hit tests, and for the QGraphicsScene::items() functions.
I encourage you to go with the QGraphics system, it's sounds more appropriate in your case.
What I understand is that you want to shrink/expand/transform your image to fit a custom form (like a cylinder).
What I suggest is to use the OpenGL module QGLWidget and to map each coordinate of your rectangular image (0,0) (0,1) (1,0) (1,1) to a custom form created in OpenGL.
It will act as a texture mapping.
Edit: you can still catch mouse input in OpenGL
I'm creating a image visualizer that open large images(2gb+) in Qt.
I'm doing this by breaking the large image into several tiles of 512X512. I then load a QGraphicsScene of the original image size and use addPixmap to add each tile onto the QGraphic Scene. So ultimately it looks like a huge image to the end user when in fact it is a continuous array of smaller images stuck together on the scene.First of is this a good approach?
Trying to load all the tiles onto the scene takes up a lot of memory. So I'm thinking of only loading the tiles that are visible in the view. I've already managed to subclass QGraphicsScene and override its drag event thus enabling me to know which tiles need to be loaded next based on movement. My problem is tracking movement on the scrollbars. Is there any way I can create an event that get called every time the scrollbar moves. Subclassing QGraphicsView in not an option.
QGraphicsScene is smart enough not to render what isn't visible, so here's what you need to do:
Instead of loading and adding pixmaps, add classes that wrap the pixmap, and only load it when they are first rendered. (Computer scientists like to call this a "proxy pattern"). You could then unload the pixmap based on a timer. (They would be transparently re-loaded if unloaded too soon.) You could even notify this proxy path of the current zoom level, so that it loads lower resolution images when they will be rendered smaller.
Edit: here's some code to get you started. Note that everything that QGraphicsScene draws is a QGraphicsItem, (if you call ::addPixmap, it's converted to a ...GraphicsItem behind the scenes), so that's what you want to subclass:
(I haven't even compiled this, so "caveat lector", but it's doing the right thing ;)
class MyPixmap: public QGraphicsItem{
public:
// make sure to set `item` to nullptr in the constructor
MyPixmap()
: QGraphicsItem(...), item(nullptr){
}
// you will need to add a destructor
// (and probably a copy constructor and assignment operator)
QRectF boundingRect() const{
// return the size
return QRectF( ... );
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget){
if(nullptr == item){
// load item:
item = new QGraphicsPixmapItem( ... );
}
item->paint(painter, option, widget);
}
private:
// you'll probably want to store information about where you're
// going to load the pixmap from, too
QGraphicsPixmapItem *item;
};
then you can add your pixmaps to the QGraphicsScene using QGraphicsScene::addItem(...)
Although an answer has already been chosen, I'd like to express my opinion.
I don't like the selected answer, especially because of that usage of timers. A timer to unload the pixmaps? Say that the user actually wants to take a good look at the image, and after a couple of seconds - bam, the image is unloaded, he will have to do something in order the image to reappear. Or may be you will put another timer, that loads the pixmaps after another couple of seconds? Or you will check among your thousand of items if they are visible? Not only is this very very irritating and wrong, but that means that your program will be using resources all the time. Say the user minimizes you program and plays a movie, he will wonder why on earth my movie is freezing every couple of seconds...
Well, if I misunderstood the proposed idea of using timers, execuse me.
Actually the idea that mmutz suggested is better. It reminded me of the Mandelbrot example. Take a look at it. Instead of calculating what to draw you can rewrite this part to loading that part of the image that you need to show.
In conclusion I will propose another solution using QGraphicsView in a much simpler way:
1) check the size of the image without loading the image (use QImageReader)
2) make your scene's size equal to that of the image
3) instead of using pixmap items reimplement the DrawBackground() function. One of the parameters will give you the new exposed rectangle - meaning that if the user scrolls just a little bit, you will load and draw only this new part(to load only part of an image use setClipRect() and then read() methods of the QImageReader class). If there are some transformations you can get them from the other parameter(which is QPainter) and apply them to the image before you draw it.
In my opinion the best solution will be to combine my solution with the threading shown in the Mandelbrot example.
The only problem that I can think of now is if the user zooms out with a big scale factor. Then you will need a lot of resources for some time to load and scale a huge image. Well I see now that there is some function of the QImageReader that I haven't tried yet - setScaledSize(), which maybe do just what we need - if you set a scale size and then load the image maybe it won't load first the entire image – try it. Another way is just to limit the scale factor, a thing that you should do anyway if you stick to the method with the pixmap items.
Hope this helps.
Unless you absolutely need the view to be a QGraphicsView (e.g. because you place other objects on top of the large background pixmap), I'd really recommend just subclassing QAbstractScrollArea and reimplementing scrollContentsBy() and paintEvent().
Add in a LRU cache of pixmaps (see QPixmapCache for inspiration, though that one is global), and make the paintEvent() pull used pixmaps to the front, and be set.
If this sounds like more work than the QGraphicsItem, believe me, it's not :)
I have a QGraphicsView for a very wide QGraphicsScene. I need to draw the background in drawBackground() and the background is a bit complicated (long loop) although it doesn't need to be repainted constantly. I store it in a static QPixmap (I tried QImage too) inside the function drawBackground() and that pixmap is what I draw onto the painter of the view. Only when needed is the QPixmap painted on again.
If I didn't use a static pixmap, the complicated background would be generated every time I scroll sideways for example. The problem is that apparently there is a maximum width for pixmaps on Windows, on my computer it's 32770. I could store a list of pixmaps and draw them side by side but it would make the code uglier and I also don't know what the maximum width of a pixmap is for every Windows machine. Since this might be a well-known problem I was wondering if anyone has a better solution.
Thanks.
You can probably avoid the windows limit by using unaccelerated raster paint device, but 32770*1024 is 100MiB of pixmap; you probably don't want to do that even if Windows would let you.
You've already thought of the usual answer (tile it in more reasonably-sized chunks and load/generate them on demand). The other piece of the usual solution is to use something like QPixmapCache to keep the recently-used tiles so you don't regenerate them too often (only when the user scrolls a long way).
You didn't say how complex your complex background is, but you might also want to look at the Mandelbrot set example for how to do piecewise rendering of an (infinitely) large background pixmap on-demand, without blocking the UI.
This is the common use case for the tiling pattern. Basically you split the background into small images.
I'm not sure why you think "it would make the code uglier". It is certainly not a one-liner. Depending whether you have fixed size background image or not, the tiling code is usually pretty straightforward.
I used the owner-drawn strategy on CMyListBox class which derives from CListBox. I only want the DrawItem() method to perform when I insert an item in the listbox. But the method is invoked many times. How can I change to invoke it whenever I need.
You could always cache the initial drawing by outputting the content to an in-memory bitmap and then drawing that, it does mean you need to track when something has changed so you can run the actual rending code agaain. It does save running through your render code everytime if there's a lot of it.
I've done exactly what Kieron suggests by caching bitmaps, but only in very expensive rendering code. I actually have to keep multiple cached "states" depending on if an item is highlighted, disabled, normal, etc (this is for toolbar buttons, not listitem - but I think it applies). I only cache the pre-rendered image when I first need it - that way I only cache "states" that I actually need.
My drawing was pure GDI calls. Mostly bitmap manipulations and other drawing that just takes time, plus I was being redrawn much too often (for no good reason - long story).
Changing the fundamentals in the framework I was using (MFC and Stingray) was just not an option. The caching was a last resort after all other optimizations weren't good enough (damn slow virtual machines!!).
Normally drawing is fast enough to do when you're invalidated (DrawItem in this case). I would take a look at what exactly you're doing in DrawItem. I would look into caching data and calculations that are needed by rendering and not the rendering itself (eg the final bitmaps) unless there are no other options.
Also, I read the Vista rending is more optimized, they cache what you've drawn on your window to reduce the contain invalidate/redraw cycle when, for example, a window is moved from behind another.
The DrawItem() method is called whenever there is a requirement to draw any given item in the listbox. If you do not respond to it you are likely to get a blank area in your list box, where the drawn data has been erased and you have not refreshed it. If you really do not think the drawing is necessary, you could do something like
void CMyListBox::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
if (!m_DrawingEnabled)
return;
}
Where m_DrawingEnabled is a member you maintain to stop unnecessary draws,