简体   繁体   中英

Tkinter Class structure (class per frame) issue with duplicating widgets

Ive been trying out OOP for use with Tkinter - Im getting there (I think) slowly...

I wanted to build a structure where each frame is handled by its own class, including all of its widgets and functions. Perhaps I am coming from the wrong angle but that is what makes most logical sense to me. - Feel free to tell me if you agree / disagree!

I know why the problem is happening - when im calling each class my __init__ runs everytime and builds the relevant widgets regardless of whether they are already present in the frame. However, the only way I can think of getting round this would be to build each frame in the __init__ of my primary class GUI_Start . - Although this seems like a messy and un-organised soloution to the problem.

Is there a way I can achieve a structure where each class takes care of its own functions and widgets but doesn't build the frame each time?

See below for minimal example of the issue:

from Tkinter import *

class GUI_Start:

    def __init__(self, master):
        self.master = master
        self.master.geometry('300x300')
        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)
        self.win_colour = '#D2B48C'
        self.frames = {}

        for window in ['win1', 'win2']:
            frame = Frame(self.master, bg=self.win_colour, bd=10, relief=GROOVE)
            frame.grid(row=0, column=0, sticky='news')
            setattr(self, window, frame)
            self.frames[window] = frame

        Page_1(self.frames)

    def Next_Page(self, frames, controller):
        controller(frames)


class Page_1(GUI_Start):

    def __init__(self, master):
        self.master = master
        self.master['win1'].tkraise()

        page1_label = Label(self.master['win1'], text='PAGE 1')
        page1_label.pack(fill=X)

        page1_button = Button(self.master['win1'], text='Visit Page 2...', command=lambda: self.Next_Page(self.master, Page_2))
        page1_button.pack(fill=X, side=BOTTOM)


class Page_2(GUI_Start):

    def __init__(self, master):
        self.master = master
        self.master['win2'].tkraise()

        page2_label = Label(self.master['win2'], text='PAGE 2')
        page2_label.pack(fill=X)

        page2_button = Button(self.master['win2'], text='Back to Page 1...', command=lambda: self.Next_Page(self.master, Page_1))
        page2_button.pack(fill=X, side=BOTTOM)


root = Tk()
gui = GUI_Start(root)
root.mainloop()

Feel free to critique the structure as I may be trying to approach this from the wrong angle!

Any feedback would be much appreciated! Luke

The point of using classes is to encapsulate a bunch of behavior as a single unit. An object shouldn't modify anything outside of itself. At least, not by simply creating the object -- you can have methods that can have side effects.

In my opinion, the proper way to create "pages" is to inherit from Frame . All of the widgets that belong to the "page" must have the object itself as its parent. For example:

class PageOne(tk.Frame):
    def __init__(self, parent):
        # use the __init__ of the superclass to create the actual frame
        tk.Frame.__init__(self, parent)

        # all other widgets use self (or some descendant of self)
        # as their parent

        self.label = tk.Label(self, ...)
        self.button = tk.Button(self, ...)
        ...

Once done, you can treat instances of this class as if they were a single widget:

root = tk.Tk()
page1 = PageOne(root)
page1.pack(fill="both", expand=True)

You can also create a base Page class, and have your actual pages inherit from it, if all of your pages have something in common (for example, a header or footer)

class Page(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        <code common to all pages goes here>

class PageOne(Page):
    def __init__(self, parent):
        # initialize the parent class
        Page.__init__(self, parent)

        <code unique to page one goes here>

Your use of OOP is not very logical here. Your main program is in the class GUI_start. If your pages inherit from GUI_start, basically you create a whole new program with every page instance you create. You should instead inherit from Frame as Bryan Oakley has pointed our in the comments. Here is a somewhat repaired version of what you have posted. The original one by Bryan is still much better.

from Tkinter import *

class GUI_Start:

    def __init__(self, master):
        self.master = master
        self.master.geometry('300x300')
        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)
        self.win_colour = '#D2B48C'
        self.current_page=0

        self.pages = []
        for i in range(5):
            page = Page(self.master,i+1)
            page.grid(row=0,column=0,sticky='nsew')
            self.pages.append(page)

        for i in range(2):
            page = Page_diff(self.master,i+1)
            page.grid(row=0,column=0,sticky='nsew')
            self.pages.append(page)

        self.pages[0].tkraise()

        def Next_Page():
            next_page_index = self.current_page+1
            if next_page_index >= len(self.pages):
                next_page_index = 0
            print(next_page_index)
            self.pages[next_page_index].tkraise()
            self.current_page = next_page_index

        page1_button = Button(self.master, text='Visit next Page',command = Next_Page)
        page1_button.grid(row=1,column=0)



class Page(Frame):

    def __init__(self,master,number):
        super().__init__(master,bg='#D2B48C')
        self.master = master
        self.master.tkraise()

        page1_label = Label(self, text='PAGE '+str(number))
        page1_label.pack(fill=X,expand=True)



class Page_diff(Frame):

    def __init__(self,master,number):
        super().__init__(master)
        self.master = master
        self.master.tkraise()

        page1_label = Label(self, text='I am different PAGE '+str(number))
        page1_label.pack(fill=X)



root = Tk()
gui = GUI_Start(root)
root.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