Python event management system

I am trying to implement an event driven process with a system call or subprocess. Basically I want to run a non-blocking system command, and after completing this system call, I want to call a function. This means that I can start the GUI progress bar, run the system command and continue the progress bar, and when the system call ends, stop the progress bar.

What I want absolutely NOT to do is create a process, get its process ID and continue checking for the completion of this process in the while loop.

Below is an example of how I think this should work (they are all inside the class)

def launchTool(self): self.progressbar.config(mode = 'indeterminate') self.progressbar.start(20) self.launchButton.config(state = 'disabled') self.configCombobox.config(state = 'disabled') ## here the "onCompletion" is a pointer to a function call("/usr/bin/make psf2_dcf", shell=True, onCompletion = self.toolCompleted) def onCompletion(self): print('DONE running Tool') self.progressbar.stop() self.launchButton.config(state = 'normal') self.configCombobox.config(state = 'normal') 
+1
python subprocess progress-bar event-driven
May 6 '15 at 9:05
source share
1 answer

To avoid polling subprocess status, you can use the SIGCHLD signal on Unix. To combine it with the tkinter event loop, you can use the trick with your own pipes . It also bypasses solutions to a possible problem with the tkinter + signal , without requiring periodic wake- ups of the event loop.

 #!/usr/bin/env python3 import logging import os import signal import subprocess import tkinter info = logging.getLogger(__name__).info def on_signal(pipe, mask, count=[0]): try: signals = os.read(pipe, 512) except BlockingIOError: return # signals have been already dealt with # from asyncio/unix_events.py #+start # Because of signal coalescing, we must keep calling waitpid() as # long as we're able to reap a child. while True: try: pid, status = os.waitpid(-1, os.WNOHANG) except ChildProcessError: info('No more child processes exist.') return else: if pid == 0: info('A child process is still alive. signals=%r%s', signals, ' SIGCHLD'*(any(signum == signal.SIGCHLD for signum in signals))) return #+end # you could call your callback here info('{pid} child exited with status {status}'.format(**vars())) count[0] += 1 if count[0] == 2: root.destroy() # exit GUI logging.basicConfig(format="%(asctime)-15s %(message)s", datefmt='%F %T', level=logging.INFO) root = tkinter.Tk() root.withdraw() # hide GUI r, w = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC) signal.set_wakeup_fd(w) root.createfilehandler(r, tkinter.READABLE, on_signal) signal.signal(signal.SIGCHLD, lambda signum, frame: None) # enable SIGCHLD signal.siginterrupt(signal.SIGCHLD, False) # restart interrupted syscalls automatically info('run children') p = subprocess.Popen('sleep 4', shell=True) subprocess.Popen('sleep 1', shell=True) root.after(2000, p.send_signal, signal.SIGSTOP) # show that SIGCHLD may be delivered root.after(3000, p.send_signal, signal.SIGCONT) # while the child is still alive root.after(5000, lambda: p.poll() is None and p.kill()) # kill it root.mainloop() info('done') 

Exit

 2015-05-20 23:39:50 run children 2015-05-20 23:39:51 16991 child exited with status 0 2015-05-20 23:39:51 A child process is still alive. signals=b'\x11' SIGCHLD 2015-05-20 23:39:52 A child process is still alive. signals=b'\x11' SIGCHLD 2015-05-20 23:39:53 A child process is still alive. signals=b'\x11' SIGCHLD 2015-05-20 23:39:54 16989 child exited with status 0 2015-05-20 23:39:54 No more child processes exist. 2015-05-20 23:39:54 done 
+1
May 16 '15 at 21:48
source share



All Articles