简体   繁体   中英

Tkinter windows as App class methods rather than new classes or toplevels (Python 3.X.)

I'm trying to create an app in which i could move between windows. While trying to put together a tkinter app, I came up with an idea to create new windows as the class App: methods.

Questions: is this a good idea? Maybe such approach has some drawbacks that I do not see?

Sample code of the idea:

import tkinter as tk
import tkinter.ttk as ttk


class App:

    def __init__(self, master=None):
        self.master = master

        # Window 1 widgets
        self.frame1 = ttk.Frame(master, width=300, height=150, relief='groove')
        self.frame1.pack_propagate(False)
        self.label1 = ttk.Label(self.frame1, text='This is window 1')
        self.button1 = ttk.Button(self.frame1, text='Go to window 2', command=self.window2)
        self.button2 = ttk.Button(self.frame1, text='Go to window 3', command=self.window3)

        # Window 2 widgets
        self.frame2 = ttk.Frame(master, width=300, height=150, relief='groove')
        self.frame2.pack_propagate(False)
        self.label2 = ttk.Label(self.frame2, text='This is window 2')
        self.button3 = ttk.Button(self.frame2, text='Go to window 1', command=self.window1)
        self.button4 = ttk.Button(self.frame2, text='Go to window 3', command=self.window3)

        # Window 3 widgets
        self.frame3 = ttk.Frame(master, width=300, height=150, relief='groove')
        self.frame3.pack_propagate(False)
        self.label3 = ttk.Label(self.frame3, text='This is window 3')
        self.button5 = ttk.Button(self.frame3, text='Go to window 1', command=self.window1)
        self.button6 = ttk.Button(self.frame3, text='Go to window 2', command=self.window2)

        self.window1()

    def window1(self):
        self.forget_widgets()
        self.frame1.pack(side='top', pady=(25, 0))
        self.label1.pack(side='top', pady=(25, 25))
        self.button1.pack(side='top', pady=(0, 5))
        self.button2.pack(side='top')

    def window2(self):
        self.forget_widgets()
        self.frame2.pack(side='top', pady=(25, 0))
        self.label2.pack(side='top', pady=(25, 25))
        self.button3.pack(side='top', pady=(0, 5))
        self.button4.pack(side='top')

    def window3(self):
        self.forget_widgets()
        self.frame3.pack(side='top', pady=(25, 0))
        self.label3.pack(side='top', pady=(25, 25))
        self.button5.pack(side='top', pady=(0, 5))
        self.button6.pack(side='top')

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


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

While looking for other possible methods I have found that it is either best to use the toplevel widget, or create each window as a new class. The first method is not what i am looking for though, because I am trying to replicate a game-like GUI right now. As for the second method, I am not really sure. While surfing in the net I found out that creating a class with only __init__ method is overuse of classes and in general a bad practice. This is done in an answer by Bryan Oakley here on stackoverflow though: Switch between two frames in tkinter

Could anyone elaborate on this and try to explain me which method is the best and the one I should stick to while learning tkinter? Thanks!

Your code works fine. It is a different approach I would use but still works. One of the downsides to using a class and building all your code inside that class in different methods is a maintainability issue. The larger the code gets the harder it is to make changes and fix issues that come up.

That said for smaller programs I do not see any harm in using a class in this way.

While surfing in the net I found out that creating a class with only init method is overuse of classes and in general a bad practice.

I am note 100% sure what you mean by this but it sounds like you are saying the articles you have found do not want you to use the __init__ method of the class. Honestly I have no idea why one would say that. There might be a reason but for the 2 years I have spent building GUI's with Tkinter I have always used the __init__ method to build the basics of the GUI and then class methods or other class objects to work out the rest of my GUI and back end.

As for the linked post that was answered by Bryan I would say that Bryan is very knowledgeable in tkinter and python in general. If it were a bad idea to use multiple classes he would say so. I think you can build things in multiple ways but I have found using separate classes for different portions of your GUI is good for maintainability IE (class for menu, class for saving files, class for controlling themes). By separating them you can keep them in separate files allowing you to read through just the part of the code you need to look at and not worry about reading through a wall of text to figure out where things are going wrong.

All that said unless your code is going to be large or extremely resource intensive I would say build your class or classes the way you want.

I have taken your code and rebuilt it the way I would have written this to give you some idea of how you can approach this in another way. I have inherited form Tk() and used only one class attribute to build my windows.

import tkinter as tk
import tkinter.ttk as ttk


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.geometry('350x200')
        self.working_frame = ttk.Frame(self)
        self.working_frame.grid(row=0, column=0, sticky='nsew')
        self.window1()

    def window1(self):
        self.working_frame.destroy()
        self.working_frame = ttk.Frame(self, width=300, height=150, relief='groove')
        self.working_frame.pack_propagate(False)
        ttk.Label(self.working_frame, text='This is window 1').pack(side='top', pady=(25, 25))
        ttk.Button(self.working_frame, text='Go to window 2', command=self.window2).pack(side='top', pady=(0, 5))
        ttk.Button(self.working_frame, text='Go to window 3', command=self.window3).pack(side='top')
        self.working_frame.pack(side='top', pady=(25, 0))

    def window2(self):
        self.working_frame.destroy()
        self.working_frame = ttk.Frame(self, width=300, height=150, relief='groove')
        self.working_frame.pack_propagate(False)
        ttk.Label(self.working_frame, text='This is window 2').pack(side='top', pady=(25, 25))
        ttk.Button(self.working_frame, text='Go to window 1', command=self.window1).pack(side='top', pady=(0, 5))
        ttk.Button(self.working_frame, text='Go to window 3', command=self.window3).pack(side='top')
        self.working_frame.pack(side='top', pady=(25, 0))

    def window3(self):
        self.working_frame.destroy()
        self.working_frame = ttk.Frame(self, width=300, height=150, relief='groove')
        self.working_frame.pack_propagate(False)
        ttk.Label(self.working_frame, text='This is window 3').pack(side='top', pady=(25, 25))
        ttk.Button(self.working_frame, text='Go to window 1', command=self.window1).pack(side='top', pady=(0, 5))
        ttk.Button(self.working_frame, text='Go to window 2', command=self.window2).pack(side='top')
        self.working_frame.pack(side='top', pady=(25, 0))


if __name__ == '__main__':
    App().mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM