以下是使用不使用Tkinter进行正常工作的多处理的示例。它找到另一个进程的指定窗口,并尝试将其设置为前景窗口。我这样做是为了允许长时间运行的任务在与Tkinter应用程序不同的进程中运行。以下示例是演示此问题的应用程序的简化版本。
Worker.py
# Worker.py
import multiprocessing
class Worker:
def __init__ (self):
self.work_queue = multiprocessing.Queue()
self.running = True
self.process = multiprocessing.Process(target=self.process_commands)
self.process.start()
def do_work (self):
self.work_queue.put('work')
def do_quit (self):
self.work_queue.put('quit')
def process_commands (self):
while self.running:
cmd = self.work_queue.get()
if cmd == 'quit':
self.running = False
elif cmd == 'work':
import win32gui
hwnd = win32gui.FindWindow(None, 'Untitled - Notepad')
win32gui.SetForegroundWindow(hwnd)
Main2.py
# Main2.py
import Worker
class App:
def __init__ (self):
self.worker = Worker.Worker()
def do_work (self):
self.worker.do_work()
def exit (self):
self.worker.do_quit()
if __name__ == '__main__':
app = App()
app.do_work()
app.exit()
然而,当我尝试从Tkinter应用程序执行相同的操作时,我收到错误。
Main.py
# Main.py
import Tkinter as tk
import Worker
class App:
def __init__ (self):
self.worker = Worker.Worker()
self.root = tk.Tk()
self.root.protocol('WM_DELETE_WINDOW', self.exit)
self.frame = tk.Frame(self.root)
self.button = tk.Button(self.root, text='Go', command=self.do_work)
self.button.pack()
self.statusbar = tk.Label(self.root, text='', bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
self.update_status('Ready')
self.frame.pack(fill='both', expand=True)
self.root.mainloop()
def do_work (self):
self.update_status('Doing work...')
self.worker.do_work()
def exit (self):
self.worker.do_quit()
self.root.quit()
def update_status (self, status):
self.statusbar.config(text=status)
self.statusbar.update_idletasks()
if __name__ == '__main__':
app = App()
以下是在Main.py中运行Tkinter应用程序时出现的错误:
C:\Scripts\Testing>python Main.py
Process Process-1:
Traceback (most recent call last):
File "C:\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "C:\Python27\lib\multiprocessing\process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "C:\Scripts\Testing\Worker.py", line 21, in process_commands
win32gui.SetForegroundWindow(hwnd)
error: (0, 'SetForegroundWindow', 'No error message is available')
一个注意事项:SetForegroundWindow有一个限制,它必须从当前前景窗口运行。如果您尝试从后台窗口调用SetForegroundWindow,它将失败。我认为这可能是问题,但是如果调用SetForegroundWindow的进程是由前景窗口的进程创建的,那么它将允许它,所以我不认为这是问题。我使用Process Explorer验证了worker进程是一个子进程:
我还认为窗口句柄可能不正确,但我手动验证了正确的窗口句柄是否传递给SetForegroundWindow。
编辑:一个稍微简单的版本,单个模块,没有类,会产生同样的错误:
Main3.py
# Main3.py
import Tkinter as tk
import multiprocessing
def process_commands (work_queue):
running = True
while running:
cmd = work_queue.get()
if cmd == 'quit':
running = False
elif cmd == 'work':
import win32gui
hwnd = win32gui.FindWindow(None, 'Untitled - Notepad')
print hwnd
win32gui.SetForegroundWindow(hwnd)
class App:
def __init__ (self):
self.work_queue = multiprocessing.Queue()
self.process = multiprocessing.Process(target=process_commands, args=(self.work_queue,))
self.process.start()
self.root = tk.Tk()
self.root.protocol('WM_DELETE_WINDOW', self.exit)
self.frame = tk.Frame(self.root)
self.button = tk.Button(self.root, text='Go', command=self.do_work)
self.button.pack()
self.statusbar = tk.Label(self.root, text='', bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
self.update_status('Ready')
self.frame.pack(fill='both', expand=True)
self.root.mainloop()
def do_work (self):
self.update_status('Doing work...')
self.work_queue.put('work')
def exit (self):
self.work_queue.put('quit')
self.root.quit()
def update_status (self, status):
self.statusbar.config(text=status)
self.statusbar.update_idletasks()
if __name__ == '__main__':
app = App()