I have my GUI Code (PyQT5) in the main thread of my program. All the logic I have in a second thread because otherwise my GUI would freeze.
My problem is that I dont understand how to connect these thread together so that I can update my progress bar or my text every second as example.
What I tryed:
Reading the whole documentary about QThread, threading.Thread, sender, emit, pyqtSignal, ...
Read everything about stackoverflow about that topic, there were some really good explanations but everything for PyQT4 and there were made some changes since then.
I could paste some of my crap code in here but I dont have a code which is working. I just want to understand how to connect these signals together so that I can update any Widget dynamically.
Related
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.
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.
We have a WinForms c++/cli CAD based application that uses VTK 6.1. One of the application's features is playing a script which plays back operations the user had previously done interactively. We pop a modal progress form while the script is playing and do the non-ui work of the script in a background thread. Part of the work of the background thread is creating and deleting vtkPolyDataMappers. While the background thread is going and the progress form is showing we need to update a display in the main thread by calling vtkWin32RenderWindowInteractor::Render().
We have a timer setup so Render is called every few hundred milliseconds at most in a UserControl::OnPaint event handler. This allows the view to update while the script is playing giving the user feedback.
This used to work in VTK 5. But now an infinite loop happens upon deletion of a vtkPolyDataMapper in the background thread. The infinite loop is in vtkClearOpenGLErrors:
void vtkClearOpenGLErrors()
{
while (glGetError()!=GL_NO_ERROR){;}
}
Inside the vtkpolydatamapper is a vtkOpenGLDisplayListPainter. When this gets deleted by vtkGarbageCollectorImpl::CollectInternal we get stuck in vtkClearOpenGLErrors.
Does anyone have experience with VTK and threading that could help? Do you know anything about this? It's only an apparent problem in VTK 6.1. Is it illegal to have the main UI thread calling Render on a vtkWin32RenderWindowInteractor while a background thread is doing deletes on a vtkPolyDataMapper? It isn't a timing issue. I think it might be an OpenGL context issue but not sure how to fix it. The problem does go away if we avoid calling Render on the display while the background thread is going but we'd like to give the user feedback while the script is playing.
We fixed the problem by recompiling VTK with
VTK_REPORT_OPENGL_ERRORS
turned off.
I need to implement a form with buttons and log widget, and when button is pressed, some long job should start in the other thread.
Details of the process should be echoed to the log.
Two jobs should not be started simultaneously.
The way I'm thinking about: create subclass of QThread with its own event loop (of course call moveToThread(this) in the QThread's constructor), and connect signal of QButton to the slot doLongJob() of this thread.
Does it sound good, or is it generally wrong? Other way: say, create new thread every time user pressed button. Seems to be worse.
Is there some best practices to do this pretty common thing? Please give me a suggestion.
Bard already answered in the comment, I asked him twice to write the same as an answer, but it seems he really doesn't want to.
So, I'm tired of waiting more than 7 months, and therefore have to do that myself, in order to get rid of this unanswered question.
The answer is:
You generally do not want to subclass QThread, but other than that, hooking up things with signals and slots is just fine.
I am trying to implement the Observer design pattern with wxPython.
I have a modelling application that computes vast amount of data in the background. Sometimes I would like to display the output of the model in the GUI---which is just a grid of squares of different colours. Other times I need to do the computation without displaying the GUI.
The advantage of the observer pattern is that you can plug in or not a GUI just by adding or removing one line of code, something like
self.observers.append(MyWxGui())
or similar.
Now, to do that I need my computation to run on one thread, and the wx GUI to run in a different one.
I tried doing this with wxPython but I always get a Fatal I/O error:
python: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.
I read tutorials on multithreading in wxPython, such as http://wiki.wxpython.org/LongRunningTasks, but they all have the Mainloop() running in the main thread and than the long running task in a secondary thread, while I need it to be the other way round. This is because if I have the Mainloop() in the main thread, the program hangs waiting for some event from the GUI, instead of proceeding with the computation.
I also saw that I cannot manipulate Device contexts (DCs) such as ClientDC or PaintDC in a sub-thread, but I'm running the entire wx code inside the same thread.
Can the Mainloop() and all the wx GUI be run in its own thread that is not the main application's one?
Running wxPython 2.8.11.0 on Ubuntu 10.10 maverick.
If you read that wiki page, then you should know that you can communicate back to the wx thread using wx.CallAfter, wxCallLater or wx.PostEvent in a thread-safe manner. I have a simple tutorial here:
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
Personally, I would use something like Pubsub + one of the threadsafe methods mentioned above to communicate with the wx MainLoop. The nice thing about Pubsub is that it can listen for messages and react to them appropriately. The example above actually shows one way to do just that. Hopefully that will help you. Otherwise, I highly recommend joining the wxPython mailing list and asking there: http://groups.google.com/group/wxpython-users/topics?pli=1