PDA

View Full Version : Fast painting scrollable widget



bunjee
28th September 2007, 13:18
Hey there trolltechies,

I'm designing a Qt interface for an instant messaging app.

http://bunjeee.free.fr/QtApp.png

I've already created my custom scrollable widget :
- It contains a widget.
- This widget is containing one layout.
- In this layout, I have every single widget contained in my list.

When scrolling I'm simply moving the contained widget to a new position.

I'm using custom painting functions for every single widgets in that list.

I have two issues.
- The scrolling is quite slow, especially when there is a lot of items on the screen.
- The resizing is very very slow.

I looked at the Qt examples, it appears some of them are using QGraphicsView / Scene.
I've been wondering if in my case I could display my widgets in a GraphicView to boost up my performance.

Is there some way to use a widget as a QGraphicItem ?
Would it help in my case ?
Is there some way to boost the standard QPainter painting speed ?

Thanks.

MrArnaud.
www.zepeople.com

wysota
28th September 2007, 13:29
In your case I'd use the model-view approach rather than the graphics view...

momesana
28th September 2007, 13:46
You can't use Widgets in the QGraphicsView Framework yet. The trolls are working on it as a research project and it is possible that the features makes it into the the Qt-4.4er release. The approach that best suits your needs is using interview as outlined by wysota and using custom itemDelegates.

bunjee
28th September 2007, 13:49
So you would stick to my current approach.

In that case, any ideas to optimize the painting ?

wysota
28th September 2007, 14:00
No, we would not stick to your current approach. You use widgets and we suggest to use a single view instead of multiple widgets.

momesana
28th September 2007, 15:17
Read about interview http://doc.trolltech.com/4.3/model-view-programming.html.
Then use a QTreeView/QListView to display the items. But instead of using the standard delegates you will provide your own. The Delegate is responsible for painting the listitem.
See this thread for some more information regarding this: http://www.qtcentre.org/forum/f-qt-programming-2/t-drawing-a-widget-in-qitemdelegates-paint-method-8660.html

cheers

bunjee
28th September 2007, 15:40
Then use a QTreeView/QListView to display the items. But instead of using the standard delegates you will provide your own. The Delegate is responsible for painting the listitem.

The only concern is : I need to use widgets not items, as a result I cannot use delegates. I need a full widget implementation inside a scrollable area.

Maybe QScrollArea would suit me better. But I doubt it would change the performance. As for the listview I'm not sure how to implement it.

wysota
28th September 2007, 16:37
Why do you need to use widgets? And what part of widget functionality? If you are after checkboxes or other buttons, you can safely go for the model-view approach. A well written custom delegate will handle all that.

momesana
28th September 2007, 17:00
I did it once and it was not the Ideal solution in terms of performance but it works. Here is a screenshot:
http://gentoo-wiki.com/Image:Servant_nr3.jpg
So if you really don't want to use Interview, you can do that but the model/view approach is by far the best way to do these kind of things.

bunjee
28th September 2007, 17:13
Why do you need to use widgets? And what part of widget functionality? If you are after checkboxes or other buttons, you can safely go for the model-view approach. A well written custom delegate will handle all that.

From a design standpoint on this application I chose to focus on the scrolling list, like the ipod/iphone would do.

So I looked the Qt Api:

QListWidget works with items.
Wouldn't that be great if we could just push back any widget into a list ? For example if I need an edit box in my list widget.

So I recoded my own settable layout based on widget that could be easily encapsulated with another so I wouldn't have to worry if I needed to fill it with an item or a widget.

Why would I emulate a QPushButton and then populate my list when I could simply add it to my list ? makes no sense, it's a hack to me.

If I need a specific QPushButton I'm just going to inherit that item an redeclare the repaint function, why would I want to hack the display of the original QPushButton ?

I'm very surprised that nobody even in the other thread seems interested by a list of widgets. Coupled with a settable widget layout class it's very convenient. A simple call and you change or add anything into your list.

I see my list as a layout of widgets where I can set and unset anything by a single call.

wysota
28th September 2007, 18:56
Wouldn't that be great if we could just push back any widget into a list ?
Actually you can. But you shouldn't if you want to avoid slowness.


For example if I need an edit box in my list widget.
You can easily achieve that using a custom delegate and making it return a proper widget from createEditor().


Why would I emulate a QPushButton and then populate my list when I could simply add it to my list ? makes no sense, it's a hack to me.
Because it will be many times faster. 100 items means 100 buttons in your case, each of them needs to be repositioned, shown or hidden as necessary by the window system. Using itemview approach and only drawing the button (without ever creating the actual button) makes things much faster and much easier.


If I need a specific QPushButton I'm just going to inherit that item an redeclare the repaint function, why would I want to hack the display of the original QPushButton ?
Nobody says you should.


I'm very surprised that nobody even in the other thread seems interested by a list of widgets.
You must have looked not deeply enough. We discussed that many times here. The bottom line is - it's slow and it's easier to do it using itemviews, but if you can't live without widgets or you can accept the fact that your application will not be scalable, you can use setIndexWidget() or setItemWidget() depending on whether you use the model approach or the convenience classes.


Coupled with a settable widget layout class it's very convenient. A simple call and you change or add anything into your list.
Yes, that's why you can do it using QScrollArea. But it will be slow and cubersome to manipulate from a central point (like having to connect each button's signals to a custom slot where you have to find out which button was clicked, etc.). Oh... and it's awfully slow. The situation will improve in 4.4, but I'm pretty sure item-view approach will still be easier to control if not faster.


I see my list as a layout of widgets where I can set and unset anything by a single call.
Yes and it works. But then you come here and say that your application is slow. And our advice to speed things up is to buy better hardware... eeem... sorry... get rid of unnecessary widgets.

EDIT:
Attached is a small and crude example of what I mean. Of course in a real app you'd need to handle mouse press events as well to make the button look sunken.
Try achieving the same speed using widgets.

bunjee
28th September 2007, 20:23
I see your point.

Even though from my perspective Qt is supposed to be heavily widget based.
Otherwise I would use SDL or embedded Opengl.

I imagine your approach is faster.
But, if I want to be fast I'm using opengl and I'll achieve 500 fps.

My goal is : creating a scalable and easy to maintain desktop application.
From my point of view using widgets and only widgets makes more sense than delegating the painting. This should be done only to customize the appearance.

And beside, what about layouts in a delegate ?

I can't help but thinking it's a hack to make a painting area "look like" a widget and I don't like the idea. And that's not the direction I'd like Qt to evolve.


Because it will be many times faster. 100 items means 100 buttons in your case, each of them needs to be repositioned, shown or hidden as necessary by the window system. Using itemview approach and only drawing the button (without ever creating the actual button) makes things much faster and much easier.

Why would rendering a given scene vary between a delegate and a widget ?
It should be as fast in a widget than in a delegate period.

If I'm displaying a button through a qwidget item or through a delegate, the only thing that should vary is the memory occupied by each instances not the rendering speed.

I guess that's what they're trying to achieve for the next Qt.

wysota
28th September 2007, 20:43
Even though from my perspective Qt is supposed to be heavily widget based.
Otherwise I would use SDL or embedded Opengl.
Depends why you use Qt. I don't use it because it has widgets (all toolkits do). I use it because it's portable, object oriented and it's very easy to develop using it both on lowlevel (like widgets) and highlevel (abstractions such as model-view, graphics view or databases).


I imagine your approach is faster.
But, if I want to be fast I'm using opengl and I'll achieve 500 fps.
And it'll take weeks to implement it.


My goal is : creating a scalable and easy to maintain desktop application.
From my point of view using widgets and only widgets makes more sense than delegating the painting. This should be done only to customize the appearance.
I don't think we understand each other.


And beside, what about layouts in a delegate ?
What about them? You can implement a layout engine if you want...


I can't help but thinking it's a hack to make a painting area "look like" a widget and I don't like the idea.
Well... that's your point of view. Widgets themselves can be considered a hack as well... After all, they are separate objects that cheat by calling the same routines I'm calling in my delegate that draws on another widget.



And that's not the direction I'd like Qt to evolve.
Of course it would be pretty to do everything without any cost. But I'm happy that I can call a single function and it will render a button in a platform independent way.


Why would rendering a given scene vary between a delegate and a widget ?
Oh... I can answer that... Or maybe not, I'll point you to an article where it's explained.

http://labs.trolltech.com/blogs/2007/08/09/qt-invaded-by-aliens-the-end-of-all-flicker/
http://labs.trolltech.com/blogs/2007/08/30/say-goodbye-to-flicker-aliens-are-here-to-stay/


It should be as fast in a widget than in a delegate period.
Yes, it should. But unfortunately due to the overhead of using native windows (see links above), it's not. Of course implementing non-window widgets is a hack as well, so we could loop to one of your previous sentences - making a not-widget behave like a widget (as QWidgets will not be real widgets in a strict meaning of this word anymore).

By the way... even with non-window widgets the item-view approach will be faster, because the layout won't have to reposition all the widgets.

Oh, and another thing. The advantage of using the item-views approach is not only the speed. The main advantage is that I can use models with it easily. And it's much more work to emulate it using non item-view approach (see my argument about connecting 1000 buttons to a slot). And an instant messenger application is a perfect candidate for using the model-view approach. But of course I'm not stopping you from doing it your way...

Gopala Krishna
29th September 2007, 14:38
@bunjee:

Wysota has shown you the path towards best possible solution. I don't understand why you don't want to use the delegate approach. Its actually not hack and if you want good performance with listview kind of widget then its best you stick on to interview and delegate approach. After all that was why trolls added the delegate concept to interview.

I also don't understand one thing. When there is already a way(faster) to do what you want why do you don't want to adopt it and want to use the widget approach itself and tweak it so much to match the performance of delegate approach ?
Widgets consume more memory too other than disadvantages pointed out in the links given above.
Anyway i could be missing something and you might have specific reasons for that. If so I'm sorry.

wysota
29th September 2007, 15:30
I also don't understand one thing. When there is already a way(faster) to do what you want why do you don't want to adopt it and want to use the widget approach itself and tweak it so much to match the performance of delegate approach ?

I think I can answer that. Using widgets is just simpler in that way that you don't have to write any code to handle things like positioning or interaction and I fully understand that regardless of problems it causes.

I think it is a very good idea to develop a delegate that would allow us to place and handle widget-like objects in the delegate. Maybe even Trolls ought to do that. We'd then have three different in approach but similar in handling frameworks - the widget system, the graphics view framework and the item delegate system - each allowing us to place objects on a canvas and interacting with them.

I think it's a nice subject for an academic project if sobebody is looking for one. But until someone actually writes it, we have to stick with a low level approach as suggested.

bunjee
24th October 2007, 04:59
Qt posters,

Here are my latest thoughts about the widget vs delegate approach.

I'm coming from the opengl scene and from my point of view a polygon is a polygon, a mesh is a mesh, a 2d drawable surface is a 2d drawable surface.
Performance wise displaying something inside a widget paint event or a delegate paint function shouldn't dramaticaly change a thing.
I had difficulties understanding why having lots of widgets was causing such a performance hit.

http://labs.trolltech.com/blogs/2007/08/09/qt-invaded-by-aliens-the-end-of-all-flicker/
http://labs.trolltech.com/blogs/2007/08/30/say-goodbye-to-flicker-aliens-are-here-to-stay/

As wysota pointed out in the above links it's mainly because in the current Qt stable release the window manager keeps track of every single widgets.
Every single time a widget is hidden / moved or whatever the window manager is notified and at some point screw the performance up.

I know what you're thinking : there is no point for the window manager to be aware of what's going on inside a window widget. Easy to say, according to the Qt dev, a portable solution for this problem was quite difficult to implement.

From what I've seen in Qt 4.4 latest snapshot, I'm glad to let you know that from a performance perspective, having a list of 1000 widgets is no longer science fiction.

In my case where I'm using a QScrollArea to display a list of widget the performance between 4.4 and 4.3 is like day and night.

I see the benefits of the model / view approach for most of the cases.
But, if you really need a QWidget list the 4.4 will grant you fairly good performance.
My program is based on a list and only a list. I took the bet that it will be possible to do everything inside a list of widgets. So it is crucial for me to have those widgets otherwise I'm trade-offing too much of the potential expandability of my software.

Now, the only concern I have with widgets is the fact that it's memory consuming. As a result even if the display is fast enough, creating and deleting widgets sometimes freezes the window.
Maybe a Threaded deletion approach would solve the problem.

mchara
24th October 2007, 08:20
I must agree with wysota and others - Qt 4.4 will fight with flickering not performance, the reason is not painting the widgets but lots of other stuff(events, communication ect.) that widgets do and You don't really need it in your application.
If you don't want to use model view with delegates, maybe consider overriding QListWidget with custom paintEvent.

I have QTreeWidget in my application with paintEvent that emulates matrix of pushButtons and it works fine for me...

wysota
24th October 2007, 09:22
I'm coming from the opengl scene and from my point of view a polygon is a polygon, a mesh is a mesh, a 2d drawable surface is a 2d drawable surface.
Performance wise displaying something inside a widget paint event or a delegate paint function shouldn't dramaticaly change a thing.
Consider that the power of OpenGL comes from the fact that you actually stream commands into the GPU (in a specific order), which is sometimes tedious and prone to errors. We could say that this is Bad(TM) because it's low-level and doesn't allow much flexibility when writing the code. But it's just the way OpenGL works - it's optimized for performance and to get best performance, you need to stream commands into the processing unit.



I had difficulties understanding why having lots of widgets was causing such a performance hit.

http://labs.trolltech.com/blogs/2007/08/09/qt-invaded-by-aliens-the-end-of-all-flicker/
http://labs.trolltech.com/blogs/2007/08/30/say-goodbye-to-flicker-aliens-are-here-to-stay/

As wysota pointed out in the above links it's mainly because in the current Qt stable release the window manager keeps track of every single widgets.
Every single time a widget is hidden / moved or whatever the window manager is notified and at some point screw the performance up.

I know what you're thinking : there is no point for the window manager to be aware of what's going on inside a window widget. Easy to say, according to the Qt dev, a portable solution for this problem was quite difficult to implement.

From what I've seen in Qt 4.4 latest snapshot, I'm glad to let you know that from a performance perspective, having a list of 1000 widgets is no longer science fiction.
Actually this is unfortunately not true. I've been talking to Trolls about this during DevDays and it looks like Aliens won't improve the speed of item-view index widgets. At least not in any significant way. Using delegates is still the preferred solution. I know that Trolltech is working on a better default delegate that would allow us to make some things in an easier fashion and they also work on a new generation item-views framework. So there is work around the subject, but it's hard to say when it will be available to public.


In my case where I'm using a QScrollArea to display a list of widget the performance between 4.4 and 4.3 is like day and night.
Yes, in that situation the performance should be better.


I see the benefits of the model / view approach for most of the cases.
But, if you really need a QWidget list the 4.4 will grant you fairly good performance.
My program is based on a list and only a list. I took the bet that it will be possible to do everything inside a list of widgets. So it is crucial for me to have those widgets otherwise I'm trade-offing too much of the potential expandability of my software.
As the name says Itemviews is oriented around items, not widgets, so it's not optimized for your case. It's like you wanted to implement an image viewer using QTextDocument. Sure it's possible, but there are better ways to do this.


Now, the only concern I have with widgets is the fact that it's memory consuming. As a result even if the display is fast enough, creating and deleting widgets sometimes freezes the window.
Memory consumption should decrease with 4.4 and Aliens. But I doubt your problem is memory consumption. I think the problem is positioning the widgets - if you create and show a widget (or hide it) the layout of all widgets needs to be recalculated and that takes time with a vast number of widgets.


Maybe a Threaded deletion approach would solve the problem.
No, it will crash your application.


I must agree with wysota and others - Qt 4.4 will fight with flickering not performance, the reason is not painting the widgets but lots of other stuff(events, communication ect.) that widgets do and You don't really need it in your application.
Exactly. According to Marius (one of the Trolls responsible for Itemviews) the slowndown is caused by actually moving the widgets in the view.


If you don't want to use model view with delegates, maybe consider overriding QListWidget with custom paintEvent.

I have QTreeWidget in my application with paintEvent that emulates matrix of pushButtons and it works fine for me...

That won't change anything - you still have to fake the widgets and it's easier to do from within a delegate as you only care about one "widget" at a time. And if you really need the power of widgets and not items, I'd think about abandoning Itemviews - it's not meant for you.

bunjee
24th October 2007, 12:24
Consider that the power of OpenGL comes from the fact that you actually stream commands into the GPU (in a specific order), which is sometimes tedious and prone to errors. We could say that this is Bad(TM) because it's low-level and doesn't allow much flexibility when writing the code. But it's just the way OpenGL works - it's optimized for performance and to get best performance, you need to stream commands into the processing unit.

I won't comment any further on this, we cannot really compare Opengl and Qt. What I was pointing out is the fact that what will make Opengl fast (or not :)) is the number of triangles period. Not the fact that they're inside whatever signal based container.


Actually this is unfortunately not true. I've been talking to Trolls about this during DevDays and it looks like Aliens won't improve the speed of item-view index widgets. At least not in any significant way. Using delegates is still the preferred solution. I know that Trolltech is working on a better default delegate that would allow us to make some things in an easier fashion and they also work on a new generation item-views framework. So there is work around the subject, but it's hard to say when it will be available to public.

You've got to accept that : if we admit that the performance of widgets was partly slow because of the interactions / updates with the window manager now that Qt 4.4 is using Alien windows/widgets it should definitely be faster. It's written on the blog you linked us to.


Yes, in that situation the performance should be better.

Why would it be way faster in my case and not with the item view approach ? Layouts ? Well I'm using layouts.


As the name says Itemviews is oriented around items, not widgets, so it's not optimized for your case. It's like you wanted to implement an image viewer using QTextDocument. Sure it's possible, but there are better ways to do this.

Yeah that's definitely why I kept my approach. Delegates are not of any use in my case since my approach is to base everything on a layouted QScrollArea of widget.


Memory consumption should decrease with 4.4 and Aliens. But I doubt your problem is memory consumption. I think the problem is positioning the widgets - if you create and show a widget (or hide it) the layout of all widgets needs to be recalculated and that takes time with a vast number of widgets.

You make a point.
But, again, performance wise, if the QLayout was really optimized what would differenciate it from a listview of delegates ? If you insert / remove items you still have to recalculate the position of your painting area.


No, it will crash your application.

As we both agreed, I'm keeping my QScrollArea of QWidgets and I'm quite happy with it. When my widgets are loaded performance is good.
Now I have to think about how to load/unload as fast as I can.

In my case I've come to the conclusion that since one user cannot see more than a given numbers of rows at a time, it makes sense to thread the adding / removing of widgets using signals. As a result I would be streaming populating and de-populating a list.

And no if it's written correctly it shouldn't crash anything. Still if you have better solutions I'm interested as I'm still brainstorming about this.


Exactly. According to Marius (one of the Trolls responsible for Itemviews) the slowndown is caused by actually moving the widgets in the view.

Again, if it's well written I see no reason for that.

momesana
24th October 2007, 15:18
Maybe using QGraphicsView would be a middleway if the research going on around QGV-Layouts and QGV-Widgets bear fruit. I don't think this will be the case with Qt-4.4 but maybe Qt-4.5. Then we could have QPushButton clones with a similiar interface but made of standard QGraphicsItems providing both performance and ease of use, not to mention the eyecandy. The slides from Trolltech's DevDay were really interesting.

wysota
24th October 2007, 20:47
I won't comment any further on this, we cannot really compare Opengl and Qt. What I was pointing out is the fact that what will make Opengl fast (or not :)) is the number of triangles period. Not the fact that they're inside whatever signal based container.
Well, that's the point - OpenGL is optimized for GPU performance, Qt is optimized for high level portable API. If you provide a high level API to OpenGL (like for example Coin3D), it will make it slower. Just the same like comparing C++ code and equivalent assembly code - the latter will be faster.


You've got to accept that : if we admit that the performance of widgets was partly slow because of the interactions / updates with the window manager now that Qt 4.4 is using Alien windows/widgets it should definitely be faster. It's written on the blog you linked us to.
The keyword is "partially" - in what portion? My guess is that the slowdown is not caused by updates (because widgets are not visible, so they shouldn't receive paint events), but by repositioning them. I don't know why they get repositioned (one can always look into the source code), but looks like they are - without using layouts, by the way. Another guess is that using "uniformItemSizes" should improve the situation a bit. If you want - just take a profiler and see what takes more time.


Why would it be way faster in my case and not with the item view approach ? Layouts ? Well I'm using layouts.
If you scroll a scroll area, the position of widgets inside the viewport doesn't have to be recalculated (the viewport is simply moved). This is not the case with item views - you need to calculate and reposition each of the item widgets. At least that's my guess after talking to Marius during DevDays.


Yeah that's definitely why I kept my approach. Delegates are not of any use in my case since my approach is to base everything on a layouted QScrollArea of widget.
Great. So we shouldn't talk about item views if you shouldn't have used them in the first place. If you don't need a model - why use a view? If you need a model, but not a view of items, why use an item view? Use a scroll area with a hand written controller (like QDataWidgetMapper)translating between the scroll area and the model.



But, again, performance wise, if the QLayout was really optimized what would differenciate it from a listview of delegates ?
There is no listview of delegates - there is a single delegate which paints multiple items. And only paints those that are actually visible - that's the main difference. It doesn't paint widgets. These are handled by the view itself.


If you insert / remove items you still have to recalculate the position of your painting area.
Yes, but you don't move anything, just redraw the canvas.


Now I have to think about how to load/unload as fast as I can.
Think about just hiding widgets instead of destroying them. You can keep a pool of invisible widgets and show them on demand.


In my case I've come to the conclusion that since one user cannot see more than a given numbers of rows at a time, it makes sense to thread the adding / removing of widgets using signals.
You can't operate on widgets from worker threads. This will result in a segfault sooner or later. Regardless of using signals for communication. You'd have to end up with manipulating widgets from the main thread anyway, rendering the whole approach useless.


And no if it's written correctly it shouldn't crash anything.
I'm sorry, but until Qt5 comes out you are wrong. Widgets are not thread safe. Period.


Again, if it's well written I see no reason for that.
Just try it and you'll see (believe me, I tried it with Qt3 and it was crashing like hell). Widgets rely on data structures that reside in the main thread. They trigger events, which need to be handled in the main thread. They paint to the screen, which again needs to be done in the main thread to prevent race conditions and simoultaneous access to the "frame buffer". There are probably more reasons.


Maybe using QGraphicsView would be a middleway if the research going on around QGV-Layouts and QGV-Widgets bear fruit. I don't think this will be the case with Qt-4.4 but maybe Qt-4.5. Then we could have QPushButton clones with a similiar interface but made of standard QGraphicsItems providing both performance and ease of use, not to mention the eyecandy.
I don't think this is the way to go. The poster really needs just a "list" of widgets, so using "items" (be it graphics view items or itemviews item) is a workaround for a problem that has a direct solution - QScrollArea.