面向对象的Tkinter,是gui中具有多个框架的小部件之间进行通信的最佳方式

时间:2018-04-29 09:23:09

标签: python python-3.x oop tkinter tkinter-layout

我试图找出在不同小部件之间进行通信的最佳方式是什么,小部件是从tkinter小部件继承的自定义类,并且我有几个框架(以帮助进行布局管理)。考虑以下简单的gui(为python 3编写,将tkinter更改为Tkinter for python 2):

from tkinter import Frame,Button,Tk

class GUI(Frame):
    def __init__(self, root):

        Frame.__init__(self,root)

        self.upper_frame=Frame(root)
        self.upper_frame.pack()

        self.lower_frame=Frame(root)
        self.lower_frame.pack()

        self.upper_btn1 = Button(self.upper_frame, text="upper button 1")
        self.upper_btn2 = Button(self.upper_frame, text="upper button 2")
        self.upper_btn1.grid(row=0,column=0)
        self.upper_btn2.grid(row=0,column=1)

        self.lower_btn = CustomButton(self.lower_frame, "lower button 3")
        self.lower_btn.pack()


class CustomButton(Button):
    def __init__(self,master,text):
        Button.__init__(self,master,text=text)

        self.configure(command=self.onClick)

    def onClick(self):
        print("here I want to change the text of upper button 1")  


root = Tk()
my_gui = GUI(root)
root.mainloop()

我将它们放在不同框架中的原因是因为我想在两个不同的框架中使用不同的布局管理器来创建更复杂的布局。但是,我希望lower_btn中的命令更改例如upper_btn1的属性。

但是,我无法立即从自定义类upper_btn1的实例lower_btn访问CustomButtonlower_btn的父级是frame2frame2父级是root(不是GUI实例)。如果我将lower_btn的父级更改为GUI实例,即

self.lower_btn = CustomButton(self, "lower button")

使用pack()时无法正确放置。如果我将frame1/frame2的父级更改为GUI实例,即

self.upper_frame=Frame(self)
self.upper_frame.pack()

self.lower_frame=Frame(self)
self.lower_frame.pack()

我原则上可以通过upper_btn1lower_btn访问self.master.master.upper_btn1,但是frame1/frame2未使用pack()正确放置(我不知道)明白为什么)。我当然可以将GUI实例作为单独的变量在master之上传递给CustomButton,例如

class CustomButton(Button):
    def __init__(self,master,window,text):
        Button.__init__(self,master,text=text)
        self.window=window
        self.configure(command=self.onClick)

    def onClick(self):
        self.window.upper_btn1.configure(text="new text")

然后将lower_btn的构造更改为

self.lower_btn = CustomButton(self.lower_frame,self, "lower button 3")

但这是“正确”的做法吗?

那么,重组这个gui的最佳方法是什么?我应该将GUI作为主/父参数之上的单独变量传递吗?我应该更改按钮和/或框架的主/父(并以某种方式修复布局管理的问题)?或者我应该将按钮的命令作为GUI实例的方法,以便它们可以与该GUI中的小部件通信,尽管这不像是面向对象的编程?我似乎无法找到一种感觉“正确”的面向工作对象的设计。另外,如果我将GUI实例(pack())作为主人,frame1/frame2self不起作用的解释也会受到赞赏,因为这本来是我的直觉,大多数对象导向,方法。

编辑:也许最好的方法是根本不使用框架内的框架,只使用grid()然后使用colspan / rowspan来为布局管理提供更大的灵活性。

1 个答案:

答案 0 :(得分:0)

晚了一年,但是:小部件之间进行通信的一种方法是实例化某种控制中心对象,该对象接收有关小部件状态的知识,然后强迫其他小部件对这些信息采取行动。此“管理器”功能将独立于小部件的布局。

这是根据您的示例定制的实现。想法是向下方的按钮添加.manager属性,并在单击该按钮时通知该管理器。 GUI类保持不变。

from tkinter import Frame,Button,Tk

class Manager(object):
    def __init__(self, gui):
        self.gui = gui
        self.gui.lower_btn.manager = self

    def onClick(self):
        self.gui.upper_btn2.configure(text="changed text")

class GUI(Frame):
    def __init__(self, root):

        Frame.__init__(self,root)

        self.upper_frame=Frame(root)
        self.upper_frame.pack()

        self.lower_frame=Frame(root)
        self.lower_frame.pack()

        self.upper_btn1 = Button(self.upper_frame, text="upper button 1")
        self.upper_btn2 = Button(self.upper_frame, text="upper button 2")
        self.upper_btn1.grid(row=0,column=0)
        self.upper_btn2.grid(row=0,column=1)

        self.lower_btn = CustomButton(self.lower_frame, "lower button 3")
        self.lower_btn.pack()

class CustomButton(Button):
    def __init__(self,master,text):
        Button.__init__(self,master,text=text)

        self.configure(command=self.onClick)
        self.manager = None

    def onClick(self):
        if self.manager:
            self.manager.onClick()
        else:
            print("here I want to change the text of upper button 1")  

root = Tk()
my_gui = GUI(root)
Manager(my_gui)
root.mainloop()