【问题标题】:How to properly access class functions from another Class python如何从另一个类 python 正确访问类函数
【发布时间】:2019-04-03 03:17:27
【问题描述】:

假设我有一组使用 Tkinter 的类:

class MAIN(tk.Tk):
    def __init__(self, *args):
    ...
class Page1(tk.Frame): #Login page Frame
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        ...
        def Method(self):
        ...
class Page2(tk.Frame): 
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        ...

Page2需要从Page1中使用Method(self),会怎么设计呢? 假设我们有 Tkinter 帧,每个帧都以类格式编写,所有帧都在 MAIN 类中启动。

我正在尝试做的,有一段时间没有成功,是让我的主菜单页面更新它的列表框,它接受并显示来自数据库的参数。例如,当我使用Page2 在数据库中插入另一行时,它必须让我回到Page1,但是,Page1 是静态的并且不会更新。我正在学习 UI,并且坚持了好几天。我有一个删除列表框参数的函数和一个再次显示参数的函数,但是,它们在Page1 中,一旦我从Page2 提出Page1 备份,Page1 将不会更新参数。

编辑:这是我需要在其他地方使用的方法的类(继续下面)

class mainMenuPage(tk.Frame): #Main Menu Frame, consists of the main navigation page, List box
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        self.controller = controller

        self.listBox()


        #self.listBox_delete(numrow)

        self.label = tk.Label(self, text="Search employee by typing his/her last name:")
        self.label.grid(row=0, column=1)

        self.entry_username = tk.Entry(self)
        self.entry_username.grid(row=0, column=3)


        button = tk.Button(self, text='Add Employee', width=15,
                            command=lambda: (controller.clear_shared_data(), controller.raise_frame(addEmpFrame)))
        button.grid(row=2, column=3)
        button = tk.Button(self, text='Remove Employee', width=15, command=lambda: controller.raise_frame(removeEmpFrame))
        button.grid(row=3, column=3)
        button = tk.Button(self, text='Employee Lookup', width=15, command=lambda: controller.raise_frame(EmpLookupFrame))
        button.grid(row=4, column=3)
        button = tk.Button(self, text='Calculate Pay Roll', width=15, command=lambda: controller.raise_frame(calcPayRollFrame))
        button.grid(row=5, column=3)

    def listBox(self):
        self.row = []

        self.scrollbar = tk.Scrollbar(self)
        self.scrollbar.grid(row=2, column=2)

        connection: Connection = pymysql.connect(host='localhost',
                                                 port=3306,
                                                 user='root',
                                                 password='root',
                                                 db='hrdb')
        cursorObject = connection.cursor()
        cursorObject.execute('SELECT `firstName`,`lastName` from `employeeprofile`')
        numrows = int(cursorObject.rowcount)
        for x in range(0, numrows):
            self.row.append(cursorObject.fetchone())

        self.list1 = tk.Listbox(self, height=10, width=35)
        for x in self.row:
            self.list1.insert(END, x)
        self.list1.grid(row=2, column=0, columnspan=2)

        self.scrollbar.config(command=self.list1.yview)

        return numrows

    def listBox_delete(self, numrow):
        self.list1 = tk.Listbox(self, height=10, width=35)
        for x in range(0, numrow):
            self.list1.delete('0', 'end')
        self.list1.grid(row=2, column=0, columnspan=2)
        self.list1 = tk.Listbox(self, height=10, width=35)

该类是我的 UI 的框架,基本上是一个主菜单导航器,其中包含来自 mySQL 的值的列表框。这里的问题是在程序中使用任何其他框架或操作导致我返回主菜单后,程序不会更新列表框list1,即当我将employee 添加到系统中时,新员工仅在我重新启动应用程序后才会显示在列表框中。 (这些方法适用于数据库)。我知道原因是框架是静态的,我的列表框只创建了一次,此后没有任何操作,但是,我找不到一种方法来基本上每次调用框架时生成列表框,然后得到所有再次从数据库中的值更新列表框。

【问题讨论】:

    标签: python class user-interface tkinter


    【解决方案1】:

    从另一帧引用一个帧的唯一方法是通过控制器。考虑这个例子:

    import tkinter as tk
    
    class MAIN(tk.Tk):
        def __init__(self, *args):
            tk.Tk.__init__(self)
    
            container = tk.Frame(self)
            container.pack(side="top", fill="both", expand=True)
    
            self.frames = {}
            for F in (Page1, Page2):
                page_name = F.__name__
                frame = F(parent=container, controller=self)
                self.frames[page_name] = frame
    
                frame.grid(row=0, column=0, sticky="nsew")
    
        def show_frame(self, page_name):
            frame = self.frames[page_name]
            frame.tkraise()
    
        def get_frame(self, page_name):
            frame = self.frames[page_name]
            return frame
    
    
    class Page1(tk.Frame):
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            tk.Label(self, text='Page 1').pack()
    
        def Method(self):
            print('Page 1 method')
    
    
    class Page2(tk.Frame): 
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            self.controller = controller
            tk.Label(self, text='Page 2').pack()
            tk.Button(self, text='Raise Page 1 and execute function', command=self.exit).pack()
    
        def exit(self):
            self.controller.show_frame("Page1")
            self.controller.get_frame("Page1").Method()
    
    
    app = MAIN()
    app.mainloop()
    

    Page1 没有对Page2 的引用,但它确实有controller,这是对MAIN 实例的引用。因为MAIN 是控制器,所以它“知道”所有帧。我给MAIN 提供了get_frame 方法,该方法通过其名称返回对框架的引用。因此,要从任何其他框架引用Page1,您可以使用self.controller.get_frame("Page1")。然后调用方法Method(),只需将其添加为self.controller.get_frame("Page1").Method()

    因为您没有显示实际的主类,所以我不确定您如何保存对帧的引用,但我认为您使用 this 作为开始。从我的例子来看,我认为这个概念应该很清楚。


    实现您想要的另一种方法是确保每次Page1 可见时调用一个方法,按照the answer to How would I make a method which is run every time a frame is shown in tkinter 中的说明进行操作。

    【讨论】:

    • 我正在使用控制器,是的,但是由于列表框的生成发生在其中一个框架类中,控制器无法真正访问它(因为我有一个主类的对象,只是就像你描述的那样)。这基本上就是我的并发症的来源。但是,我不知道第二种方式,我将尽快尝试实施。非常感谢,它所描述的方式正是我所需要的(基本上listBox() 每次显示框架时都会运行。
    • 一个更好的解决方案是给控制器一个返回页面实例的方法(例如:self.controller.get_page('Page1')。这样各个页面不需要知道内部实现细节控制器。
    • @BryanOakley 介意详细说明一下吗?假设我有一个带有控制器的主要GUI 类,而mainMenuPage 是我需要在def raise_frame:... tkraise() 引发时不断刷新的类,有没有一种方法可以使用控制器不断重新生成框架?跨度>
    • @Yurius 我相信@BryanOakley 的意思是我直接从Page2 访问了控制器的一个属性(controller.frames)。这意味着框架对控制器的内部工作有一些了解,这不是必需的。如果框架应该能够从控制器获取对另一个框架的引用,则控制器应该有一个方法来执行此操作。这使得框架只需要通过方法与控制器进行通信。我改变了我的答案,以纳入我认为 Bryan 的意思。
    • @fhdrsdg 非常感谢您的回复和您的时间。我刚刚开始工作。感谢你们俩
    猜你喜欢
    • 1970-01-01
    • 2018-11-05
    • 2020-03-05
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多