Python 如何正确实施tkinter';我的程序上下文中的s.after()方法?
我正在创建一个简单的GUI程序,它利用Python和Tkinter记录用户按下界面上的按钮时的时间/日期(通过向.txt文件添加信息),并向地址列表发送电子邮件,通知收件人日志已更新 该程序有三个主框架/屏幕,我希望用户能够浏览。屏幕之间的导航应基于时间。换句话说,我希望用户在按下Tkinter按钮(我已经使用Tkinter窗口小部件的“command”参数建立了该按钮)后从主屏幕重定向到辅助屏幕,然后在5秒延迟后自动重定向回主屏幕 我知道在GUI程序中不鼓励使用Python 如何正确实施tkinter';我的程序上下文中的s.after()方法?,python,oop,tkinter,Python,Oop,Tkinter,我正在创建一个简单的GUI程序,它利用Python和Tkinter记录用户按下界面上的按钮时的时间/日期(通过向.txt文件添加信息),并向地址列表发送电子邮件,通知收件人日志已更新 该程序有三个主框架/屏幕,我希望用户能够浏览。屏幕之间的导航应基于时间。换句话说,我希望用户在按下Tkinter按钮(我已经使用Tkinter窗口小部件的“command”参数建立了该按钮)后从主屏幕重定向到辅助屏幕,然后在5秒延迟后自动重定向回主屏幕 我知道在GUI程序中不鼓励使用time.sleep()。然而,
time.sleep()
。然而,我在实现Tkinter的.after()
方法时遇到了一些问题,并没有完全达到我想要的结果
我附上了一个简化的程序代码示例,用于模拟我的问题:
import tkinter as tk
class mainApplication(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (MainScreen, AcknowledgeScreen):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainScreen)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Press Button to Log Time")
label.pack()
button = tk.Button(self, text="Confirm", command=lambda: controller.show_frame(AcknowledgeScreen))
button.pack()
class AcknowledgeScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Logging Completed. Please Wait.")
label.pack()
# The implementation below is giving me trouble.
self.after(5000, controller.show_frame(MainScreen))
root = mainApplication()
root.mainloop()
我尝试的其他解决方案(针对兴趣线)包括:
不幸的是,在开始使用Tkinter之前,我从未接触过面向对象编程,因此我认为我的错误可能是由于对OOP工作原理的根本误解。尽管如此,如果有人能给我指出正确的方向或澄清我的错误,我将不胜感激。after()
(如bind()
和command=
)需要回调-它意味着函数名没有()
,也没有参数
使用lambda
self.after(5000, lambda:controller.show_frame(MainScreen))
但我看到了不同的问题
当您运行程序时,它会在main应用程序中创建所有帧的实例。因此,它也会在开始时运行AcknowledgeScreen.\uuu init\uuu
,并在开始时运行after()
。它不会等待你的点击
顺便说一句:因为帧是在main应用程序内部创建的。因此它们不能使用root
,而root将在main应用程序之后创建。\uu init
结束其作业
您必须在方法中添加一些部分,并在每次单击按钮时运行
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Press Button to Log Time")
label.pack()
button = tk.Button(self, text="Confirm", command=self.change)
button.pack()
def change(self):
self.controller.show_frame(AcknowledgeScreen)
self.controller.frames[AcknowledgeScreen].change_after()
class AcknowledgeScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Logging Completed. Please Wait.")
label.pack()
def change_after(self):
# The implementation below is giving me trouble.
#both works
#root.after(5000, lambda:self.controller.show_frame(MainScreen))
self.after(5000, lambda:self.controller.show_frame(MainScreen))
在mainApplication
中,两个屏幕都被初始化,它们的类用于字典键映射到它们的实例
根据操作顺序,在显示AcknowledgeScreen
后,应按堆叠顺序提升Main屏幕
此操作不应存在于AcknowledgesScreen.\uuuu init\uuuu
中,除非您在需要时初始化屏幕
您想将其移动到main屏幕
。您可以按照以下方式重构main屏幕
class主屏幕(tk.Frame):
定义初始化(自、父、控制器):
tk.Frame.\uuuu init\uuuuu(自,父)
self.controller=控制器
label=tk.label(self,text=“按下按钮记录时间”)
label.pack()
按钮=tk.button(self,text=“确认”,command=self.Confirm)
button.pack()
def确认(自我):
self.controller.show_帧(确认屏幕)
赛尔夫后(5000,赛尔夫后)
def返回(自我):
self.controller.show_框架(主屏幕)
让我们看看这段代码:
self.after(5000, controller.show_frame(MainScreen))
上述代码的作用与此完全相同:
result = controller.show_frame(MainScreen)
self.after(5000, result)
注意发生了什么?功能controller.show_frame(主屏幕)
立即执行,而不是在之后由执行
相反,您需要在
之后提供一个可调用的函数,粗略地说,就是对函数的引用。如果该函数需要附加参数,则可以在
之后调用时添加这些参数:
self.after(5000, controller.show_frame, MainScreen)
上面的代码告诉after
运行controller.show\u frame
在五秒钟内,并在调用时将参数main screen
传递给它 谢谢你的回复,布莱恩。这是对after
方法如何操作的一个很好的解释。话虽如此,我测试了您的实现并遇到了以下情况:当加载程序并按下“确认”按钮时,AcknowledgeScreen
帧出现了五秒钟,然后返回到主屏幕
帧。但是,如果在窗口仍然打开时再次按下按钮,AcknowledgeScreen
不会返回到Main屏幕,而是无限期地挂起在AcknowledgeScreen
。你知道这可能是什么原因吗?
self.after(5000, controller.show_frame, MainScreen)