【问题标题】:How can I update Tkinter label from a spawned process (multiprocessing.Process)?如何从衍生进程 (multiprocessing.Process) 更新 Tkinter 标签?
【发布时间】:2021-04-18 08:15:50
【问题描述】:

总结:在 Python 中,当我从衍生进程更新 Tkinter 标签文本时,GUI 上的标签不会更新,尽管已执行衍生进程。我怎样才能让它从生成的进程中更新?

我正在 Lubuntu 20.04 中使用 Python 2.7.2

编辑我也尝试过使用 Python 3.8,必须额外安装 python3-tk,更改一些语法(打印命令后的括号并将 Tkinter 替换为 tkinter),但问题仍然存在看起来一模一样。 结束编辑

这是我的示例代码,可以独立试用:

from Tkinter import *
from multiprocessing import Process

# simple Label change invoked from "simple" button
def bnAction_sync():
   print "bnAction_sync"
   changeLabel()
   print "bnAction_sync done"

# asynchronous label change invoked from Async button
def bnAction_async():
   print "bnAction_async"
   p = Process(target=changeLabel)
   p.start()
   print "bnAction_Async done"

def changeLabel():
   print "change label"
   lbl['text'] = "Text changed"
   ### Apr 19 2021: 
   ### uncommenting the following line really updates label but makes program crash ###
   # root.update_idletasks
   print "change label done"

root = Tk()

btnSync = Button(root, text="simple", command=bnAction_sync)
btnSync.pack()
btnAsync = Button(root, text="async", command=bnAction_async)
btnAsync.pack()
lbl = Label(root, text="Initial text")
lbl.pack()

root.mainloop()

如果我按下“简单”按钮,标签中的文本会更新。都很好。

但是: 如果我按下“异步”按钮,

  • 您可以通过我提供的打印结果进行验证,异步过程开始,
  • 标签文本更新行被执行。
  • 但是:这是我的问题: GUI 上的标签未显示更新后的文本。

我想这样做的原因: 因为我正在启动一个长时间运行的衍生进程,之后我想更新标签。然而,所有其他进程应该并行运行。所以我创建了一个函数f,依次包含长时间运行函数和标签更新函数。我想异步调用 f 。 所以原则上:

def longprocess():
   code...
def updatelabel_after_longprocess():
   code...
def f():
   longprocess()
   updatelabel_after_longprocess()

p = Process(target=f)
p.start()
do other things immediately

在我读到的某处,刷新被暂停,而脚本仍在运行。 我尝试了一些 p.join 插入,但没有成功。

请帮忙,谢谢!

【问题讨论】:

  • 您仍然停留在 Python 2 上是否有充分的理由?如果您升级到当前推荐和支持的语言版本,即 Python 3,您更有可能获得帮助。
  • 我从 python 2 开始,因为那是 Ubuntu 18.2 给我的。如果我现在更改为 python 3,我必须更改一些语法。目前不需要,除非我的问题当然与版本有关。 :-)
  • @tripleee:按照你的建议做了。请参阅编辑的描述。没有变化。
  • 更新:有点进步,当我在 changeLabel 函数的末尾添加行 root.update_idletasks 时。标签得到真正更新(!),可见一秒钟。然而不幸的是,整个 python 程序崩溃并显示消息“[xcb] 很可能这是一个多线程客户端并且 XInitThreads 尚未被调用”。至少我可以证明我原来的问题是 GUI 刷新问题。

标签: python python-3.x python-2.7 python-multiprocessing tkinter-label


【解决方案1】:

您不太可能通过其他流程更新您的标签。这可能是可能的,但它会非常复杂。相反,我建议您创建一个线程,在另一个进程中启动昂贵的代码,然后等待更新 GUI,直到该进程完成:

from multiprocessing import Process
from threading import Thread

def longprocess():
    # do whatever you need here, it can take a long time

def updatelabel_after_longprocess():
    # do whatever GUI stuff you need here, this will be run in the main process

def thread_helper():
    process = Process(target=longprocess)
    process.start()
    process.join() # this waits for the process to end
    updatelabel_after_longprocess()

if __name__ == "__main__":
    t = Thread(target=thread_helper)
    t.start()

    # do whatever else you have to do in parallel here

    t.join()

【讨论】:

  • 哇:效果立竿见影。非常感谢。但是,我仍然必须了解一切。如果您不介意,我可能会返回一个后续问题。
  • 再次查看并篡改它,它似乎也可以工作,如果在函数thread_helper()中我只是调用longprocess(),然后updatelabel_after_longprocess,所以显然不需要在线程。
  • 您是否需要流程取决于longprocess 中花费的时间。 GIL 限制了 Python 代码可以有效地并行执行的操作。如果它受 CPU 限制,则两个线程将争夺 GIL,并且完成的工作不会比没有任何线程的情况多得多。但是如果工作是 IO 受限的(从网络下载文件,甚至做大量的磁盘操作),或者如果 CPU 密集型部分发生在某些聪明的库中,那么你可以只用线程来完成并行工作,因为代码在等待 Python 外部的结果时会释放 GIL。
  • 在我的情况下,longprocess 是一个需要很长时间的互联网下载过程,所以它主要是 IO 或等待。
猜你喜欢
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-05
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
相关资源
最近更新 更多