How to correctly stop pyqt5 QThread? - python

There is a program for pyqt5. It has a thread (qthread) in which you need to perform some action with a certain front end. I used QThread.msleep to define this period, and the flag that came out of the while cycle.
But if the period is long, then it will take a long time to wait for the exit from the loop, and I want to finish it as soon as I click on the button in the GUI (but not kill, start again soon).
The source code is pretty standard here, I need ideas.
An example of an exaggerated class for a separate thread.
class intrhead(QObject):
...
def run(self):
while(flag):
...
QThread.msleep(period)

The correct solution is to split the period.

Related

How to print in PyQt5 GUI program while the main window is running?

Currently i am working on a GUI program in PyQt5. My program has several steps which run after each other. After finish each step i want to print for example Step1 finished and then Step2 finished and so on. Since the main window freezes the print does not work.
It would be very helpful if you give me some solutions.
A simple solution would be a thread based solution, where you set an event flag after finishing the task:
import threading
done_flag = threading.Event()
def print_status():
while True:
done_flag.wait()
print("I'm done!")
done_flag.clear()
Inside your function, you have to call .set_flag. This functions blocks until your function have finished (if you always call set_flag).
It's by the way always good practice to use threads in GUI applications. This avoids freezing cause the main loop etc. cannot run.
By the way, instead of raw printing, I would suggest using logging which gives more useful information.

How to run tasks periodically without interrupting the whole program

I have a program that constantly runs if it receives an input, it'll do a task then go right back to awaiting input. I'm attempting to add a feature that will ping a gaming server every 5 minutes, and if the results every change, it will notify me. Problem is, if I attempt to implement this, the program halts at this function and won't go on to the part where I can then input. I believe I need multithreading/multiprocessing, but I have no experience with that, and after almost 2 hours of researching and wrestling with it, I haven't been able to figure it out.
I have tried to use the recursive program I found here but haven't been able to adapt it properly, but I feel this is where I was closest. I believe I can run this as two separate scripts, but then I have to pipe the data around and it would become messier. It would be best for the rest of the program to keep everything on one script.
'''python
def regular_ping(IP):
last_status = None
while True:
present_status = ping_status(IP) #ping_status(IP) being another
#program that will return info I
#need
if present_status != last_status:
notify_output(present_status) #notify_output(msg) being a
#program that will notify me of
# a change
last_status = present_status
time.sleep(300)
'''
I would like this bit of code to run on its own, notifying me of a change (if there is one) every 5 minutes, while the rest of my program also runs and accepts inputs. Instead, the program stops at this function and won't run past it. Any help would be much appreciated, thanks!
You can use a thread or a process for this. But since this is not a CPU bound operation, overhead of dedicating a process is not worth it. So a thread would be enough. You can implement it as follows:
import threading
thread = threading.Thread(target=regular_ping, args=(ip,))
thread.start()
# Rest of the program
thread.join()

python tkinter window going unresponsive and hanging

MyButton1 =Button(master, text='Quit',bg="grey",width=20,
command=master.quit)
MyButton1.place(x=200, y=100)
MyButton2 =Button(master, text='Propagate', bg="grey",width=20,
command=mainmethod)
MyButton2.place(x=1000, y=100)
master.geometry("1500x1500")
master.mainloop( )
In the above code after pressing propagate button mainmethod is invoking..
I wrote my logic in main method where this method alone taking 2minutes to execute in the mean time GUI going unresponsive state for few min and later displaying all my required output on text box i inserted
whether any away to avoid the unresponsive issue apart from using multi threading
and i am looking such that after pressing propagate button button should disabled and window should not go unresponsive and display text.insert statements continuously which i added in main method ?????
To prevent hanging, you need to separate the calculations in the mainmethod from Tkinter's main loop by executing them in different threads. However, threading system in Python is not that well-developed as in other languages (AFAIK) because of GIL (Global Interpreter Lock), but there is an alternative - using processes just like threads. This is possible with multiprocessing library.
In order to just prevent hanging, you could create another function
from multiprocessing import Process
def mainmethodLaunch():
global mainmethodProcess
mainmethodProcess = Process(target=mainmethod)
mainmethodProcess.start()
And bind this function to MyButton2 instead of mainmethod itself.
Docs: https://docs.python.org/2/library/multiprocessing.html#the-process-class
You can see p.join in the example. join method will cause your main process to wait for the other one to complete, which you don't want.
So when you press the button, mainmethodLaunch function will be invoked, and it will create another process executing mainmethod. mainmethodLaunch function's own run duration should be insignificant. Due to usage of another process, Tkinter window will not hang. However, if you do just this, you will not be able to interact with mainmethod process in any kind while it will be working.
In order to let these processes communicate with each other, you could use pipes (https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes)
I guess the example is quite clear.
In order to receive some data from the mainmethod process over time, you will have to poll the parent_conn once a little time, let's say, second. This can be achieved with Tkinter's after method
(tkinter: how to use after method)
IMPORTANT NOTE: when using multiprocessing, you MUST initialize the program in if __name__ == '__main__': block. I mean, there should be no doing-something code outside functions and this block, no doing-something code with zero indent.
This is because multiprocessing is going to fork the same Python executable file, and it will have to distinguish the main process from the forked one, and not do initializing stuff in the forked one.
Check twice if you have done that because if you make such a mistake, it can cost you hanging of not just Tkinter window, but the whole system :)
Because the process will be going to fork itself endlessly, consuming all RAM you have, regardless of how much you have.

PyQt GUI change Button-text process time

I don't understand much about Gui action execution order etc., but my Problem is very simple:
I have a Code that looks like this:
button.setText('text')
do sth
time.sleep(1)
do sth else
Somehow the script always fails to set the text before the timeout, and therefore the timeout also breaks this action and the text appears only after the timeout. How can I tell this line of code to finish execution dispite the time.sleep()? Or do i have to introduce an if-statement, only to wait until the button has set its text (is there a better way)
Thanks
GUI applications typically use some event loop, which is responsible for actually painting your widgets, etc.
You can not block this event loop with a sleep, or your application will stop responding.
Instead, in the case of Qt, use a QTimer to invoke whatever you want to do asynchronously.

Python / Pyside Code not executing sequentially?

I am building a Qt-based GUI using Pyside. Inside a specific class who has access to a QMainWindow (_theMainWindow) class which in turn has access to 2 other Qt Widgets (theScan & theScanProgress) I am trying to show() the last one by executing
def initiateScan(self):
self._theMainWindow.theScan.theScanProgress.show()
This works just fine, the theScanProgress widget appears.
However, when I add the line that makes the application sleep (and a print statement), as below
def initiateScan(self):
self._theMainWindow.theScan.theScanProgress.show()
print("test")
time.sleep(3)
the program seems to go to sleep BEFORE the widget appears, i.e. as if the time.sleep(3) gets executed before self._theMainWindow.theScan.theScanProgress.show()
Any ideas why this happens?
This is because of the main loop which processes gui events. If you are not using threads you can only execute one function at a time. I strongly suspect that show emits a signal which goes into the event queue, which is in turn blocked until the current function returns.
Put another way, Qt is event driven, and it can only do one event at a time. What ever you did call initiateScan added an event to the stack that was to execute the function (something like you pushed a button, which emitted a signal, which then triggered the function), and that function can do some computation, alter internal state of your objects, and add events to the stack. What show does underneath is emit a signal to all of it's children for them to show them selves. For that code to run, it has to wait for the current event (the function with your sleep) to return. During the sleep the entire gui will be unresponsive for the exact same reason.
[I probably butchered some of the terms of art]
show only schedules the appearance of the progress widget. But since you are blocking the main thread with your sleep, the main thread cannot perform the scheduled action until you release it.
You have to use threads or find another way to wait 3 seconds.

Categories