tkraise和Python GUI中的一些OOP问题(tkinter)

oxf4rvwz  于 2023-06-20  发布在  Python
关注(0)|答案(2)|浏览(106)

我是一个Python的初学者,对Python的基础知识和一点OOP的基础知识进行了非常少的培训。我试图制作一个可以重定向到多个页面的GUI,我的GUI如下所示:

import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as mx
from PIL import Image, ImageTk
import tkinter.scrolledtext as tkst
  
# Initialize The Planar code Changing Functionality frame 
class PGcodefixer(ttk.Frame):
    def __init__(self, container, controller):
        super().__init__(container)
        self.config(width = 950, height = 587)
        self.controller = controller
        self.pack_propagate(False)
        self.grid_propagate(False)
        self.errflag = 0
        
        # Initialize Labels
        self.img3 = Image.open(r"C:\some\path\here") 
        self.img32 = self.img3.resize((250,250), Image.Resampling.LANCZOS)
        self.img3final = ImageTk.PhotoImage(self.img32)

        # Initialize styling of ttk widgets
        self.style = ttk.Style(self)
        self.style.theme_use("alt") 
        self.style.configure("Heading.TLabel", font = ("Comic Sans MS", "20", "bold"), justify = "center", compound  = "top", background = "#568BFC", foreground = "white")
        self.style.configure("TLabel", font = ("Comic Sans MS", "15", "bold"), justify = "center", compound  = "top", background = "#568BFC", foreground = "white") 
        self.style.configure("TButton", font = ("Comic Sans MS", "11", "bold"), justify = "center", Expand = True, background = "#FCC756", foreground = "white")
        self.style.configure("TEntry", justify = "center", Expand = True, background = "#FCC756", foreground = "Black")
        self.style.configure("TFrame", background = "#568BFC") # Background color in Hexcode

# Initialize The Nonplanar code Changing Functionality frame (Work in progress)
class NPGcodefixer(ttk.Frame):
    def __init__(self,container, controller):
        super().__init__(container)
        self.controller = controller
        self.config(width = 950, height = 587)
        self.pack_propagate(False)
        self.grid_propagate(False)

        # Initialize Labels
        self.img2 = Image.open(r"C:\some\path\here") 
        self.img22 = self.img2.resize((200,200), Image.Resampling.LANCZOS)
        self.img2final = ImageTk.PhotoImage(self.img22) 

        # Initialize the styling of ttk widgets
        self.style = ttk.Style(self)
        self.style.theme_use("alt") 
        self.style.configure("TLabel", font = ("Comic Sans MS", "16", "bold"), justify = "center", compound  = "top", background = "#DDE805", foreground = "white") 
        self.style.configure("TButton", font = ("Comic Sans MS", "11", "bold"), justify = "center", Expand = True, background = "#F57FF1", foreground = "white")
        self.style.configure("TFrame", background = "#DDE805") 

# Initialize The Homepage Frame
class Homepage(ttk.Frame):
    def __init__(self, container, controller):
        super().__init__(container)
        self.controller = controller
        self.config(width = 950, height = 587)
        self.pack_propagate(False)
        self.grid_propagate(False)
            
        # Initialize Labels (The first two lines correspond to how you are supposed to put an image into tkinter using Pillow)
        self.img1 = Image.open(r"C:\some\path\here")
        self.img12 = self.img1.resize((200,200), Image.Resampling.LANCZOS) 
        self.img1final = ImageTk.PhotoImage(self.img12) 
        self.labelh1 = ttk.Label(self, image = self.img1final, text = "Welcome to the Timesaver!\n Select the function you wish to use").place(relx = 0.315, rely = 0.15)
    
        # Initialize Button
        self.buttonh1 = ttk.Button(self, text = "Planar Gcode Compiler", command = controller.showframe(self = App, frame = "PGcodefixer")).place(relx = 0.2255, rely = 0.65)
        self.buttonh2 = ttk.Button(self, text = "Nonplanar Gcode Compiler", command = controller.showframe(self = App, frame = "NPGcodefixer")).place(relx = 0.55, rely = 0.65)
        self.buttonh3 = ttk.Button(self, text = "About", command = self.AboutMsg).place(relx = 0.85, rely = 0.9)

        # Initialize the styling of ttk Widgets
        self.style = ttk.Style(self)
        self.style.theme_use("alt") # To enable the button color to be changed, standard ttk will not allow it.
        self.style.configure("TLabel", font = ("Comic Sans MS", "16", "bold"), justify = "center", compound  = "top", background = "#FFB6C1", foreground = "white") # Compound method for relative positioning of text and Image
        self.style.configure("TButton", font = ("Comic Sans MS", "11", "bold"), justify = "center", Expand = True, background = "#67EB67", foreground = "white")
        self.style.configure("TFrame", background = "#FFB6C1") # Background color in Hexcode

    # Initialize a popup about Window for those interested to know more
    def AboutMsg(self):
        mx.showinfo(title = 'About', message =
                'Multi-Purpose Timesaver ver1.0\n'
                'Designed by X')   

# 5. Initialize App as a class
class App(tk.Tk):
    def __init__(self):
        super().__init__()

        # Initializing Window
        self.title("IMPT - Internship Multi-Purpose Timesaver") # Window Title
        self.geometry("950x587") # Using Golden Ratio, haha. Just the Window Size
        self.resizable(False, False) 
        H = Homepage(self, App).pack()
        P = PGcodefixer(self,App).pack()
        N = NPGcodefixer(self,App).pack()
        self.frames = {"Homepage": H, "PGCodefixer": P, "NPGcodefixer": N}

    def showframe(self, frame):
        if frame in self.frames.keys():
            print("yes")
            self.frames[frame].tkraise(self) #The self was asked for by vscode as positional argument is missing without it.
        else:
            print("no")
            pass

# 6. Bootstrapping the App
if __name__ == "__main__":
    app = App()
    App.showframe("Homepage")
    app.mainloop()

我对三个主要问题感到困惑,并将非常感谢您的帮助。

  1. super(init)container是如何工作的?特别是对于在顶部初始化的第一帧-容器是从哪个类继承的?
    1.为什么控制器和self在代码中无法识别(即controller.function根本不执行(用print语句检查)
    1.与2有关。- 我考虑在最顶层初始化一个mixin类来存储tkraise方法(将来还有其他方法)和帧字典,但发现这相当奇怪(即它会提升一些帧(特别是它们的背景颜色和按钮颜色),但不会提升它们的按钮或其他窗口小部件,这些窗口小部件与前一帧的窗口小部件保持相同。这是实现框架tkraise方法的一个可能的解决方案吗?我目前的解决方案(就方法论而言)是一个好的解决方案吗?
    再次感谢你的帮助。很多在线tkraise方法的解决方案都使用了tk.frame,但是我还没有找到一个使用ttk.frame的解决方案,所以我很困惑。我是一个初学者在编程GUI方面(甚至一般的编程-我试图通过尝试和错误来学习),这是我在tkinter上的第一个主要步骤,所以即使你有任何提示来提供我的代码结构/糟糕的编程实践(例如:时间复杂度O(n)等)为了避免我正在做的,我会非常感谢他们以及!
    编辑:包含了几行缺失的代码,显示了tkraise的实现,并添加了控制器到控制器类的分配。
    EDIT 2:添加了控制器函数的self分配,因为它们是方法。
    EDIT 3:我还意识到在进一步的测试中str(classname)不等于“classname”,所以我改变了标准。在从按钮调用showframe方法时,它只是由于某种原因而没有执行,这真的让我很困惑。为什么会出现这种情况呢?我想在字符串列表中使用类型更改方法(if str(x)),以避免不必要地创建多个重复的类示例。我现在很失落,至于我的方法是否是甚至正确的一个在这里实施tkraise。请帮助!!
    编辑4:考虑到Roberts先生的建议,将框架的示例添加到列表中。但是现在弹出了一个AttributeError,表明self.frames不是App的属性,尽管它是App示例中初始化的字典。我该如何继续?
    修正了tkraise的错误示例为self.frames[frame].tkraise(self),并修正了import语句中的另一个错别字
t5zmwmid

t5zmwmid1#

1.* super(init)是如何工作的?*
你可不是这样的。你所拥有的是super().__init__。第一个类(PGcodefixer)继承自ttk.Frame,所以super().__init__与输入ttk.Frame.__init__是相同的。如果你的类继承自多个父类,super()的处理会更加复杂,但是如果你只是从一个基类派生,这个概念就很简单了。
1.为什么控制者和自我不被认可…
您有controller作为构造函数的参数,但是您没有将该参数保存在任何地方。如果你以后需要它,你需要做一些类似self.controller = controller的事情,以便以后可以参考它。
请记住,self在任何方面都不是魔法。这只是一个参数名。按照惯例,类方法中的第一个参数被称为self,但像所有函数参数一样,它只在函数的生命周期内持续。不像this是C++,你可以使用另一个名字,但你会赢得Python社区永恒的嘲笑。
1.我是为了钱?
我在代码中没有看到tkraise,所以我不知道你在说什么。Mixins是减少重复输入的一种有用的方法,但前提是代码真正“通用”。如果你要添加例外和特殊情况,那么这可能不是一个好主意。

chhkpiq4

chhkpiq42#

您的代码中有几个问题:

class Homepage(tk.Frame):
    def __init__(self, container, controller):
        ...
        # Initialize Button
        self.buttonh1 = ttk.Button(self, text = "Planar Gcode Compiler", command = controller.showframe(self = App, frame = "PGcodefixer")).place(relx = 0.2255, rely = 0.65)
        self.buttonh2 = ttk.Button(self, text = "Nonplanar Gcode Compiler", command = controller.showframe(self = App, frame = "NPGcodefixer")).place(relx = 0.55, rely = 0.65)
        self.buttonh3 = ttk.Button(self, text = "About", command = self.AboutMsg).place(relx = 0.85, rely = 0.9)
        ...

在上面的代码中:

  • command = controller.showframe(...)将执行controller(...) * 立即 *,并将结果(即None)分配给command选项,因此稍后单击按钮将不会执行任何操作。使用command = lambda: controller.showframe(...)代替
  • controller.showframe(self = App, frame = "...")应为controller.showframe("...")
class App(tk.Tk):
    def __init__(self):
        ...
        H = Homepage(self, App).pack()
        P = PGcodefixer(self,App).pack()
        N = NPGcodefixer(self,App).pack()
        self.frames = {"Homepage": H, "PGCodefixer": P, "NPGcodefixer": N}
   ...
  • H = Homepage(self, App).pack()将把.pack()的结果赋值给H,所以H将是None。将线分成两行:H = Homepage(...)H.pack()(实际上在这里使用.pack()并不适合您的情况)。PN也是如此。
  • 使用App作为 * 控制器 * 是错误的,请改用self
def showframe(self, frame):
        if frame in self.frames.keys():
            print("yes")
            self.frames[frame].tkraise(self) #The self was asked for by vscode as positional argument is missing without it.
        else:
            print("no")
            pass

在上面的代码中,self.frames[frame].tkraise(self)应该是self.frames[frame].tkraise()
所以更新后的代码应该如下:

class Homepage(tk.Frame):
    def __init__(self, container, controller):
        ...
        # Initialize Button
        ### use lambda in command option and pass the class name only to showframe()
        ttk.Button(self, text = "Planar Gcode Compiler", command = lambda: controller.showframe("PGcodefixer")).place(relx = 0.2255, rely = 0.65)
        ttk.Button(self, text = "Nonplanar Gcode Compiler", command = lambda: controller.showframe("NPGcodefixer")).place(relx = 0.55, rely = 0.65)
        ttk.Button(self, text = "About", command = self.AboutMsg).place(relx = 0.85, rely = 0.9)
        ...

    ...

# 5. Initialize App as a class
class App(tk.Tk):
    def __init__(self):
        ...

        # make the widget put at (0, 0) occupy all the available space
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        H = Homepage(self, self)  # use self as the controller argument
        P = PGcodefixer(self, self)
        N = NPGcodefixer(self, self)
        self.frames = {"Homepage": H, "PGcodefixer": P, "NPGcodefixer": N}

        # use grid() to overlay frames on top of each other
        H.grid(row=0, column=0, sticky="nsew")
        P.grid(row=0, column=0, sticky="nsew")
        N.grid(row=0, column=0, sticky="nsew")

    def showframe(self, frame):
        if frame in self.frames:   # .keys() is not necessary
            print("yes")
            self.frames[frame].tkraise()  # removed self argument passed to tkraise()
        else:
            print("no")

# 6. Bootstrapping the App
if __name__ == "__main__":
    app = App()
    # use the instance "app" instead of class name "App" to call .showframe(...)
    app.showframe("Homepage")
    app.mainloop()

相关问题