Tkinter 理解主循环
- 2024-11-22 08:47:00
- admin 原创
- 6
问题描述:
到目前为止,我习惯用以下方式结束我的 Tkinter 程序:tk.mainloop()
,否则什么都不会显示!参见示例:
from Tkinter import *
import random
import time
tk = Tk()
tk.title = "Game"
tk.resizable(0,0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
def draw(self):
pass
ball = Ball(canvas, "red")
tk.mainloop()
但是,当我尝试执行此程序中的下一步(使球随时间移动)时,我所读的书却说要执行以下操作。因此,我将绘制函数更改为:
def draw(self):
self.canvas.move(self.id, 0, -1)
并将以下代码添加到我的程序中:
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
但我注意到添加这段代码块使其变得毫无用处tk.mainloop()
,因为即使没有它,所有内容都会显示出来!!!
此时我应该提到,我的书从来没有谈论过tk.mainloop()
(可能是因为它使用了 Python 3),但我通过搜索网络了解到了它,因为我的程序无法通过复制书中的代码来运行!
所以我尝试了以下操作,但没有效果!!!
while 1:
ball.draw()
tk.mainloop()
time.sleep(0.01)
发生了什么事? 是什么意思tk.mainloop()
?tk.update_idletasks()
和是什么意思?tk.update()
和 有什么不同tk.mainloop()
? 我应该在我的程序中使用上面的循环吗?tk.mainloop()
?还是两者都用?
解决方案 1:
tk.mainloop()
块。这意味着 Python 命令的执行在此停止。您可以通过以下方式看到:
while 1:
ball.draw()
tk.mainloop()
print("hello") #NEW CODE
time.sleep(0.01)
你永远不会看到 print 语句的输出。因为没有循环,所以球不会移动。
另一方面,方法update_idletasks()
如下update()
:
while True:
ball.draw()
tk.update_idletasks()
tk.update()
...不要阻塞;这些方法完成后,执行将继续,因此循环while
将一遍又一遍地执行,从而使球移动。
包含方法调用的无限循环update_idletasks()
可以update()
替代调用tk.mainloop()
。请注意,整个 while 循环可以说是阻塞的,就像tk.mainloop()
因为 while 循环之后的任何内容都不会执行。
但是,tk.mainloop()
这并不能替代以下几行:
tk.update_idletasks()
tk.update()
而是tk.mainloop()
替代整个 while 循环:
while True:
tk.update_idletasks()
tk.update()
回复评论:
以下是tcl 文档的内容:
更新 idletasks
update 的这个子命令会从 Tcl 的事件队列中清除所有当前安排的空闲事件。空闲事件用于推迟处理,直到“没有其他事情可做”,其典型用例是 Tk 的重绘和几何重新计算。通过将这些推迟到 Tk 空闲,昂贵的重绘操作将不会执行,直到事件集群(例如,按钮释放、当前窗口更改等)中的所有事件都在脚本级别处理完毕。这让 Tk 看起来快得多,但是如果您正在执行一些长时间运行的处理,这也可能意味着长时间内不会处理任何空闲事件。通过调用 update idletasks,将立即处理由于内部状态更改而导致的重绘。(由于系统事件(例如,被用户取消图标化)而导致的重绘需要进行完整更新才能处理。)
APN 正如在更新被认为有害中所述,使用更新来处理更新空闲任务未处理的重绘存在许多问题。Joe English 在 comp.lang.tcl 帖子中描述了一种替代方案:
因此update_idletasks()
会导致一些事件子集被处理update()
。
来自更新文档:
更新?空闲任务?
更新命令用于通过反复进入 Tcl 事件循环直到所有待处理事件(包括空闲回调)都已处理,从而使应用程序“保持最新”。
如果将 idletasks 关键字指定为命令的参数,则不会处理任何新事件或错误;只会调用空闲回调。这会导致通常被推迟的操作(例如显示更新和窗口布局计算)立即执行。
KBK(2000 年 2 月 12 日)——我个人认为 [update] 命令不是最佳实践之一,建议程序员避免使用它。我很少甚至从未见过 [update] 的使用不能通过其他方式更有效地编程,通常是使用事件回调。顺便说一句,此警告适用于所有以递归方式进入事件循环的 Tcl 命令(vwait 和 tkwait 是其他常见的罪魁祸首),但使用全局级别的单个 [vwait] 在 shell 内启动事件循环(不会自动启动)除外。
我见过[更新]推荐的最常见用途是:
在执行某些长时间运行的计算时保持 GUI 处于活动状态。请参阅倒计时程序以了解替代方案。2) 等待窗口配置完成,然后再对窗口进行几何管理等操作。替代方案是绑定事件,例如通知进程窗口的几何形状。请参阅居中窗口以了解替代方案。
更新有什么问题?答案有多种。首先,它往往会使周围 GUI 的代码复杂化。如果您在 Countdown 程序中进行练习,您会感觉到当每个事件在其自己的回调中处理时,它会变得多么容易。其次,它是隐蔽错误的来源。一般问题是执行 [更新] 几乎有不受约束的副作用;从 [更新] 返回时,脚本很容易发现它下面的地毯被抽走了。在 Update 被认为有害的网站上对这种现象进行了进一步的讨论。
.....
我有没有可能让我的程序在不使用 while 循环的情况下运行?
是的,但事情有点棘手。您可能认为下面的方法可行:
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
def draw(self):
while True:
self.canvas.move(self.id, 0, -1)
ball = Ball(canvas, "red")
ball.draw()
tk.mainloop()
问题是 ball.draw() 会导致执行进入 draw() 方法中的无限循环,因此 tk.mainloop() 永远不会执行,并且您的小部件永远不会显示。在 GUI 编程中,必须不惜一切代价避免无限循环,以使小部件能够响应用户输入,例如鼠标单击。
那么,问题是:如何反复执行某些操作而不会真正创建无限循环? Tkinter 对这个问题有一个答案:一个小部件的after()
方法:
from Tkinter import *
import random
import time
tk = Tk()
tk.title = "Game"
tk.resizable(0,0)
tk.wm_attributes("-topmost", 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
def draw(self):
self.canvas.move(self.id, 0, -1)
self.canvas.after(1, self.draw) #(time_delay, method_to_execute)
ball = Ball(canvas, "red")
ball.draw() #Changed per Bryan Oakley's comment
tk.mainloop()
after() 方法不会阻塞(它实际上会创建另一个执行线程),因此在调用 after() 后,python 程序将继续执行,这意味着接下来将执行 tk.mainloop(),这样您的小部件就会得到配置和显示。after() 方法还允许您的小部件继续响应其他用户输入。尝试运行以下程序,然后在画布上的不同位置单击鼠标:
from Tkinter import *
import random
import time
root = Tk()
root.title = "Game"
root.resizable(0,0)
root.wm_attributes("-topmost", 1)
canvas = Canvas(root, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
class Ball:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 100)
self.canvas.bind("<Button-1>", self.canvas_onclick)
self.text_id = self.canvas.create_text(300, 200, anchor='se')
self.canvas.itemconfig(self.text_id, text='hello')
def canvas_onclick(self, event):
self.canvas.itemconfig(
self.text_id,
text="You clicked at ({}, {})".format(event.x, event.y)
)
def draw(self):
self.canvas.move(self.id, 0, -1)
self.canvas.after(50, self.draw)
ball = Ball(canvas, "red")
ball.draw() #Changed per Bryan Oakley's comment.
root.mainloop()
解决方案 2:
while 1:
root.update()
... (非常!)大致类似于:
root.mainloop()
不同之处在于,mainloop
这是正确的编码方式,而无限循环则有点不正确。不过,我怀疑,绝大多数情况下,这两种方法都可以。只是那mainloop
是一种更干净的解决方案。毕竟,调用mainloop
本质上就是这样的:
while the_window_has_not_been_destroyed():
wait_until_the_event_queue_is_not_empty()
event = event_queue.pop()
event.handle()
... 如您所见,这与您自己的 while 循环没有太大区别。那么,既然 tkinter 已经有一个可以使用的无限循环,为什么还要创建自己的无限循环呢?
用最简单的术语来说:始终将调用mainloop
作为程序中代码的最后一行逻辑。这就是 Tkinter 的设计用途。
解决方案 3:
我使用的是 MVC / MVA 设计模式,具有多种类型的“视图”。一种类型是“GuiView”,即 Tk 窗口。我将视图引用传递给窗口对象,该对象执行诸如将按钮链接回视图函数(适配器/控制器类也会调用该函数)之类的操作。
为了做到这一点,需要在创建窗口对象之前完成视图对象构造函数。在创建并显示窗口后,我想自动对视图执行一些初始任务。起初我尝试在 mainloop() 之后执行这些任务,但由于 mainloop() 被阻塞,所以没有成功!
因此,我创建了窗口对象并使用 tk.update() 来绘制它。然后,我开始了初始任务,最后启动了主循环。
import Tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None, view=None ):
tk.Frame.__init__( self, master )
self.view_ = view
""" Setup window linking it to the view... """
class GuiView( MyViewSuperClass ):
def open( self ):
self.tkRoot_ = tk.Tk()
self.window_ = Window( master=None, view=self )
self.window_.pack()
self.refresh()
self.onOpen()
self.tkRoot_.mainloop()
def onOpen( self ):
""" Do some initial tasks... """
def refresh( self ):
self.tkRoot_.update()
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件