Reputation: 1825
I'm developing a UI which has a Run button to run a certain tests on command prompt.I tried implementing threads here for invoking the command prompt and thought of monitoring the thread. Till the tests are run (on command prompt), I want the run button to be disabled and want to enable it only when the command prompt is closed.
I have created a .bat file to run the list of tests in command prompt.
The code is as follows:
Thread for invoking command prompt:
class RunMonitor(threading.Thread):
def run(self):
print 'Invoking the command prompt .....'
subprocess.call(["start", "/DC:\\Scripts", "scripts_to_execute.bat"], shell=True)
For Monitoring the thread
def runscript(self):
print 'Complete_file_Path inside Run script is : ' , self.complete_file_path
file_operation.Generate_Bat_File(self.complete_file_path)
run_monitor_object = RunMonitor()
run_monitor_object.start()
while True:
if run_monitor_object.isAlive():
print 'The thread is still alive....'
else:
print 'The Thread is not alive anymore'
self.run_button.setEnabled(True)
break
From the above example, as soon as i invoke the command prompt, I run a while loop to monitor the status and I expect that the thread would be active as long as the command prompt is invoked and would be dead once I close the command prompt. But in my case, the application just hangs..
Few questions: 1. is this the right way to invoke thread? 2. is this the right way to monitor the status of the thread? 3. is there a better way of handling this ??
Any help towards this would be greatly appreciated.
Thanks.
Upvotes: 0
Views: 770
Reputation: 1354
I would almost recommend not using thread, however, if you still want to do that, one very simple way to communicate between your main program and your thread is via threading events. This is really just a boolean event (on or off), and you can take action when the Event is set. Here is your original program updated to use this:
class RunMonitor(threading.Thread):
def __init__(self, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
def run(self):
print 'Invoking the command prompt .....'
subprocess.call(["start", "/DC:\\Scripts", "scripts_to_execute.bat"], shell=True)
self.quit_event.set()
def runscript(self):
print 'Complete_file_Path inside Run script is : ' , self.complete_file_path
file_operation.Generate_Bat_File(self.complete_file_path)
quit_event = threading.Event()
run_monitor_object = RunMonitor(quit_event).start()
while True:
if not quit_event.is_set():
print 'The thread is still alive....'
else:
print 'The Thread is not alive anymore'
self.run_button.setEnabled(True)
break
So, essentially, before you start the thread you create a threading.Event()
object and pass that to your thread. Once this event is created you can .set()
it to 'turn on' the Event, the main program simply waits for that to happen.
Like I say, this is very simple, it's just a boolean event. If you need something more complex you could add more events, or use a threading.Queue()
instead.
[EDIT] Here is the fully working sample I created (rather than try to shoe-horn everything into your sample):
Here is the python file, note the changed to the subprocess.call
line:
import threading
import subprocess
import time
class RunMonitor(threading.Thread):
def __init__(self, quit_event):
threading.Thread.__init__(self)
self.quit_event = quit_event
def run(self):
print 'Invoking the command prompt .....\n'
subprocess.call(["start", "/WAIT", "/DC:\\python27\\sample", "xxx12.bat"], shell=True)
self.quit_event.set()
class Something:
def runscript(self):
print 'Starting the thread...'
quit_event = threading.Event()
run_monitor_object = RunMonitor(quit_event).start()
while True:
if not quit_event.is_set():
print 'The thread is still alive....'
else:
print 'The Thread is not alive anymore'
break
time.sleep(1)
runme = Something()
runme.runscript()
Note that I've added a sleep to the main loop so that the console doesn't fill up with "The thread is still alive..." messages.
Also, for reference here is my batch file (I named it xxx12.bat, as referenced in the python code), I just used this to cause delays so I could prove the thread was terminating correctly:
echo wscript.sleep 2500 > xxx12.vbs
start /wait xxx12.vbs
dir c:\
start /wait xxx12.vbs
dir c:\
start /wait xxx12.vbs
dir c:\
start /wait xxx12.vbs
dir c:\
start /wait xxx12.vbs
dir c:\
exit
The important thing to note here is that the 'exit' command in this batch file is vital, if we don't put that there the subprocess call will never terminate. Hopefully this example will help.
Upvotes: 2
Reputation: 273756
The problem is how you're monitoring the thread. runscript
blocks the program by executing in a loop as long as the thread is alive, thus blocking the GUI event system from processing user events - this creates the appearance of "hanging" you're experiencing.
There are many techniques for interacting with threads in GUI programming and PyQt in particular. The one I prefer in cases like this is using a timer. Once you start the work thread, also create a timer that invokes a method (slot) every 100 ms or so. This timer method does the monitoring - check if the work thread is still alive, etc. This won't block the event loop, for obvious reasons.
Upvotes: 2