I have two UI windows created with QT Designer. I have two separate python scripts for each UI. What I'm trying to do is the first script opens a window, creates a thread that looks for a certain condition, then when found, opens the second UI. Then the second UI creates a thread, and when done, opens the first UI.
This seems to work fine, here's the partial code that is fired when the signal is called:
def run_fuel(self):
self.st_window = L79Fuel.FuelWindow(self)
self.thread.exit()
self.st_window.show()
self.destroy()
So that appears to work fine. I am still unsure of the proper way to kill the thread, the docs seem to state exit() or quit(). But...the new window from the other script (L79Fuel.py) is shown and the old window destroyed.
Then the new window does some things, and again when a signal is called, it triggers an similar function that I'd like to close that window, and reopen the first window.
def start_first(self):
self.r_window = L79Tools.FirstWindow(self)
self.thread.exit()
self.r_window.show()
self.destroy()
And this just exits with a code 0. I stepped through it with a debugger, and what seems to be happening is it runs through start_first, does everything in the function, and then goes back to the first window's sys.exit(app.exec_()), does that line, and then loops back to the start_first function (the second window) and executes that code again, in a loop, over and over.
I'm stumped. I've read as much as I could find, but nothing seems to address this. I'm guessing there's something I'm doing wrong with the threading (both windows have a thread going) and I'm not killing the threads correctly, or something along those lines.
Only the main thread can create GUI elements. You won't be able to create a second window in another thread. It's hard to see exactly how your program is set up based on the two callback slots in your question.
Generally, you kill threads with quit
self.thread.quit()
If you want, you can wait until it full quits and then delete the reference
self.thread.wait()
self.thread = Nonee
The answer? self.hide()
I was doing self.destroy on the windows to get rid of them, but what I didn't realize was that also was causing the program to start the process to exit completely. So it seems the answer is to kill the thread, and then simply .hide() the window. Now, each window opens and closes, multiple times if needed, while starting and stopping threads. New code (each function is essentially the same) below:
def run_race(self):
self.thread.stop()
self.thread.wait()
self.thread = None
self.r_window = L79Race.RaceWindow(self)
self.r_window.show()
self.hide()
While the threading wasn't the problem, I thought it was better to completely kill the thread before creating the next window. The self.thread.stop() is shown below. I realize .terminate() is not recommended, but I'm not really writing or trying to save anything in the thread, so I'm not worried about when it closes, only that it does.
def stop(self):
self.terminate()
Related
I have an application that is doing some data processing in its main thread. So far it was a pure console application. Now I had to add a QT App for visualization purpose and did this as a separate thread.
If the QT Window is closed, the main thread of course still runs. How could I terminate the main thread once the window is closed?
class Window(threading.Thread)
def __init__(self, data_getter):
super(Window, self).__init__()
self.getter = data_getter
def update(self):
data = self.getter()
#update all UI widgets
def run(self):
app: QApplication = QApplication([])
app.setStyleSheet(style.load_stylesheet())
window = QWidget()
window.setWindowTitle("Test Widget")
window.setGeometry(100, 100, 600, 300)
layout = QGridLayout()
self.LABEL_state: QLabel = QLabel("SM State: N/A")
layout.addWidget(self.LABEL_state)
window.setLayout(layout)
window.show()
timer = QTimer()
timer.timeout.connect(self.update)
timer.start(1000)
app.exec_()
class Runner:
def __init__(self)
pass
def data_container(self):
return data
def process_data(self):
#do the data processing
def main():
runner: Runner = Runner()
time.sleep(1)
w = Window(runner.data_container)
w.start()
while True:
runner.process_data()
time.sleep(2)
if __name__ == "__main__": main()
The best idea I had is to give Window another function reference of Runner that is then registered inside Window to atexit and would set a termination flag that is frequently checked inside the main process (Runner). Is there a better approach? I know it migth be better to have the QApp run as the main process, but I'd like to not have to do that in this case.
There are basically two questions here: synchronising an event accross two threads, and stopping a running thread from outside. The way you solve the latter problem will probably affect your solution to the former. Very broadly, you can either:
poll some flag inside a main loop (in your case the while True loop in main would be an obvious target, possibly moving the logic into process_data and having it run to completion), or
use some mechanism to stop the containing process (like a signal), optionally registering cleanup code to get things into a known state.
In either case you can then design your api however you like, but a .stop() or .cancel() method is a very normal solution.
The trouble with relying on polling is that the worse case response time is an entire cycle of your main loop. If that's not acceptable you probably want to trigger the containing process or look for ways to check more frequently (if your process_data() takes << 2s to run, replace the sleep(2) with a looped smaller delay and poll the flag there).
If stopping by setting a flag isn't workable, you can trigger the containing process. This normally implies that the triggering code is running in a different thread/process. Python's threads don't have a .terminate(), but multiprocessing.Processes do, so you could delegate your processing over to a process and then have the main code call .terminate() (or get the pid yourself and send the signal manually). In this case the main code would be doing nothing until signalled, or perhaps nothing at all.
Lastly, communication between the graphical thread and the processing thread depends on how you implement the rest. For simply setting a flag, exposing a method is fine. If you move the processing code to a Process and have the main thread idle, use a blocking event to avoid busy-looping.
And yes, it would be easier if the graphical thread were the main thread and started and stopped the processing code itself. Unless you know this will greatly complicate things, have a look at it to see how much you would need to change to do this: well designed data processing code should just take data, process it, and push it out. If putting it in a thread is hard work, the design probably needs revisiting. Lastly there's the 'nuclear option' of just getting the pid of the main thread inside your window loop and killing it. That's horribly hacky, but might be good enough for a demonstration job.
I have a thread that is running and a 'stop thread' button which when clicked calls a method that calls the terminate() of the thread. I have a QMessage Box that confirms the stop button before it terminates. The functionality is as expected but if I then start the thread again and click the close button then I get the confirmation MessageBox, I then select confirm and the thread stops but another confirmation box appears. It continues in this manner - if I run it 3 times I have to confirm three boxes, 4 I get 4 etc. The first box will terminate the thread in all cases. It seems like the MessageBox is not being destroyed properly each time it appears so they just build up? Not sure what's going on.
See below code. I have a thread 'my_thread' and a button connected to the function 'stop_thread' outlined below.
def stop_thread(self):
prompt=QWidgets.QMessageBox.question(self, 'Stop!', 'Are you sure you want to stop thread?', QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if prompt == QtWidgets.QMessageBox.Yes:
self.my_thread.terminate()
else:
pass
I would expect each time I start and then stop the thread using the button I get one confirmation box appearing and upon selecting 'Yes' the thread will terminate. In reality the thread terminates but I get multiple MessageBoxes appearing one after the other - the number of them corresponds to how many times I have started the thread.
Solved it - It was because I was connecting the stop_button to the function outlined above in the function that I used to start the thread. So every time I started a thread I connected it again and called the function an extra time. What I didn't know was you could connect buttons to a function more than once (or multiple functions) but you learn something new everyday.
To fix I moved the connection of the stop button to the stop_thread function to the main class and now works great.
I want to know if there is an option to know when a task is added/appended to Queue in python.
I have an application where some process runs in thread. depending on the condition a function is called from that thread. the called function will display a window.
currently its working and the window is getting displayed, my main issue is the application gets hanged when the window is displayed. I think this is due to the call of the function which displays the window was with in the thread.
So from my search some one suggested me to use Queue in python.
But when i go through python example shows that a infinite loop will be running in main thread.
If thats the case then my entire app will be in infinite loop.
You didn't specify the exact GUI framework you're using.
If you're using wxPython you can use wx.CallAfter to call a function from another thread:
def ShowWindow(txt):
wx.MessageBox(txt)
From another thread:
wx.CallAfter(ShowWindow,'message from another dimension')
This won't cause your main loop to hang.
I'm just getting started with Tk and using it in python. I set up a button that does a ton (like two minutes worth) of stuff behind the scenes when you press it. I also set up a status text to show what's going on while this is happening. I set it up like this:
status_text = Tkinter.StringVar()
ttk.Button(mainframe, text="Stats!", command=go).grid(column=1, row=4)
def go(*args):
status_text.set("Logging in...")
do_lots_of_stuff()
status_text.set("Doing stuff #1...")
do_even_more_stuff()
status_text.set("Success!")
The problem is that when you press that button the window freezes, and the status text never actually changes. It looks broken, and doesn't come out of this state until all the processing finishes 2-3 minutes later. How do I make it not freeze and update the status text?
It's time to learn multithreading!
What's happening is that the GUI (main thread) is waiting for the method to return so that it can continue updating the interface.
You'll want to cause the action of a button to spawn a threading.Thread instead of running the heavy code in the main thread. You'll also want to create a Queue to access the data from the other thread (since sending GUI requests should ONLY be made from the main thread).
import threading, Queue #queue in 3.x
my_queue = Queue.Queue()
def go(*args):
my_thread = threading.Thread(target=function_that_does_stuff)
def function_that_does_stuff():
my_queue.put("Logging in...")
do_lots_of_stuff()
my_queue.put("Doing stuff #1...")
do_even_more_stuff()
my_queue.put("Success!")
Then you'll need a function that is run when the update event happens.
def OnUpdate(*args):
try:
status_text.set(my_queue.get())
except Queue.Empty:
pass
If you have control of do_lots_of_stuff and you can break it into small chunks, you can place small jobs on the event queue to do each chunk.
For example, if you're do_lots_of_stuff is reading lines from a file, create a method that reads one line and then puts a job on the event queue to call itself after a ms or two. This way the event loop continues to run, and your code does a little processing on each iteration. This is a surprisingly effective technique, though it only works if you're able to break your 'lots of stuff' into atomic chunks.
If you can't do that you'll have to either use multiple threads or multiple processes. I personally prefer the latter -- multiprocessing code is (arguably) less difficult to write and debug.
I'm attempting to build a very simple wxPython GUI that monitors and displays external data. There is a button that turns the monitoring on/off. When monitoring is turned on, the GUI updates a couple of wx StaticLabels with real-time data. When monitoring is turned off, the GUI idles.
The way I tried to build it was with a fairly simple Python Thread layout. When the 'Start Monitoring' button is clicked, the program spawns a thread that updates the labels with real-time information. When the 'Stop Monitoring' button is clicked, thread.join() is called, and it should stop.
The start function works and the real-time data updating works great, but when I click 'Stop', the whole program freezes. I'm running this on Windows 7 64-bit, so I get the usual "This Program has Stopped Responding" Windows dialog.
Here is the relevant code:
class MonGUI(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
...
... other code for the GUI here ...
...
# Create the thread that will update the VFO information
self.monThread = Thread(None, target=self.monThreadWork)
self.monThread.daemon = True
self.runThread = False
def monThreadWork(self):
while self.runThread:
...
... Update the StaticLabels with info
... (This part working)
...
# Turn monitoring on/off when the button is pressed.
def OnClick(self, event):
if self.isMonitoring:
self.button.SetLabel("Start Monitoring")
self.isMonitoring = False
self.runThread = False
self.monThread.join()
else:
self.button.SetLabel("Stop Monitoring")
self.isMonitoring = True
# Start the monitor thread!
self.runThread = True
self.monThread.start()
I'm sure there is a better way to do this, but I'm fairly new to GUI programming and Python threads, and this was the first thing I came up with.
So, why does clicking the button to stop the thread make the whole thing freeze up?
In wxPython, GUI operations need to take place in the main thread. At places in your code you are calling the GUI from a different thread.
The easiest solution is to use wx.CallAfter(). A line of code would look like
wx.CallAfter(self.button.SetLabel, “Start Monitoring”)
which will then call self.button.SetLabel(“Start Monitoring”) from the main thread after the function completes.
There are other ways around this as well, such as using a Python threading Queue or wx.PostEvent, but start with CallAfter because it's easiest.
Other issues are also relevant, like you can't restart the same thread, but using CallAfter will stop the crashing.
It's likely hanging on join([timeout]), which blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception – or until the optional timeout occurs.
Do you have some inner loop in your thread, or a blocking call that waits for some source of data that may never come? When I wrote a basic serial program that grabbed COM port data, it would sometimes hang because a read function in my thread would block until it got something.
I would sprinkle in a few debugging print statements to see whats happening.
Edit:
I'd also use a threading.Event() instead of a Boolean flag, e.g.:
# in the init code...
self.runThread = threading.Event()
# when starting thread...
self.runThread.set()
self.monThread.start()
# in the thread...
while self.runThread.isSet():
pass # do stuff
# killing the thread...
self.runThread.clear()
self.monThread.join()
This shouldn't make it work differently, but it's a slightly safer way to do it.
tom10 has the right idea with avoiding UI updates from the monitor thread.
Also, it is probably not a good idea to have the blocking call self.monThread.join() in your UI thread. If you want the UI to give some feedback that the monitor thread has actually ended, have monThreadWorker issue a wx.CallAfter() or wx.PostEvent() just before it closes.
Avoid anything that blocks in your UI thread, and you will avoid deadlocking the UI