Объектно-ориентированный Tkinter, лучший способ связи между виджетами в графическом интерфейсе с множеством фреймов - PullRequest
0 голосов
/ 29 апреля 2018

Я пытаюсь выяснить, каков наилучший способ связи между различными виджетами, где виджеты - это пользовательские классы, наследуемые от виджетов tkinter, и у меня есть несколько фреймов (чтобы помочь с управлением макетом). Рассмотрим, например, следующий простой графический интерфейс (написанный для python 3, замените tkinter на Tkinter для 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 настроенного класса CustomButton. Родитель lower_btn - frame2, а родитель frame2 - 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_btn1 из lower_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 как отдельную переменную поверх аргумента master / parent? Должен ли я изменить основной / родительский элемент кнопок и / или рамок (и как-то решить проблемы с управлением макетом)? Или я должен сделать команды кнопок как методы экземпляра GUI, чтобы они могли взаимодействовать с виджетами в этом GUI, хотя на самом деле это не похоже на объектно-ориентированное программирование? Я не могу найти работающий объектно-ориентированный дизайн, который кажется «правильным». Кроме того, объяснение, почему pack() не работает для frame1/frame2, если я сделаю экземпляр GUI (self) мастером, также будет оценено, поскольку это был бы мой интуитивный, наиболее объектно-ориентированный подход.

Редактировать: Возможно, лучший способ - вообще не использовать фреймы внутри фреймов, а использовать только grid (), а затем использовать colspan / rowspan для большей гибкости управления компоновкой.

1 Ответ

0 голосов
/ 13 мая 2019

Год спустя, но: один из способов обмена данными между виджетами - создать экземпляр какого-либо объекта центра управления, который получает информацию о состоянии ваших виджетов, а затем заставляет другие виджеты воздействовать на эту информацию. Эта функция «менеджера» не зависит от макета ваших виджетов.

Вот реализация, адаптированная к вашему примеру. Идея состоит в том, чтобы добавить атрибут .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()
...