gpt4 book ai didi

python - 使用 Tkinter 时如何并行化方法

转载 作者:行者123 更新时间:2023-12-01 09:24:53 25 4
gpt4 key购买 nike

如果其中一个方法使用 Tkinter 对象,如何同时运行两个方法?

问题设置:当给定时间过去后,方法 A 停止方法 B。在此之前,方法 A 在 Tkinter 标签中显示剩余时间。

问题:方法不能同时运行。

Python版本:2.7

操作系统: Windows 7

我使用线程来实现并发。我读到Python有一个叫做全局解释器锁的东西,它使线程串行运行。我认为这会导致问题。

解决方法是使用进程。这是不可能的,因为 Tkinter 对象无法转换为字符流(“pickle”)。当我尝试使用进程时出现此错误:PicklingError: Can't pickle 'tkapp' object.

下面的可运行示例模仿了真实的程序,该程序要大得多。因此,它使用模型- View - Controller 设计模式。我从 Timeout on a function call 复制了一些代码.

用例:用户单击按钮。这将启动一个可能需要很长时间的后台任务。后台任务运行时,前端不断通知用户还剩多少时间,直到程序取消后台任务。

后台运行的线程尚未实现,因此可以停止。但这不是我想知道的。

from Tkinter import *
from time import sleep
from threading import Thread, Timer

class Frontend(Tk):

def __init__(self):

Tk.__init__(self)

self.label = Label(self, text = "", font = ("Courier", 12))
self.button = Button(self, text = "Run thread in background.", font = ("Courier", 12))
self.label.grid()
self.button.grid(sticky = "nsew")

class Backend:

def background_task(self):

print "Background task is executing."
sleep(6)
print "Finished."

class Controller:

def __init__(self):

self.INTERRUPT_AFTER = 4
self.done = True
self.backend = Backend()
self.frontend = Frontend()
self.frontend.button.configure(command = self.run_something_in_background)

class Decorator(object):

def __init__(self, instance, time):

self.instance = instance
self.time = time

def exit_after(self):
def outer(fn):
def inner():

timer = Timer(self.time, self.quit_function)
timer.start()
fn()

return timer
return inner
return outer

def quit_function(self):

if not self.instance.done:
self.instance.display_message("Interrupted background task.")
self.instance.set_done(True)

def run_something_in_background(self):

backendThread = Thread(target = self.backend.background_task)
decorator = self.Decorator(self, self.INTERRUPT_AFTER)
countdown = decorator.exit_after()(self.countdown) # exit_after returns the function with which we decorate.

self.set_done(False)
countdown()
backendThread.start()
backendThread.join()
self.set_done(True)


def countdown(self):

seconds = self.INTERRUPT_AFTER
while seconds > 0 and not self.done:
message = "Interrupting background task in {} seconds\nif not finished.".format(str(seconds))
self.display_message(message)
seconds -= 1
sleep(1)

def set_done(self, val):

self.done = val

def display_message(self, message):

self.frontend.label.config(text = message)
self.frontend.update_idletasks()

def run(self):

self.frontend.mainloop()


app = Controller()
app.run()

最佳答案

尝试使用线程或多处理时面临的挑战是 tkinter 事件循环似乎受到后端线程/进程的影响。你能做的就是我在这里所做的。关键是使用 subprocess.Popen()。这会强制解释器打开另一个尚未加载 tkinter 且未运行主循环的解释器(请确保不运行)。

这是 frontend.py 程序:

from Tkinter import *
from subprocess import Popen

class Frontend(Tk):

def __init__(self):

Tk.__init__(self)
self.label = Label(self, text = "", font = ("Courier", 12), justify='left')
self.button = Button(self, text = "Run thread in background.", font = ("Courier", 12))
self.label.grid()
self.button.grid(sticky = "nsew")

class Controller:

def __init__(self):

self.INTERRUPT_AFTER = 4
self.done = False
self.frontend = Frontend()
self.frontend.button.configure(command = self.run_something_in_background)

def run_something_in_background(self, *args):

self.set_done(False)
seconds = 4
self.frontend.after(int(seconds) * 1000, self.stopBackend)
self.countdown(seconds)

self.backendProcess = Popen(['python', 'backend.py'])

def stopBackend(self):
self.backendProcess.terminate()
self.done = True
print 'Backend process terminated by frontend.'
self.display_message('Backend process terminated')

def countdown(self, remaining):
print 'countdown', remaining
if remaining > 0:
message = "Interrupting background task in"
message += " {} seconds\nif not finished.".format(str(remaining))
elif self.done:
message = 'Backend process completed'
self.display_message(message)
remaining -= 1
if remaining > 0 and not self.done:
self.frontend.after(1000, lambda s=remaining: self.countdown(s))
else:
message = 'Interrupting backend process.'
self.display_message(message)

def set_done(self, val):
self.done = val

def display_message(self, message):
self.frontend.label.config(text = message)
self.frontend.update_idletasks()

def run(self):
self.frontend.mainloop()

app = Controller()
app.run()

以及 backend.py 代码:

from time import sleep


def background_task():

print "Background task is executing."
for i in range(8):
sleep(1)
print 'Background process completed', i+1, 'iteration(s)'
print "Finished."

background_task()

关于python - 使用 Tkinter 时如何并行化方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50510624/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com