多处理和Tkinter的Python错误

时间:2016-02-23 22:08:55

标签: python windows python-2.7 winapi tkinter

以下是使用不使用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进程是一个子进程:

Parent and child python.exe processes

我还认为窗口句柄可能不正确,但我手动验证了正确的窗口句柄是否传递给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()

0 个答案:

没有答案