【问题标题】:Tkinter buttons are loosing callbacks when switching windows (class implementation) (python 3.X)Tkinter 按钮在切换窗口时失去回调(类实现)(python 3.X)
【发布时间】:2019-01-28 10:31:46
【问题描述】:

我正在尝试学习如何使用类构建 tkinter GUI 应用程序。除此之外,我还试图复制一个简单的类似游戏的 GUI,其中确实希望在每个窗口中都有一个完全不同的菜单,而不是顶层。所以我想做的是__init__我的班级通过创建我将要使用的所有小部件然后在窗口之间跳转时忘记并根据窗口放置它们。

问题:我无法在使用类时重现上述行为。

我创建了两个示例程序来说明问题。在第一个中,两个窗口之间的切换工作正常,程序复制了我期望的行为:

import tkinter as tk
import tkinter.ttk as ttk


def forget_widgets():
    for widget in root.winfo_children():
        widget.pack_forget()


def window1():
    forget_widgets()

    my_frame1.pack()
    my_button1.grid(row=0, column=0)
    my_button2.grid(row=1, column=0)


def window2():
    forget_widgets()

    my_frame2.pack()
    my_button3.grid(row=0, column=0)
    my_button4.grid(row=1, column=0)


if __name__ == '__main__':
    root = tk.Tk()

    my_frame1 = ttk.Frame(root)
    my_frame2 = ttk.Frame(root)
    my_button1 = ttk.Button(my_frame1, text='Button1', command=window2)
    my_button2 = ttk.Button(my_frame1, text='Button2', command=window2)
    my_button3 = ttk.Button(my_frame2, text='Button3', command=window1)
    my_button4 = ttk.Button(my_frame2, text='Button4', command=window1)

    window1()
    root.mainloop()

但是,当我尝试将所有这些包装在一个类中时 - GUI 在切换到窗口 2 后停止工作。按钮似乎失去了它们的回调。代码:

import tkinter as tk
import tkinter.ttk as ttk


class App:

    def __init__(self, master=None):
        self.frame = ttk.Frame(master)

        self.button1 = ttk.Button(self.frame, text='Button1', command=self.window2)
        self.button2 = ttk.Button(self.frame, text='Button2', command=self.window2)
        self.button3 = ttk.Button(self.frame, text='Button3', command=self.window1)
        self.button4 = ttk.Button(self.frame, text='Button4', command=self.window1)

        self.window1()

    def window1(self):
        self.widget_forget()

        self.frame.pack()
        self.button1.grid(column=0, row=0)
        self.button2.grid(column=0, row=1)

    def window2(self):
        self.widget_forget()

        self.frame.pack()
        self.button3.grid(column=0, row=0)
        self.button4.grid(column=0, row=1)

    @staticmethod
    def widget_forget():
        for widget in root.winfo_children():
            widget.pack_forget()


if __name__ == '__main__':
    root = tk.Tk()
    App(master=root)
    root.mainloop()

我认为这个问题与我对类如何工作的理解有关。我对编程还是比较陌生,所以我可能缺少一些简单的东西。

我已经尝试以多种方式重组我的widget_forget() 函数,在 App 类之外定义它,使用 lambdas 创建回调,但没有任何积极影响。

起初我认为问题可能在于我只在根对象上使用forget(),但情况似乎并非如此,因为示例 1 工作正常并且其中的按钮以相同的方式定义.

我在堆栈上找到了一些答案,这些答案指向了一些其他可能的解决方案(即切换框架或使用 Toplevel 小部件),但我没有遇到与我自己类似的问题。

【问题讨论】:

  • 在您的第一个示例中,您有 my_frame1my_frame2。在第二个示例中,您只有 self.frame。为什么在第二个示例中没有两个框架?
  • 实际上,添加第二帧解决了我的问题!所以谢谢。也许你可以解释一下,为什么程序不能在同一个框架下工作?是不是因为我把小部件放在第二个框架上,即使我忘记了框架,它们也会留在那儿?
  • 我试图在我刚刚发布的答案中解释它,希望这会有所帮助!

标签: python-3.x class tkinter


【解决方案1】:

在您的第一个示例中,您有两个框架,my_frame1my_frame2。您将my_button1my_button1 放入my_frame1my_button3my_button4 放入my_frame2。然后,您切换帧的方式是从root 中删除所有打包的帧并打包您要显示的帧。请注意,您不会从框架中删除按钮,仅从主窗口中删除框架!

在第二个示例中,您将所有按钮放在同一框架中,self.frame。然后,您使用相同的策略来尝试切换帧。您从root 中删除所有帧,但在这两种情况下都打包@​​987654332@。因此,每次都显示相同的帧。

我猜你相信widget_forget() 会从框架中删除按钮,但它不会因为你在root 上调用它并且root 的唯一直接子级是框架(print(root.winfo_children())输出[<tkinter.ttk.Frame object .!frame>, <tkinter.ttk.Frame object .!frame2>])。

由于您没有从框架中删除按钮,因此每次在回调方法中grid 它们也没有任何意义。相反,您可以将它们在各自的框架中网格化一次,然后删除并打包框架:

import tkinter as tk
import tkinter.ttk as ttk


class App:

    def __init__(self, master=None):
        self.master = master            
        self.frame1 = ttk.Frame(self.master)
        self.frame2 = ttk.Frame(self.master)


        self.button1 = ttk.Button(self.frame1, text='Button1', command=self.window2)
        self.button2 = ttk.Button(self.frame1, text='Button2', command=self.window2)
        self.button3 = ttk.Button(self.frame2, text='Button3', command=self.window1)
        self.button4 = ttk.Button(self.frame2, text='Button4', command=self.window1)
        self.button1.grid(column=0, row=0)
        self.button2.grid(column=0, row=1)
        self.button3.grid(column=0, row=0)
        self.button4.grid(column=0, row=1)
        self.window1()

    def window1(self):
        self.widget_forget()
        self.frame1.pack()

    def window2(self):
        self.widget_forget()
        self.frame2.pack()

    def widget_forget(self):
        for widget in self.master.winfo_children():
            widget.pack_forget()


if __name__ == '__main__':
    root = tk.Tk()
    App(master=root)
    root.mainloop()

附:不要在第二个示例中使用root.winfo_children()root 是在类之外声明的变量。而是使用传递给类的master 变量,将其保存在self.master 中并使用它。当根窗口在类之外重命名时,这可以防止您的类中断。

【讨论】:

    猜你喜欢
    • 2018-08-25
    • 2021-03-08
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-03
    • 1970-01-01
    • 2013-04-11
    相关资源
    最近更新 更多