Connecting, Reconnecting slots to a signal - Can this cause a crash? - pyqt4

In a PyQt4 app I connect a slider to one or another slot depending on user actions. I change the slot assignment in response to button clicks. The several different slots do different things, too different to merge into one slot with some "if" statements.
This app crashes frequently. I am wondering if it's kosher to be reassigning slots to one signal on the fly like this. Could this lead to dangling pointers internal to PyQt4, memory corruption, or something else bad? If what I'm doing is okay, I will look elsewhere for the bug.
Here's a very sketch version of my app:
def MyThing(QWidget):
(in some approprate place)
self.s = QSlider()
def MethodOne(self): # this is a slot handling a button click
self.s.valueChanged.connect(self.someslot1)
def MethodTwo(self): # likewise
self.s.valueChanged.connect(self.someslot2)
def someslot1(self, svalue): # slider movement handler
(compute, show info based on slider position)
def someslot2(self, svalue): # similar to someslot1
....

If the 2 connected objects still exist when you call connect, that shouldn't cause a crash by itself.
According to your code, you don't seem to disconnect the previously connected slots (with self.s.valueChanged.disconnect() for instance), and that can cause both slots to be called multiple times for each signal, which means that your application could slow down with time, and might crash depending on what you do in the slots.

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.

Is it OK to read Qt Widgets from another thread?

I found many answers say that accessing Qt Gui Widgets from another thread is not safe. I agree with this if we try to modify the widgets. But what if I only read the value of widget, without any modification?
I have designed a GUI tool using Qt, and my working thread reads Widgets directly, for example, get the text of QLineEdit, get the value of QComboBox,etc. And I haven't found any problem.
I pass the pointer of MainWindow to working thread.
Is this really not safe?
First: It is always a bad idea to read properties from another thread without protecting the memory (Mutex, Signal & Slot).
Your situation: If nothing wants to change the value, you should be fine. There are no problems if every thread only reads the value. But if you change the value of the QComboBox (for example) by clicking arrow up, arrow down or selecting a new item (what I expect you will do, because that's the reason of using a QComboBox) the value will be written and then the application can crash if your worker thread wants to read the value in the exact same moment (the possibility of a carsh depends on the frequenz your thread pulls the informations).
Your application never crashed because the condition never appeared.
But that does not mean that this can not happen.

python program using Glade, GObject, runs fine for days, then suddenly all windows are blank

I have a large data acquisition and control program written in Python3.4.2 using GUI mostly developed on Glade 3.18.3, Gtk3.0 GObject running Debian 8 with XFCE.
There are timers that keep doing things, and these work fine. After startup, the program runs for some 3 - 7 days, then suddenly, all of the windows go blank and stay blank. Other applications are not affected. Memory and CPU usage is modest.
There are no indications of problems prior to the windows going blank. The windows show their title bars and respond normally to minimize, restore, move to another Workspace, etc. It looks like they are not getting repainted - no widgets are visible at all. The code is way too large to post here, and I am not able to isolate a specific problem area for lack of obvious symptoms other than the blank screens. There are no error messages or warnings.
The timers continue to run, acquire data and control things. This happens whether the program is run from the command line or under PyDev in Eclipse.
Things I have tried:
In the main timer loop, I put code to look for a file, and then exec the command in it, printing the results, so I have been able to mess with the program in real time:
Replace the usual Gtk.main() with a while loop whose variable, if not made false, will re-execute the Gtk.main(). Executing Gtk.main.quit() stops Gtk.main and starts it again. Windows still blank. Did this repeatedly to no avail.
Experimented with garbage collection with GC. Collecting garbage makes no different. Windows still blank.
Put in code to print percent of time consumed by the timer loops. Fairly steady around 18 - 20% of available CPU time, so nothing is hogging the CPU preventing re-paint.
I have a button that clears a label. I read the label, then executed a builder.get_object(...).activate command to the button. I re-read the label and it was now properly blank. So events and widgets appear to be working normally, at least to some extent.
Finally, if I click on the close X on the title bar, XFCE asks me if I want to wait or close now. So it seems as though there may be a disconnection or problem with signals and the OS, even though XXX.activate() works.
Web searching is in vain. Does anybody have ideas of what might be happening, useful diagnostics, or other suggestions? Many thanks!
April 27, 2017 Update:
I have taken two substantial steps to mostly work around problems. First, partly in response to a couple of Gtk crashes over the last few months, instead of ending the main program with:
Gtk.main()
I end with:
while wannalive:
try:
Gtk.main()
except:
pass
wannalive is True until user does a quit, so recovery is instant.
Second, I grouped all of the code for each window setup and initial population of static items into two functions. I also made another function for closing a window. These functions propagate to children, grandchildren windows. A function in the top window first, closes, then re-creates all windows, with one call. In operation, there are overlaps in what windows exist, but that is not a problem.
Above, I describe that I can inject code with an external program. The external program has a button that injects a call to that third function. In about five seconds or less, the result of a single button click is to replace all blank windows with functional windows. For my purposes in a controlled environment with a trained operator, this is acceptable.
Next, let me address the relationship between the timer loops and GUI events processing. I do use GObject.timeout_add(ms, somefunction). Experiment shows that a button that calls time.sleep(5) stalls the timer. Experiment shows that injecting time.sleep(5) in the timer loop stalls the GUI. This is consistent with my belief (correct me if I am wrong) that Python runs on a single thread. Therefore, bad code caught in an infinite loop should stall both the GUI and the timer loop. (The program has one timeout_add call.)

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.

QAbstractItemModel Lazy Loading locks application

I have implemented canFetchMore, hasChildren and fetchMore in order to allow my model to be lazy loaded. It's very simple and based on QT's: http://doc.qt.io/archives/qt-4.7/itemviews-simpletreemodel.html
My problem is that in my application fetching children is not a very quick operation, it involves a few seconds of delay on the server side while it figures out who the children actually are.
I'm unsure how to deal with that. I can't have my application locking up for several seconds every time someone expands a node. I don't know how to go about making this happen in the background. If I was to create a sub-process or thread to actually do the work of retrieving the children and updating the client side data structure, how would I go about telling the model that this had successfully completed (and for the node to finally expand).
Also, is there a way to show that the node is currently in the process of loading the data in the background?
Apologies if these are stupid questions, GUI programming is still a bit of a mystery to me and I've never used QT before.
For the record, I'm using Python, but if answers are given in C++ I can understand them.
Thanks
If I was to
create a sub-process or thread to actually do the work of retrieving
the children and updating the client side data structure, how would I
go about telling the model that this had successfully completed (and
for the node to finally expand).
You can use signal and slots. In the thread you retrieve the data you will emit a custom signal like someDataAvailable(YourdataType) and then in the gui you will handle this signal with a slot something like handleDataReadySignal(YourdataType). The signal passes the object that you give it when emitting. Apparently you need to update the gui and the list in the handleDataReadySignal slot. Of course you need to connect the slot to the signal preferably in the constructor of the window/dialog to which the list is attached

Resources