Python中的事件驱动系统调用
我正在尝试通过系统调用或子流程来实现事件驱动的流程。基本上,我想启动一个非阻塞系统命令,并且在该系统调用完成后,我希望一个函数被调用。这样一来,我可以启动GUI进度栏,启动系统命令并使进度栏继续运行,并在系统调用结束时停止进度栏。
我绝对不希望做的是生成一个进程,获取其进程ID,并在while循环中继续检查该进程的完成情况。
下面只是一个我如何想象它应该工作的示例(所有这些都在一个类中)
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')
-
为了避免轮询子进程的状态,可以
SIGCHLD
在Unix上使用signal。要将其与tkinter的事件循环结合使用,可以使用self-
pipe技巧。它还解决了可能的tkinter
+信号问题,而无需定期唤醒事件循环。#!/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')
输出量
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