Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何正确实施tkinter';我的程序上下文中的s.after()方法?_Python_Oop_Tkinter - Fatal编程技术网

Python 如何正确实施tkinter';我的程序上下文中的s.after()方法?

Python 如何正确实施tkinter';我的程序上下文中的s.after()方法?,python,oop,tkinter,Python,Oop,Tkinter,我正在创建一个简单的GUI程序,它利用Python和Tkinter记录用户按下界面上的按钮时的时间/日期(通过向.txt文件添加信息),并向地址列表发送电子邮件,通知收件人日志已更新 该程序有三个主框架/屏幕,我希望用户能够浏览。屏幕之间的导航应基于时间。换句话说,我希望用户在按下Tkinter按钮(我已经使用Tkinter窗口小部件的“command”参数建立了该按钮)后从主屏幕重定向到辅助屏幕,然后在5秒延迟后自动重定向回主屏幕 我知道在GUI程序中不鼓励使用time.sleep()。然而,

我正在创建一个简单的GUI程序,它利用Python和Tkinter记录用户按下界面上的按钮时的时间/日期(通过向.txt文件添加信息),并向地址列表发送电子邮件,通知收件人日志已更新

该程序有三个主框架/屏幕,我希望用户能够浏览。屏幕之间的导航应基于时间。换句话说,我希望用户在按下Tkinter按钮(我已经使用Tkinter窗口小部件的“command”参数建立了该按钮)后从主屏幕重定向到辅助屏幕,然后在5秒延迟后自动重定向回主屏幕

我知道在GUI程序中不鼓励使用
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)