直到现在,我曾经用以下结束我的Tkiter程序: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()
Run Code Online (Sandbox Code Playgroud)
然而,当尝试下这个程序的下一步(让球随时间移动)时,本书正在阅读,说要做到以下几点.将绘图功能更改为:
def draw(self):
self.canvas.move(self.id, 0, -1)
Run Code Online (Sandbox Code Playgroud)
并将以下代码添加到我的程序:
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
Run Code Online (Sandbox Code Playgroud)
但我注意到添加这段代码,使用tk.mainloop()无用,因为即使没有它,一切都会出现!
在这一刻,我应该提到我的书从未谈到tk.mainloop()(也许是因为它使用Python 3)但我学会了它在网上搜索,因为我的程序不能通过复制书的代码工作!
所以我尝试做以下不起作用!
while 1:
ball.draw()
tk.mainloop()
time.sleep(0.01)
Run Code Online (Sandbox Code Playgroud)
这是怎么回事?这是什么tk.mainloop()?它的作用tk.update_idletasks()和tk.update()作用以及它与之有何不同tk.mainloop()?我应该使用上面的循环吗?tk.mainloop()?或两者都在我的程序中?
7st*_*tud 73
tk.mainloop() 块.这意味着你的python程序的执行停止了.你可以通过写:
while 1:
ball.draw()
tk.mainloop()
print "hello" #NEW CODE
time.sleep(0.01)
Run Code Online (Sandbox Code Playgroud)
您永远不会看到print语句的输出.因为没有环,所以球不会移动.
另一方面,方法update_idletasks()和update()这里:
while True:
ball.draw()
tk.update_idletasks()
tk.update()
Run Code Online (Sandbox Code Playgroud)
......不要阻止; 在这些方法完成后继续执行,因此while循环一遍又一遍地执行,这使得球移动.
包含方法调用的无限循环update_idletasks(),update()可以作为调用的替代tk.mainloop().请注意,整个while循环可以说就像阻塞一样,tk.mainloop()因为while循环之后没有任何东西会执行.
但是,tk.mainloop()不能仅仅替代线条:
tk.update_idletasks()
tk.update()
Run Code Online (Sandbox Code Playgroud)
相反,它tk.mainloop()是整个while循环的替代:
while True:
tk.update_idletasks()
tk.update()
Run Code Online (Sandbox Code Playgroud)
回复评论:
以下是tcl文档所说的内容:
更新idletasks
此更新子命令从Tcl的事件队列中刷新所有当前调度的空闲事件.空闲事件用于推迟处理,直到"没有别的事情可做",其中典型的用例是Tk的重绘和几何重新计算.通过推迟这些直到Tk空闲,直到在脚本级别处理来自一组事件(例如,按钮释放,当前窗口的改变等)的所有内容之后才进行昂贵的重绘操作.这使得Tk看起来更快,但是如果你正在进行一些长时间运行的处理,那么它也意味着很长时间没有处理空闲事件.通过调用更新idletasks,可以立即处理由于状态内部更改而导致的重绘.(由于系统事件而重绘,例如,被用户取消图标化,需要完整更新才能处理.)
APN如更新中所述有害,使用更新来处理未由更新idletasks处理的重绘有许多问题.comp.lang.tcl帖子中的Joe English描述了另一种选择:
因此update_idletasks()导致处理update()导致处理的事件的一些子集.
从更新文档:
更新?idletasks?
update命令用于通过重复进入Tcl事件循环使应用程序"更新",直到处理完所有挂起事件(包括空闲回调).
如果将idletasks关键字指定为命令的参数,则不会处理任何新事件或错误; 只调用空闲回调.这会导致通常延迟的操作(例如显示更新和窗口布局计算)立即执行.
KBK(2000年2月12日) - 我个人认为[update]命令不是最佳实践之一,建议程序员避免使用它.我很少看到[更新]的使用无法通过其他方式更有效地编程,通常适当使用事件回调.顺便说一句,这种谨慎适用于递归进入事件循环的所有Tcl命令(vwait和tkwait是其他常见的罪魁祸首),除了在全局级别使用单个[vwait]在shell中启动事件循环之外不会自动启动它.
我看到[更新]推荐的最常见目的是:1)在执行一些长时间运行的计算时保持GUI处于活动状态.请参阅倒计时程序以获取替代方案.2)在对其进行几何管理之前等待配置窗口.另一种方法是绑定事件,例如通知窗口几何体的过程.请参阅居中窗口以获取替代方案.
更新有什么问题?有几个答案.首先,它往往使周围GUI的代码复杂化.如果你在倒计时程序中练习练习,你会发现每个事件在自己的回调上处理时会有多容易.其次,它是阴险错误的根源.一般问题是执行[更新]几乎没有受到限制的副作用; 从[更新]返回时,脚本可以很容易地发现地毯已经从它下面拉出来了.在被认为有害的更新中,有关此现象的进一步讨论.
.....
有没有机会我可以让我的程序在没有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()
Run Code Online (Sandbox Code Playgroud)
问题是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()
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
Bry*_*ley 13
while 1:
root.update()
Run Code Online (Sandbox Code Playgroud)
......(非常!)大致类似于:
root.mainloop()
Run Code Online (Sandbox Code Playgroud)
不同的是,mainloop正确的编码方式和无限循环是巧妙的错误.但我怀疑,绝大多数时候,要么会奏效.这只是mainloop一个更清洁的解决方案.毕竟,呼叫mainloop基本上是这样的:
while the_window_has_not_been_destroyed():
wait_until_the_event_queue_is_not_empty()
event = event_queue.pop()
event.handle()
Run Code Online (Sandbox Code Playgroud)
......正如你所看到的,与你自己的while循环没什么不同.那么,为什么在tkinter已经有一个你可以使用的循环时创建自己的无限循环?
尽可能简单地说:始终将程序mainloop中的最后一行代码称为.这就是Tkinter的设计用途.
| 归档时间: |
|
| 查看次数: |
123619 次 |
| 最近记录: |