Long running processes with a Python Tkinter GUI - python

I've been doing normal python scripts for years, but had to recently dive into GUIs (with Tkinter). Simply put, I have a basic Tk window set up with a Start/Stop button, which should call function Foo(). Function Foo() contains a while True though, which would cause the UI to lock up forever.
I am wondering if I can get some guidance from here. Should I use pythons multiprocessing tools? I was thinking about Twisted / Gevent / threads as well, but I don't think they fit the task (could be wrong).
Thanks!

I ended up using the multiprocessing library from python. I start Foo() in a new process and start. When the stop button is pressed, it terminates the sub process.

Related

Threading module python stopping and re-start threads

I was looking how to stop a thread on python using the thread module, and I found that this method is not provided by the module. I have seen some tricks to implement a way to stop the threads but nothing of this worked for me.
My program have a main window that shows every function on it, and one of this functions opens another window that do a "function2" with a button.
I want to be able to do things, or not let the windows freeze while "function2" is running, so I have used threading.Thread to define the "function2" and called it using Thread.run() method.
This, works great, but when "function2" is done, I cannot re-run the function because of the threads can only be started once.
I need a solution to this, if someone can help me, I would be glad.
Thanks.
Expanding on comments. What you have is
fun2 = threading.Thread(name='funcion2',target=funcion2)
ttk.Button(loginpanel,text='Initfun2',command=fun2.start)
which basically creates one thread and tries to re-run it on click. There is no such thing as re-runing threads so instead you have to create a new thread on click:
def fun2():
threading.Thread(name='funcion2',target=funcion2).start()
ttk.Button(loginpanel,text='Initfun2',command=fun2)
While this is better it has another drawback: what if someone starts clicking the button like mad? You want to restrain the number of threads to use. For that using a thread pool is a good option:
from concurrent.futures import ThreadPoolExecutor
THREADPOOL = ThreadPoolExecutor(10)
def fun2():
THREADPOOL.submit(funcion2)
ttk.Button(loginpanel,text='Initfun2',command=fun2)
This code is for Python3.x. For Python2 I think you need some external library.

A thread is blocked by a blocking call - how do I make a timeout on the blocking call?

I have a python program which operates an external program and starts a timeout thread. Timeout thread should countdown for 10 minutes and if the script, which operates the external program isn't finished in that time, it should kill the external program.
My thread seems to work fine on the first glance, my main script and the thread run simultaneously with no issues. But if a pop up window appears in the external program, it stops my scripts, so that even the countdown thread stops counting, therefore totally failing it's job.
I assume the issue is that the script calls a blocking function in API for the external program, which is blocked by the pop up window. I understand why it blocks my main program, but don't understand why it blocks my countdown thread. So, one possible solution might be to run a separate script for the countdown, but I would like to keep it as clean as possible and it seems really messy to start a script for this.
I have searched everywhere for a clue, but I didn't find much. There was a reference to the gevent library here:
background function in Python
, but it seems like such a basic task, that I don't want to include external library for this.
I also found a solution which uses a windows multimedia timer here, but I've never worked with this before and am afraid the code won't be flexible with this. Script is Windows-only, but it should work on all Windows from XP on.
For Unix I found signal.alarm which seems to do exactly what I want, but it's not available for Windows. Any alternatives for this?
Any ideas on how to work with this in the most simplified manner?
This is the simplified thread I'm creating (run in IDLE to reproduce the issue):
import threading
import time
class timeToKill():
def __init__(self, minutesBeforeTimeout):
self.stop = threading.Event()
self.countdownFrom = minutesBeforeTimeout * 60
def startCountdown(self):
self.countdownThread= threading.Thread(target=self.countdown, args=(self.countdownFrom,))
self.countdownThread.start()
def stopCountdown(self):
self.stop.set()
self.countdownThread.join()
def countdown(self,seconds):
for second in range(seconds):
if(self.stop.is_set()):
break
else:
print (second)
time.sleep(1)
timeout = timeToKill(1)
timeout.startCountdown()
raw_input("Blocking call, waiting for input:\n")
One possible explanation for a function call to block another Python thread is that CPython uses global interpreter lock (GIL) and the blocking API call doesn't release it (NOTE: CPython releases GIL on blocking I/O calls therefore your raw_input() example should work as is).
If you can't make the buggy API call to release GIL then you could use a process instead of a thread e.g., multiprocessing.Process instead of threading.Thread (the API is the same). Different processes are not limited by GIL.
For quick and dirty threading, I usually resort to subprocess commands. it is quite robust and os independent. It does not give as fine grained control as the thread and queue modules but for external calls to programs generally does nicely. Note the shell=True must be used with caution.
#this can be any command
p1 = subprocess.Popen(["python", "SUBSCRIPTS/TEST.py", "0"], shell=True)
#the thread p1 will run in the background - asynchronously. If you want to kill it after some time, then you need
#here do some other tasks/computations
time.sleep(10)
currentStatus = p1.poll()
if currentStatus is None: #then it is still running
try:
p1.kill() #maybe try os.kill(p1.pid,2) if p1.kill does not work
except:
#do something else if process is done running - maybe do nothing?
pass

Freezing of threads that doesn't allow to recognize a pattern

I have a Python application that uses wxPython and some additional threads. One thread uses PIL.Image.open. Under certain circumstances the application freezes so that you see an uncomplete GUI. I found out that it hangs at PIL.Image.open. When I put debug prints in the PIL module, I can see one time it hangs here, one time there ... -- which I can't understand. It seems totally unrelated.
Is there anything a thread can do in Python, that causes other threads to stop at actually unproblematic lines like import string? Or is wxPython able to give such influence?
Long running tasks will freeze a GUI, like wxPython or Tkinter. Putting the long running process into a thread usually takes care of the issue though. I am guessing that you are doing something in your thread that communicates with wxPython in a non-thread-safe manner. If you are not using wx.CallAfter, wx.CallLater or wx.PostEvent to communicate with wxPython from the thread, then that is the issue. You have to use one of those methods.
Otherwise we'll need a small runnable example to diagnose the issue.

Python watch long running process, alert after so many minutes?

I'm working with Python and Jython to deploy applications to WebSphere. However, we are running into an issue with the WAS libraries where calls to these actions will sometimes take up to 30 minutes to execute. In order to troubleshoot this issue, I need to be able to keep tabs on if a process is still executing after so many minutes and, if so, send an alert.
I'm assuming I will need to put the call in a separate thread and watch it, but I have no experience with multithreading. What is the best way to do so and are there any gotchas?
Python has a built-in Timer class which will handle the threading stuff for you:
import threading
def after_2_minutes():
if process_still_running():
print "ALERT!!"
# start a timer in the background that waits 2 minutes
threading.Timer(2 * 60, after_2_minutes).start()
# start the process
foo.bar()
Gotchas: after_30_minutes will run in a separate thread, so as with any thread, you have to make sure that its actions don't interfere with your other threads (although you can manipulate simple data structures without problems due to the Global interpreter lock in CPython).

keep alive thread in PyQt4

I have a PyQt4 application, which at some point packs a big file using the tarfile module. Since the tarfile module does not implement any callback strategy, it blocks and the Qt GUI gets unresponsive.
I want the GUI to keep updating during that time. The only possibility is a separate thread.
So, I start a QThread. What do I have to do in the QThread to make the GUI update itself?
As soon, as the tar process is finished, I want the thread to finish.
Thanks!
Nathan
QThread's are pretty much identical to normal Python threads so you can just use normal communication methods. However, QThreads also have a few signals available, so if you simply connect to those, than you're done.
In your GUI code do something like this and you're pretty much done:
thread = Thread()
thread.finished.connect(gui.do_update_thingy)
There is also a terminated and started signal available which you can use :)

Categories

Resources