我的小弟弟正在进入编程,而在他的科学博览会项目中,他正在模拟天空中的一群鸟.他已经完成了大部分代码编写工作,并且工作得很好,但是鸟类需要每时每刻都在移动.
然而,Tkinter占用了自己的事件循环的时间,所以他的代码不会运行.做root.mainloop()
运行,运行和继续运行,它运行的唯一事情是事件处理程序.
有没有办法让他的代码与mainloop一起运行(没有多线程,这很混乱,这应该保持简单),如果是这样,它是什么?
现在,他想出了一个丑陋的黑客,把他的move()
功能绑在一起
,所以只要他按住按钮并摆动鼠标,就可以了.但必须有一个更好的方法.
after
在Tk
对象上使用该方法:
from tkinter import * root = Tk() def task(): print("hello") root.after(2000, task) # reschedule event in 2 seconds root.after(2000, task) root.mainloop()
这是该after
方法的声明和文档:
def after(self, ms, func=None, *args): """Call function once after given time. MS specifies the time in milliseconds. FUNC gives the function which shall be called. Additional parameters are given as parameters to the function call. Return identifier to cancel scheduling with after_cancel."""
Bjorn发布的解决方案导致我的计算机上出现"RuntimeError:从不同的公寓调用Tcl"消息(RedHat Enterprise 5,python 2.6.1).Bjorn可能没有得到这个消息,因为根据我检查的一个地方,错误处理与Tkinter的线程是不可预测的和平台相关的.
问题似乎是app.start()
作为对Tk的引用,因为app包含Tk元素.我通过更换固定此app.start()
带self.start()
内__init__
.我还做了它,这样所有的Tk的引用或者是内部调用函数mainloop()
或者是内部是由调用的函数调用该函数mainloop()
(这显然是至关重要的,以避免"不同公寓"的错误).
最后,我添加了一个带回调的协议处理程序,因为如果没有这个,程序会在用户关闭Tk窗口时退出并出错.
修订后的代码如下:
# Run tkinter code in another thread import tkinter as tk import threading class App(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.start() def callback(self): self.root.quit() def run(self): self.root = tk.Tk() self.root.protocol("WM_DELETE_WINDOW", self.callback) label = tk.Label(self.root, text="Hello World") label.pack() self.root.mainloop() app = App() print('Now we can continue running code while mainloop runs!') for i in range(100000): print(i)
在编写自己的循环时,如在模拟中(我假设),您需要调用执行以下操作的update
函数mainloop
:使用您的更改更新窗口,但是您在循环中执行此操作.
def task(): # do something root.update() while 1: task()
另一个选择是让tkinter在单独的线程上执行。一种方法是这样的:
import Tkinter import threading class MyTkApp(threading.Thread): def __init__(self): self.root=Tkinter.Tk() self.s = Tkinter.StringVar() self.s.set('Foo') l = Tkinter.Label(self.root,textvariable=self.s) l.pack() threading.Thread.__init__(self) def run(self): self.root.mainloop() app = MyTkApp() app.start() # Now the app should be running and the value shown on the label # can be changed by changing the member variable s. # Like this: # app.s.set('Bar')
但是要小心,多线程编程很难,而且很容易使自己陷入困境。例如,当您更改上面的示例类的成员变量时,请务必小心,以免中断Tkinter的事件循环。