Qt: Sometimes wheelEvent() is not called immediately when rotating the mouse wheel - linux

I use Qt's wheelEvent to implement scrolling in a custom widget. Normally, this works fine.
However, when dragging the mouse and scrolling with the mouse wheel at the same time, it sometimes happens that rotating the mouse wheel by one step does not do anything at first (no wheelEvent is received). This might even randomly happen a few times successively. The next time the application does receive a wheelEvent, it comes with an accumulated event->angleDelta() of all previous events that were not reported separately. Apparently the events are buffered together in this case. Note that the wheelEvent may happen a long time after the initial mouse wheel rotation(s) in this case: it only happens once the wheel is rotated again.
This is an issue, since it means that my widget sometimes does not properly react to scrolling. Is there any way to avoid this kind of buffering and receive all wheel events immediately instead?
Notably this behavior is only well reproducible if at the same time dragging the mouse within the widget, which performs some non-trivial actions as a result. So I suspect it might happen if the event loop seems busy. But it certainly isn't busy enough to be unable to handle each wheelEvent. So I would like to disable this buffering.
I observe this on Linux with Qt 5.12.0. Any help is appreciated, thanks.

I am answering my own question here since I found the answer after writing it (but was unable to google it before).
The same problem has been reported here (with a somewhat non-descriptive title): Qt QOpenGLWidget wheelEvent strange behaviour
For me, setting Qt::AA_CompressHighFrequencyEvents (see https://doc.qt.io/qt-5/qt.html#ApplicationAttribute-enum) to false seems to have fixed the issue, as posted by #gatis paeglis as an answer to that question.

Related

PyQt5 stops updating GUI within 30...300 seconds after start

I develop a SCADA in python3 with the help of PyQt. I expect my program to continuously indicate various parameters received through RS-485 interface, however, within several minutes from the start (it is always a different time), the GUI stops to update itself. At the same time, the GUI stays responsive and if I were to click on one of the animated QAbstractButtons it has, the GUI starts to work as intended again for a short period of time. The problem occurs on both Linux and Windows.
The program has several worker threads: one for RS-485 exchange, one for reading/writing various data to disk, one for decoding received package and refreshing data in memory, one for request queue management, etc. They all work in loops of while (True): time.sleep(...) - do the job. The GUI is implemented in the main thread.
The data is indicated with QLabels. The QLabel.setText is added to painterEvent() of QWidgets containing the QLabels.
When the GUI stops updating, the other threads are up and running: the exchange is functioning, the request queue is forming, etc. Despite not being updated, the GUI stays responsive and reacts to QAbstractButton clicks.
I tried adding gui.update() or app.processEvents() into one of the worker threads, tried force updating through QTimer in the main thread. The result is the same: it works for a short while and then stops.
I tried increasing the time.sleep of the refresh thread and force updating the main widget over longer intervals of time (0.5 to 5 seconds) and it seems to help the situation a lot, this way it can run for several minutes, but it still does not solve the problem.
I would love to show the code, but the whole thing is way too bulky to post here and if I could narrow it down to a at least a hundred lines, I would have already solved the issue by now. So if any of you could at least share some general considerations on what to look for, I would be very happy.
update:
This seems to work, I'll leave it running for a few hours tomorrow to confirm:
update_timer = QTimer()
update_timer.setSingleShot(False)
update_timer.timeout.connect(self.gui.repaint)
update_timer.setInterval(500)
update_timer.start()
I assume that self.gui.update() did not work because dataChanged() was probably not emitted and control passed instead of doing repaint(). As far as I understand, the solution above is not the right way to update widgets.
So, the question actually boils down to the following:
What is the right way to update the main QWidget and how do I let the program know it needs to be redrawn, probably using dataChanged() signal?
The answer is simple. Never do anything related to PyQt from outside the main thread. Even if it seems the only logical way to do something.
After I 'fixed' the issue with the gui not being updated, I stumbled into a more serious problem: the program crashed every now and then. As it turns out, the GUI not being updated was only the top of the iceberg. The uptime was usually 2 to 7 minutes, I received a few glibc errors ("corrupted double-linked list" and "double free or corrution"). As it turns out, the source of the problem was inside refresh thread that has several hundreds of lines updating gui:
self.gui.screen_name.device_name.setCrash()
where setCrash() changes widget color and palette, or even more direct things like
self.gui.screen_name.label_name.setText(str(value))
I tracked down everything even loosely related to gui from the main file (the place where all the threading took place) and restyled it. Now it only has one worker thread - the RS-485, the rest are wrapped into separate QTimers.
This is what it was:
class Main():
def __init__():
self.plots_thread = Thread(target = self.plots_refresh)
self.plots_thread.start()
def plots_refresh():
while True:
time.sleep(0.5)
#do stuff
And this is what it is now:
class Main():
def __init__():
self.plots_refresh_timer = QTimer()
self.plots_refresh_timer.setSingleShot(False)
self.plots_refresh_timer.timeout.connect(self.plots_refresh)
self.plots_refresh_timer.setInterval(499) #prime number
self.plots_refresh_timer.start()
def plots_refresh():
#do stuff
This time the program has been running for over an hour and never crashed. After coming to conclusion that this was THE fix, I refactored the really messy test file and resumed testing - again, no troubles for half an hour.

How to keep PyQt GUI responsive when blocking tasks are GUI related?

I'm writing an app that embeds a bunch of matplotlib figures into a PyQt GUI. The updating of these figures can take up to a few seconds, so I would like to introduce a waiting indicator to display while the plots are being drawn. I've moved all the data processing code into its own thread, but it seems the actual plotting calls are often making up the majority of processing time.
I have written a waiting indicator that uses a QTimer instance to trigger paintEvent on the widget. This works just fine when all the intensive processing can be pushed into another thread. The problem is that these calls to construct the matplotlib plots cannot be moved outside of the main thread due to the way Qt is designed, and so block the updating of the waiting indicator, rendering it kind of useless.
I've introduced some calls to QCoreApplication.processEvents() after the updating of each figure, which improves the performance a little. I've also toyed with the idea of monkeypatching a bunch of methods of matplotlib.axes.Axes to include calls to QCoreApplication.processEvents(), but I can see that getting messy. Is this the best I can do? Is there any way to interrupt the main thread at regular intervals and force it to process new events?
It should also help a big deal to do the actual drawing on a QPixmap in a thread. Drawing that pixmap with QPainters drawPixmap() method is very fast. And you need to recreate the pixmap only when really needed (e.g. after Zooming or so). In the meantime you just have to reuse that already drawn pixmap. The actual paintEvents using drawPixmap() will cost close to nothing and your GUI will be completely responsive.
Clobbering the code with processEvent() is not only ugly but can cause very nasty and very hard to debug malfunctions. E.g. it might cause premature deletion of objects which are still in use but were scheduled for deletion using deleteLater().
This Answer might be also of use: Python - matplotlib - PyQT: plot to QPixmap
I havn't used matplotlib, yet. But in case it uses directly QWidgets and can not be used without it won't be so easy as you mentioned above. But you could do the drawing in another process started by your GUI which uses matplotlib as in the link above and stores the pixmap to disk and your gui loads whenever a new pixmap is ready. QFileSystemWatcher might help here to avoid polling.

How to avoid sublime text 2 freezing

Sublime text 2 is freezing from time to time, when I have many selections in large files. After a while it continues. I am on windows 2008.
For example it happens when I hit ctrl-d many times or even keep it pressed to add many selections. It adds selections, then stops, and after a while continues adding selections.
I suspect this is due to garbage collection.
Is there a way to avoid this or make the freeze period shorter? Maybe give it more memory, or even better less memory so gc is more frequent but much shorter.
Or maybe this is some sort of swapping or missing or wrong temp file config.
What is the reason and is there a good solution for it?
Sublime Text 3 may freeze too. Plugins can cause that.
First thing
...you may want to do is run command CTRL + SHIFT + P -> Plugin Development: Profile Events. You will get a list of events which different plugins are listening to and time it taken for them to process the event.
Give special attention to on_modified event. Quote from sublime docs:
Expensive operations in event listeners can cause Sublime Text to
become unresponsive, especially in events triggered frequently, like
on_modified() and on_selection_modified(). Be careful of how much work
is done in these and don’t implement events you don’t need, even if
they just pass.
This was my case: a plugin was meticulously processing every on_modified event which resulted in overflowing the thread queue and freezing ST.
Also
... don't forget that Sublime Text has a console where you sometimes can find useful messages and tracebacks. To reveal console use a key binding CTRL+`. Working for a while with your eye on the console may help you get the idea if something is going on wrong.
It recently starting freezing for me too, each time I'd re-focus on code in a Sublime window, whether that be alt-tabbing or even searching the command palette!
What fixed it up quickly for me was Reverting to a Freshly Installed State, then restoring those folders.
Update: It turned out to be Git Conflict Resolver
, perfectly responsive after disabling.
This question is old, and the issue did not occur with later versions anymore.
So I suspect it was fixed at some point.
If you still experience freezing in sublime text try the following:
Find out what version you are running and try switching to the latest stable version.
See if the freezing could be an operating system issue. This could be indicated by not only sublime text but at the same time all other applications freezing.
If you have a different solution or explanation why not add it here for reference.
I still believe the underlying reason could have been a garbage colletion (read at wikipedia about garbage collection) within the application.

glXSwapBuffers blocks until new X-events are fired

We have a really strange bug in our software. Call of glXSwapBuffers will block every now and then until some X-events are sent (mouse hovering over window/keyboard events). It seems that the bug is identical to Qt QGLWidget OpenGL rendering from thread blocks on swapBuffers() which was never properly solved. We have a same kind of situation.
In our application we create a multiple number of windows because our application needs to work with multiple screens. Each of our window is basically QWidget which has a class derived from QGLWidget as its only child. Each window has its own rendering thread attached which executes OpengGL-commands.
In this setting, application just halts every now and then. It continues normally if we feed X-events to it (moving the mouse over windows/push keyboard buttons). Based on the debugger info glXSwapBuffers() blocks somewhere inside the closed driver code.
We haven't confirmed this behaviour on NVidia cards, only with AMD-cards, and it is more likely to appear when using multiple AMD-cards. This suggests that the bug may come from the GPU-drivers.
I would like to know has any other bumped into this and has somebody even managed to solve this.

How to monitor screen updates?

I am trying to write a program that monitors when the screen has been redrawn.
Meaning if any part of any window is redrawn, then the program is notified.
As far as I understand I should use a journal record hook like at
http://www.vbaccelerator.com/home/vb/code/libraries/Hooks/Journal_Record_Hooks/article.asp
However, I do not understand which MSG type would get me the WM_PAINT events (WH_CALLWNDPROC and WH_CALLWNDPROCRET do not seem to do the job). I'm not even sure that WM_PAINT is what I'm looking for...
Basically, if I knew when the DC associated with GetDesktopWindow() has changed then my problem would be solved.
Question is: How do you monitor screen updates?
I don't believe this is possible without hooking the display driver. I can imagine there would be some serious performance implications if it were possible in general...
You would be better taking a screenshot every second or whatever. Every version of Windows has the little network icon in the tray always changing when you transfer data over a network, meaning the screen will be changing pretty much constantly.

Resources