How to keep GUI responsive when you need to call a slightly costly slot while mouse move
-
Hello, my problem is regarding keeping the GUI responsive when I need to call a slot while mouse move.
I have a QGraphicsView and I need to create and move some items on it in a slot which is called when I am panning the scene via mouse move. The thing is, this slot is a little bit costly time-wise although not so much, which causes the panning to stutter. The GUI does not become completely unresponsive but you can feel that panning is not as responsive as when you don't call that slot.
This slot does not need to be called immediately after every pixel move, I wouldn't mind if it followed the mouse a bit behind (although it should still somewhat follow the mouse).
What approach can I take to solve this problem? One approach I was thinking is to create a background thread in mouse move slot with idle priority, if there is not a background thread already running. But I create new items and move some of the items inside this slot which would need to communicate over signals in order not to do those things from the non-GUI thread. So things might get complicated.
Another approach I was thinking is maybe I can start a timer or reset it if there is already a timer every time mouse moves with about 0.01 seconds for example, which would call that slot at the end. So that when the user moves the mouse, the slot would get called only when the mouse stops for a while. The downside would be that if the user makes a continuous long move, the slot might get called very late, at the end of the mouse move.
Would there be a better/simpler approach? By the way I am using PySide, but I guess this applies to the general framework, not very language specific.
-
Hi,
Can you explain what operation(s) you are doing that takes that time ?
-
@SGaist said in How to keep GUI responsive when you need to call a slightly costly slot while mouse move:
Hi,
Can you explain what operation(s) you are doing that takes that time ?
So the application is like this: we have a QGraphicsView, and there are QGraphicsSimpleTextItem all over the scene, giving information about specific locations in the scene to the user. As the user pans around with the mouse, these TextItems move along with the scene. And I only give info to the user about the locations that are currently visible in the view otherwise I would have to draw thousands or even millions of TextItems. But since I only draw the locations that are currently in the view, I only draw about 100 TextItems.
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
And I also do some bounding box, calculations to center the TextItems at a position etc.
I can probably do other kinds of optimizations like creating the maximum number of TextItems that can be visible at any time in the beginning (this might be a bit hard to implement though), and just use those precreated objects; also I can calculate the possible TextItem dimensions beforehand, but I was wondering, what would be the way to go if this slot wasn't getting any faster.
-
@SGaist said in How to keep GUI responsive when you need to call a slightly costly slot while mouse move:
Hi,
Can you explain what operation(s) you are doing that takes that time ?
So the application is like this: we have a QGraphicsView, and there are QGraphicsSimpleTextItem all over the scene, giving information about specific locations in the scene to the user. As the user pans around with the mouse, these TextItems move along with the scene. And I only give info to the user about the locations that are currently visible in the view otherwise I would have to draw thousands or even millions of TextItems. But since I only draw the locations that are currently in the view, I only draw about 100 TextItems.
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
And I also do some bounding box, calculations to center the TextItems at a position etc.
I can probably do other kinds of optimizations like creating the maximum number of TextItems that can be visible at any time in the beginning (this might be a bit hard to implement though), and just use those precreated objects; also I can calculate the possible TextItem dimensions beforehand, but I was wondering, what would be the way to go if this slot wasn't getting any faster.
@canol said:
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
If you're doing all that work on your own why are you using QGraphicsView at all? You could just draw the items you need yourself in a paint event of some widget. Panning is usually implemented by changing the view's transform, not by moving items around. The scene has a graph that uses item bounding boxes to determine which of them are visible and the view only draws those. Dynamically removing and adding items sounds slow. Let the graphics view/scene do their job.
If there's really a lot of items I would group them in some areas and only add/remove them in large batches with a large bounding box (maybe spread across some time), but definitely not add/remove them one by one on every pan.
-
@canol said:
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
If you're doing all that work on your own why are you using QGraphicsView at all? You could just draw the items you need yourself in a paint event of some widget. Panning is usually implemented by changing the view's transform, not by moving items around. The scene has a graph that uses item bounding boxes to determine which of them are visible and the view only draws those. Dynamically removing and adding items sounds slow. Let the graphics view/scene do their job.
If there's really a lot of items I would group them in some areas and only add/remove them in large batches with a large bounding box (maybe spread across some time), but definitely not add/remove them one by one on every pan.
@Chris-Kawa said in How to keep GUI responsive when you need to call a slightly costly slot while mouse move:
@canol said:
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
If you're doing all that work on your own why are you using QGraphicsView at all? You could just draw the items you need yourself in a paint event of some widget. Panning is usually implemented by changing the view's transform, not by moving items around. The scene has a graph that uses item bounding boxes to determine which of them are visible and the view only draws those. Dynamically removing and adding items sounds slow. Let the graphics view/scene do their job.
If there's really a lot of items I would group them in some areas and only add/remove them in large batches with a large bounding box (maybe spread across some time), but definitely not add/remove them one by one on every pan.
I don't move items manually when panning, I have to update their positions though since the information font changes as the view changes and I need to recalculate the center of the TextItems, so I only update the position of the item since the text inside it changes. And I of course use transformations for panning, zooming etc :)
Also the reason I was dynamically adding removing TextItems is because if I add all of them to the view it will probably take a considerable time/memory because in fact every pixel has an information text, so if a 5000x5000 scene is shown, I would need to create 25 million TextItems, whereas you can only fit about 100 pixels to the view (it is zoomed in).
By the way, I was playing with my code and I suspect the slow down actually happens because of text rendering and not the calculations, because when I don't show the TextItems but still do all the calculations, it is still very fast. When I actually show the TextItems, then it slows down...
-
@Chris-Kawa said in How to keep GUI responsive when you need to call a slightly costly slot while mouse move:
@canol said:
So as the user pans around, the TextItems that are already in the view have their coordinates updated, the TextItems that go out of the view are deleted, and the locations that get into the view are added to the scene as new TextItems.
If you're doing all that work on your own why are you using QGraphicsView at all? You could just draw the items you need yourself in a paint event of some widget. Panning is usually implemented by changing the view's transform, not by moving items around. The scene has a graph that uses item bounding boxes to determine which of them are visible and the view only draws those. Dynamically removing and adding items sounds slow. Let the graphics view/scene do their job.
If there's really a lot of items I would group them in some areas and only add/remove them in large batches with a large bounding box (maybe spread across some time), but definitely not add/remove them one by one on every pan.
I don't move items manually when panning, I have to update their positions though since the information font changes as the view changes and I need to recalculate the center of the TextItems, so I only update the position of the item since the text inside it changes. And I of course use transformations for panning, zooming etc :)
Also the reason I was dynamically adding removing TextItems is because if I add all of them to the view it will probably take a considerable time/memory because in fact every pixel has an information text, so if a 5000x5000 scene is shown, I would need to create 25 million TextItems, whereas you can only fit about 100 pixels to the view (it is zoomed in).
By the way, I was playing with my code and I suspect the slow down actually happens because of text rendering and not the calculations, because when I don't show the TextItems but still do all the calculations, it is still very fast. When I actually show the TextItems, then it slows down...
@canol said in How to keep GUI responsive when you need to call a slightly costly slot while mouse move:
when I don't show the TextItems but still do all the calculations, it is still very fast. When I actually show the TextItems, then it slows down...
In that case the next thing to check would be to try and display simple rectangles instead of text. If that is still fast enough you could try to render the text onto a pixmap and use a pixmap item, which should be faster than text rendering.