当前位置:  开发笔记 > 编程语言 > 正文

你如何在Tkinter的事件循环中运行自己的代码?

如何解决《你如何在Tkinter的事件循环中运行自己的代码?》经验,为你挑选了4个好方法。

我的小弟弟正在进入编程,而在他的科学博览会项目中,他正在模拟天空中的一群鸟.他已经完成了大部分代码编写工作,并且工作得很好,但是鸟类需要每时每刻都在移动.

然而,Tkinter占用了自己的事件循环的时间,所以他的代码不会运行.做root.mainloop()运行,运行和继续运行,它运行的唯一事情是事件处理程序.

有没有办法让他的代码与mainloop一起运行(没有多线程,这很混乱,这应该保持简单),如果是这样,它是什么?

现在,他想出了一个丑陋的黑客,把他的move()功能绑在一起,所以只要他按住按钮并摆动鼠标,就可以了.但必须有一个更好的方法.



1> Dave Ray..:

afterTk对象上使用该方法:

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."""


如果您将超时指定为0,则任务将在完成后立即将其自身重新置于事件循环中.这不会阻止其他事件,同时仍然尽可能频繁地运行您的代码.

2> Kevin..:

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)


通常你会将参数传递给`__init __(..)`,将它们存储在`self`中并在`run(..)中使用它们

3> jma..:

在编写自己的循环时,如在模拟中(我假设),您需要调用执行以下操作的update函数mainloop:使用您的更改更新窗口,但是您在循环中执行此操作.

def task():
   # do something
   root.update()

while 1:
   task()  


你必须非常小心这种编程.如果任何事件导致`task`被调用,你将最终得到嵌套的事件循环,这很糟糕.除非你完全理解事件循环是如何工作的,否则你应该不惜一切代价避免调用`update`.

4> 小智..:

另一个选择是让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的事件循环。


jldupont:我收到“ RuntimeError:从不同的位置调用Tcl”(在不同版本中可能存在相同的错误)。解决方法是在run()中而不是在__init __()中初始化Tk。这意味着您将在与调用mainloop()相同的线程中初始化Tk。
不确定是否可以正常工作。刚刚尝试类似的东西,我得到“ RuntimeError:主线程不在主循环中”。
只需使用队列与线程进行通信即可。
推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有