简体   繁体   English

了解在OOP中何时使用类与方法-使用tkinter切换帧

[英]Understanding when to use classes vs methods in OOP - switching frames with tkinter

I'm trying to replicate an answer to my own accord from this answer/question here: Switch between two frames in tkinter 我正在尝试从此处的此答案/问题中复制自己的答案: 在tkinter中的两个帧之间切换

As you can see from the answer above, tehy instantiate multiple sub-classes of the Frame widget, and when we want to switch pages, we click a button and that class a method from our base class. 从上面的答案中可以看到,实例化了Frame小部件的多个子类,并且当我们要切换页面时,我们单击一个按钮,然后该类作为基类的方法。

However, wouldn't creating multiple 'pages' methods under a 'Pages' class, be a lot cleaner and make sense? 但是,难道不是在“ Pages”类下创建多个“ pages”方法会更清洁并且有意义吗? I'm not sure what to believe, and I would love clarification as to how I should be tackling this project, and why using classes or instanced methods would be better? 我不确定该相信什么,我想澄清我应该如何处理这个项目,为什么使用类或实例化方法会更好?

I've added my comments into the code below for lines I don't quite understand and I'm hoping I can gain some knowledge from the world of StackOverflow. 我已将我的注释添加到下面的代码中,以了解我不太了解的行,并希望可以从StackOverflow的世界中获得一些知识。

import Tkinter as tk

LARGE_FONT = ("Verdana", 12)

class Main(tk.Tk):

    def __init__(self, *args, **kwargs):
    ########### are we calling the Tk class from tkinter and passing in our 'Main' class?
        tk.Tk.__init__(self, *args, **kwargs)
    # why not self.container?
        container = tk.Frame(self)
    # again, should I be using self.page here instead?
        page = Pages(parent=container, controller=self)

        container.pack(side="top", fill="both", expand=True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in ('page.page_one()', 'page.page_two()'):
        # what exactly does __name__ do? And how can I replicate this with my derived classes instanced methods?
            page_name = F#F.__name__
            frame = page#(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky='nsew')

        self.show_frame("page.page_one()")

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()

class Pages(tk.Frame):

    # i could just use *args, **kwargs here couldn't I?
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.page_one(controller)

    # here I have my instance methods inside my derived 'Pages' class
    # isn't this much cleaner, having multiple instance methods inside a class?
    # or I should be separating each page into it's own instanced class?
    def page_one(self, controller):
        label = tk.Label(self, text='show_firmware_page', font=LARGE_FONT)
        label.pack(pady=10, padx=10)

        # how can I switch pages using the next button?
        next_btn = tk.Button(self, text='Next', command=lambda: controller.show_frame(self.page_two(controller)))
        quit_btn = tk.Button(self, text='Quit', command=lambda: controller.show_frame())

        quit_btn.pack()
        next_btn.pack()

    def page_two(self, controller):
        label = tk.Label(self, text='second_page', font=LARGE_FONT)
        label.pack(pady=10, padx=10)

        # how can I switch pages using the next button?
        next_btn = tk.Button(self, text='Next', command=lambda: Pages.show_frame("page_one"))
        quit_btn = tk.Button(self, text='Quit', command=lambda: Pages.show_frame())

        quit_btn.pack()
        next_btn.pack()


app = Main()
app.mainloop()

Basically, my current push is to try and use methods within my class in order to define my pages and switch between them. 基本上,我当前的努力是尝试在类中使用方法,以便定义我的页面并在它们之间进行切换。 I'm currently having some trouble, and upon taking a look at other's answers, a lot of them have the idea that each class instantiates a Frame, in which we call a method to switch between those instances. 我目前遇到了一些麻烦,在查看其他人的答案时,很多人都认为每个类都实例化一个Frame,在其中我们调用在这些实例之间切换的方法。

Let me know your thoughts on this process to get me up to speed about how I should be tackling this project. 让我知道您对这个过程的想法,以使我快速了解如何应对这个项目。

Many thanks in advance for your help - I really want to get this OOP stuff down. 在此先感谢您的帮助-我真的很想把这个OOP的东西放下来。

You could theoretically make methods in a single class reconfigure the UI the way you want, but it's probably not going to be easy. 从理论上讲,您可以使单个类中的方法以所需的方式重新配置UI,但这可能并不容易。

Your current code can't work because there's no simple way for one of your methods to clean up the work done by another previous method (eg by getting rid of the label and buttons it created). 您当前的代码无法正常工作,因为您的方法中没有一种简单的方法可以清除另一种先前方法所完成的工作(例如,摆脱其创建的标签和按钮)。

The original answer you linked to avoided that issue by having all the widgets created at the start of the program (not only when they're about to be displayed). 您链接到的原始答案通过在程序开始时创建所有窗口小部件(不仅是要显示它们的时候)来避免了该问题。 Only one page is displayed at a time though, since they're all Frames that have been configured to display in the same location. 但是,一次只显示一页,因为它们都是配置为在同一位置显示的框架。 The Frame that is on top hides the others and prevents their widgets from doing anything. 顶部的框架隐藏其他框架,并阻止其小部件执行任何操作。 By moving a different frame on top, you can switch between the pages. 通过在顶部移动其他框架,可以在页面之间切换。

If we ignore those widget display issues, you could make your main class call the methods you've written: 如果我们忽略了这些小部件显示问题,则可以使您的主类调用您编写的方法:

class Main(tk.Tk):

    def __init__(self, *args, **kwargs):
        container = tk.Frame(self) # I'm not sure a separate container is necessary
        container.pack(side="top", fill="both", expand=True)  # since we're only putting 
        container.grid_rowconfigure(0, weight=1)              # one other frame inside it
        container.grid_columnconfigure(0, weight=1)

        page = Pages(parent=container, controller=self)
        page.grid(row=0, column=0, sticky='nsew') # moved up from below

        self.frames = {}
        for F in (page.page_one, page.page_two): # no quotes or parentheses
            page_name = F.__name__ # the names are the strings "page_one" and "page_two"
            self.frames[page_name] = F

        self.show_frame("page_one")

    def show_frame(self, page_name):
        method = self.frames[page_name]
        method() # call the method we are switching to

class Pages(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

    def page_one(self):
        # do something to clean up previous pages here?
        label = tk.Label(self, text='show_firmware_page', font=LARGE_FONT)
        label.pack(pady=10, padx=10)

        next_btn = tk.Button(self, text='Next',
                             command=lambda: self.controller.show_frame("page_two")
        next_btn.pack()

    def page_two(self):
        label = tk.Label(self, text='second_page', font=LARGE_FONT)
        label.pack(pady=10, padx=10)

        next_btn = tk.Button(self, text='Next',
                             command=lambda: self.controller.show_frame("page_one"))
        next_btn.pack()

This will work (for some definition of "work"). 这将起作用(对于“工作”的某些定义)。 I removed the quit buttons because I'm not sure exactly what the best way to handle them is (you probably don't want them to be calling show_frame ). 我删除了退出按钮,因为我不确定确切地处理它们的最佳方法是什么(您可能不希望它们调用show_frame )。

Note that I'm by no means an expert at TKinter, so it's entirely possible that there's some easy way to remove the widgets from the previous page when you've moved on to the next one. 请注意,我绝不是TKinter的专家,所以当您移至下一页时,很可能有一些简单的方法可以将上一页中的小部件删除。 I just don't know how. 我就是不知道

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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