循环后无法取消无限Tkinter(嵌套)



TL;DR:我在Tkinter中有一个after循环,我想无限期地运行它,但如果我想用新的启动输入重新启动循环,我希望能够终止它。由于循环的每次迭代都会再次调用after函数,所以我不知道如何在启动新循环时为after_cancel函数获取正确的after ID以杀死旧循环。

更详细的帖子:所以我试图通过Tkinter制作一个(点阵)滚动文本栏,用于虚拟出发板。为了实现这一点,我需要一段给定的文本来无限期地滚动GUI的相关部分,直到一段更新的文本准备好替换它;然而,我在停止旧卷轴准备让新卷轴取代它的位置时遇到了问题。

我觉得我的问题是,一旦其中一个循环开始,我就无法轻易地与它"交流";我的新循环也不能"告诉"我的旧循环停止。为了解决这个问题,我尝试在GUI对象中使用一些切换属性,但这些尝试都失败了(并不是说这不可能;只是我无法管理它)。进一步寻找解决方案让我觉得Tkinter函数after_cancel是可能的解决方案,但我一直无法实现这一点,因为我循环文本的方式涉及再次使用Tkinter的after函数(因此循环的after ID不断变化)。我无法解决这个问题,老实说,我不确定是我没有完全获得after和/或after_cancel函数,还是我以一种糟糕的方式循环我的函数(即我是否不应该使用after函数来实现这一点)。

如有任何帮助/建议,我们将不胜感激;如果需要任何额外的信息,我非常高兴。

以下是我的最低可复制示例代码。我很快就把它放在了一起,它非常像我的主代码的一个更简单的版本(这里我在点阵显示器上移动一个正方形而不是文本);这以与我的真实代码相同的方式循环函数,并且在其中存在相同的问题

import tkinter as tk
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.background = "black"
self.width = 600
self.height = 150
self.canvas = tk.Canvas(self, width=self.width, height=self.height, borderwidth=0, 
highlightthickness=0, background = self.background)
self.canvas.pack(side="top", fill="both", expand="true")
self.run_id_1 = self.after( 100, lambda: self.moving_square( "red", 50, 10 ) )
self.run_id_2 = self.after( 5000, lambda: self.moving_square( "green", 200, 90, 
prev_run_id = self.run_id_1 ) )
def moving_square( self, color, x_top_left, y_top_left, last_square = None, prev_run_id = None ):
if not prev_run_id == None:
self.after_cancel( prev_run_id )
if not last_square == None:
self.canvas.delete( last_square )
last_square = self.canvas.create_rectangle(x_top_left, y_top_left, x_top_left + 50, 
y_top_left + 50, outline=color, fill=color)
if x_top_left + 50 < 0:
self.after( 500, lambda: self.moving_square( color, self.width, y_top_left, last_square ) )
else:
self.after( 500, lambda: self.moving_square( color, x_top_left - 10, y_top_left, last_square ) )

if __name__ == "__main__":
app = App( )
app.mainloop()

无论何时再次调用该函数,都需要更新该ID,以便(如您所怀疑的)有一个可以传递给after_cancel的引用。您已经将该引用存储为self.run_id_1,但没有更新一个。

最简单的方法是添加一个检查if color == 'red',然后设置self.run_id_1 = self.after(...),对于'green'self.run_id_2也是如此。

然而,我应该注意到,您的moving_square函数的结构并不理想。你可能发现很难找到要更改的内容的一个原因是它已经被过度参数化了。你应该把它的工作稍微分开,分成更具体的各种函数。或者,您可以创建一个数据表来存储滚动行的相关状态和id,这样您就可以简化逻辑。

最新更新