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.
Related
i made a little GUI to take input from the user one of the main process is withing a thread and since that command starts there's an event that triggers an endless progress bar to simulate waitting time.
But i want to make it really usefull, but i can't find a way stop the progressbar when the process is done.
so any help on that, would be great.
You can place a return statement in the code that will be hit when the thread is done
Currently I am making a very simple interface which asks user to input parameters for a test and then run the test. The test is running brushless dc motor for several minutes. So when the run button is pressed the button is engaged for the time period till the function is finished executing. I have another stop button which should kill the test but currently cant use it since the run button is kept pressed till the function is finished executing and stop button cant be used during the test. I want to stop the test with pressing the stop button even if the run button function is currently being executed. The run button should release and the function should continuously check the stop function for stopping the test. Let me know how this can be executed.
Your problem is that all your code it taking place sequentially in a single thread. Once your first button is pressed, all of the results of that pressing are followed through before anything else can happen.
You can avoid this by running the motor stuff in a separate thread. Your stop button will then need to interrupt that thread.
I'm new to event-driven programming and I would really like to understand better what happens under the hood, when the CPython interpreter goes through the code line by line. So far I have only programmed sequentially, and I have a fairly good idea in mind how the interpreter converts my code into bytecode and then goes from one statement to the next and executes the commands.
But for event-driven programming, I'm totally confused how the interpreter works.
In particular, I'm confused
how the interpreter knows where to jump next in the source code
also how function handlers are called when certain events happen
how the refresh rate of the event loop is handled: Is actually all the code of the function handles run thousand of times per second, but not executed because some kind of "event-has-not-happened" flag say "don't execute this function now"?
To make this discussion more concrete, could you illustrate these points on the following example, taken from this site:
from Tkinter import *
ROOT = Tk()
def ask_for_userinput():
user_input = raw_input("Give me your command! Just type \"exit\" to close: ")
if user_input == "exit":
ROOT.quit()
else:
label = Label(ROOT, text=user_input)
label.pack()
ROOT.after(0, ask_for_userinput)
LABEL = Label(ROOT, text="Hello, world!")
LABEL.pack()
ROOT.after(0, ask_for_userinput)
ROOT.mainloop()
Ideally I'd like an explanation in a similar spirit to this article, where it is brilliantly explained from the point of view of how the CPython interpreter works, why some statements are thread-safe and some not, and how thread-safety is achieved.
All that an event loop does is call other functions when an event takes place. The graphical subsystem helps out here, by signalling to the event loop that events are waiting to be processed.
Events like keyboard input and mouse interactions (moving the pointer, clicking) are all handled by the graphical subsystem (GUI) and the operating system (OS). Keyboards and mice are hardware devices, and computers use interrupts to record their state for the GUI to pick up.
If you don't touch your keyboard or mouse, an event loop can just do nothing; the loop blocks, and the OS will execute other processes as the loop has signalled it is waiting for something to happen. The OS is in control at this point, the process is not given any CPU time and other processes run instead. Once something happens, then there are events in the queue and the OS can resume the process. Imagine a function call in the event loop that asks if there are more events, and that call won't return until there are.
Once the loop resumes, there are events in the queue to process ('mouse position is now x, y', 'the keyboard input queue contains the characters F, O, and O'). Each event can trigger code you wrote, and registered to be run on that event. For example, you can register a handler to be run when a button is clicked; the event framework has a registry that if the conditions are right ('mouse button click' event happened, cursor is at the right location on the screen, button is active and visible) so knows to call your custom event handler.
Such an event handler is entirely synchronous, if the handler takes a long time to complete you'll notice that your GUI 'freezes', does nothing else, as Python is too busy running that one handler. The usual work-around is to use threads in that case; your event handler quickly starts a separate thread to do the real work, and returns. That way the main thread (with the event loop) can handle the next event, while the OS switches between the work in the extra thread and the main thread.
As to the specific piece of code you posted, that's actually not a very good example to use. It actively ignores GUI input, using the raw_input() function to capture keyboard input from the console instead. The GUI is entirely blocked every time the function runs!
The ask_for_userinput() function is an event handler, it is registered as one with the after() method. after() uses a timer interrupt, (usually implemented with a SIGALRM interrupt) to be called after at least 0 seconds have passed (so as soon as possible, really). Each time it is called it adds a new label to the GUI (just a piece of text) and re-schedules itself. It is not very interesting!
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()
I'm writing a simple GUI for the Lights Out Board (Wiki) using PyQt4. The GUI has a 'Solve' Button which uses Iterative Deepening Depth First Search. This being time consuming, I spawn a new QThread to solve for the puzzle and update the board when it finishes while the GUI remains responsive. This I'm able to do.
But, I also have a 'Stop' Button which should stop the Search thread if it is currently running and I'm unable to stop the QThread using the exit(). Here's the code for the three functions.
class LightsOut(QWidget):
def __init__(self, parent=None):
# Whole other initialization stuff
self.pbStart.clicked.connect(self.puzzleSolver) # The 'Start' Button
self.pbStop.clicked.connect(self.searchStop) # The 'Stop' Button
def searchStop(self):
if self.searchThread.isRunning():
self.searchThread.exit() # This isn't working
self.tbLogWindow.append('Stopped the search !') # This is being printed
else:
self.tbLogWindow.append('Search is not running')
def searchFinish(self):
self.loBoard.setBoard() # Redraw the lights out board with solution
def puzzleSolver(self):
maxDepth = self.sbMaxDepth.value() # Get the depth from Spin Box
self.searchThread = SearchThread(self.loBoard, self.tbLogWindow, maxDepth)
self.searchThread.finished.connect(self.searchFinish)
self.tbLogWindow.append('Search started')
self.searchThread.start()
When I click the 'Stop' Button, in the Log Window (QTextBrowser), I can see 'Stopped the search' message, but my CPU is still running at 100% and when the search finishes, the solution is being displayed (searchFinish is being called). Obviously, I'm missing something very simple, and I haven't used terminate() as it is frowned upon in the documentation.
Use terminate() instead of quit and call wait(). wait() will block until the QThread has finished.
Another thing you could do is to set a quit condition outside the thread which you will check for inside the thread (Probably the best solution). Additionally you can connect a slot to the finished signal.