当前位置: 首页 > 面试题库 >

Python-Tkinter了解主循环

暴乐邦
2023-03-14
问题内容

到现在为止,我以前以:结束我的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()

但是,当尝试该程序的下一步(使球随着时间移动)时,该书正在阅读,并说要执行以下操作。将绘图功能更改为:

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()?还是两者都在我的程序中?


问题答案:

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文档说的:

更新空闲任务

此更新子命令从Tcl的事件队列中清除所有当前调度的空闲事件。空闲事件用于推迟处理,直到“没有其他事情可做”为止,它们的典型用例是Tk的重绘和几何图形的重新计算。通过将这些操作推迟到Tk处于空闲状态,直到在脚本级别处理了事件簇(例如,按钮释放,当前窗口的更改等)中的所有内容之后,昂贵的重绘操作才完成。这使Tk看起来快得多,但是如果你正在执行一些长时间运行的处理,这也可能意味着很长时间都没有空闲事件被处理。通过调用更新空闲任务,可以立即处理由于内部状态变化而导致的重绘。(由于系统事件(例如,由用户取消图标化而重画,

APN如“更新被认为是有害的”中所述,使用更新来处理更新空闲任务未处理的重绘有许多问题。在comp.lang.tcl帖子中的Joe English描述了一种替代方法:

因此,update_idletasks()导致要处理的事件的某些子集update()。

从更新文档:

更新?idletasks?

通过重复进入Tcl事件循环,直到处理完所有未决事件(包括空闲回调),使用update命令使应用程序“更新”。

如果将idletasks关键字指定为命令的参数,则不会处理任何新事件或错误;仅空闲的回调被调用。这将导致通常推迟执行的操作(例如显示更新和窗口布局计算)立即执行。

KBK(2000年2月12日)-我个人认为[update]命令不是最佳实践之一,因此建议程序员避免使用它。我很少见过[update]的使用,而该更新无法通过其他方法(通常适当地使用事件回调)更有效地编程。顺便说一句,此警告适用于所有递归进入事件循环的Tcl命令(vwait和tkwait是其他常见的罪魁祸首),除了在全局级别使用单个[vwait]在外壳程序内部启动事件循环外不会自动启动它。

我看到[update]推荐的最普通的目的是:1)在执行一些长时间运行的计算时,保持GUI处于活动状态。另请参见倒数计时程序。2)等待配置一个窗口,然后再对其进行几何管理。另一种方法是绑定诸如通知窗口几何图形过程的事件。请参阅将窗口居中以获取其他选择。

更新有什么问题?有几个答案。首先,它倾向于使周围GUI的代码复杂化。如果你在Countdown程序中进行练习,则可以感觉到在每个事件上都有自己的回调处理每个事件时,它会变得多么容易。其次,它是隐患的来源。一般的问题是执行[update]具有几乎不受限制的副作用。从[update]返回时,脚本可以轻松地发现地毯已经从下面拉出。在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()


 类似资料:
  • 我正在为我的公司开发一个“多层”GUI来监控温度和状态。因为我是python编程新手,所以我的代码需要一些帮助。 代码由类构成。“Main”初始化主窗口(tkinter)并创建其他要显示的帧(如果需要)。除“canvas”外,其他每个类都是一个框架,它将显示不同的内容。 每个画布都包含一个图像和一些文本/可变文本。线程用于从数据库获取数据并更改画布中的文本。 每次线程访问画布并尝试更改文本或创建新

  • 我有以下代码: 这里的主要循环是: 但是我不确定这是最好的方法(如果我想输入什么,这是行不通的) 然后我试了这个: 但正如我所意识到的,这并不像我预期的那么快。所以问题是:创建主循环的最佳方法是什么?

  • 问题内容: 我有以下代码: 这里的主要循环是: 但是我不确定这是做到这一点的最佳方法(如果我想输入一些信息,这将不起作用) 然后我尝试了这个: 但是,正如我已经意识到的那样,它并没有达到我的预期。所以问题是:创建主循环的最佳方法是什么? 问题答案: Tkinter为此提供了一个强大的工具,它被称为after。它旨在用作同步睡眠命令,但可以通过调用自身在mainloop内建立一个循环。 之后,是一个

  • 问题内容: 如何从单独的对象调用tkinter ? 我在寻找类似wxWidgets的东西。例如,如果我创建一个对象,并将其根实例传递给它,然后尝试从我的对象中调用该根窗口的方法,则我的应用程序将锁定。 我能想到的最好的方法是使用该方法并从单独的对象检查状态,但这似乎很浪费。 问题答案: 要回答“如何从单独的对象调用TkInter事件”这一特定问题,请使用命令。它允许您将事件注入到根窗口的事件队列中

  • 问题内容: 我是Python的新手,但是我有其他OOP语言的经验。我的课程没有解释python中的主要方法。 请告诉我main方法如何在python中工作?我很困惑,因为我试图将其与Java进行比较。 main是如何执行的,为什么我需要这个奇怪的东西才能执行。删除时,我的代码将终止,而不会输出。 最小的代码- 问题答案: Python的“ main”方法几乎是该语言(*)所独有的。 语义有些微妙。

  • 问题内容: 我有一个带有“开始”按钮和进度条的小型GUI测试。所需的行为是: 点击开始 进度条振荡5秒钟 进度栏停止 观察到的行为是“开始”按钮冻结5秒钟,然后显示进度条(无振荡)。 到目前为止,这是我的代码: 根据Bryan Oakley 在此提供的信息,我了解我需要使用线程。我尝试创建一个线程,但是我猜测由于该线程是从主线程中启动的,因此没有帮助。 我有想法放置在不同的类中的逻辑部分,以及从该