Struktur Tkinter Applikation

Fragen zu Tkinter.
Antworten
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Hallo Zusammen,

ich habe ein Frage, die die Strukturierung meines Programmcodes betrifft.
In meiner Applikation habe ich Bereiche, die sich wie folgt aufbauen:

Eine Klasse MainFrame beinhaltet immer eine Klasse StatusFrame und DataFrame.
Im DataFrame werden alle Eingaben vom Nutzer gemacht und der StatusFrame soll ensprechend in Kurzform auflisten, was bereits vom Nutzer definiert/ eingegeben wurde.

Nun möchte ich bei Eingabe des Nutzers in ein Entry-Widget des DataFrame das Label-Widget des StatusFrame aktualisieren.
Ich habe dazu dem Entry ein StringVar() zugewiesen und diese "überwache" ich per trace()-Methode, um bei Änderungen dann das Label-Widget direkt anzupassen. Diese Methoden habe alle in die übergeordnete Klasse MainFrame gepackt.
( siehe Klasse MainFrame - die Methoden manage_binds_traces() & update_status_label() )

Nun zu meiner Frage:
Spricht irgendwas gegen diese Art der Strukturierung, wie sie unten im Detail zu sehen ist?
Dass ich die Kontrolle (trace()) im DataFrame und die Änderung des Label im StatusFrame im MainFrame abarbeite?
Oder sollte ich ggf. die trace()_methode im DataFrame selbst und die Änderung des Labels im StatusFrame getrennt behandeln?

Gruß Seb

Ps.: sorry für den langen Codeausschnitt...wollte f´ür´s Verständnis nicht zu viel rausnehmen.

Code: Alles auswählen

class MainFrame(tk.LabelFrame):
    """..."""

    def __init__(self, parent, fonts):
        super().__init__(parent)
        self.parent = parent
        self.fonts = fonts
        self.config(text='Generic Specification', font=self.fonts['header'])

        self.grid_rowconfigure((0, 1), weight=1)
        self.columnconfigure(0, weight=1)

        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()


    def create_widgets(self):
        """..."""

        self.data_frame = DataFrame(self, self.fonts)
        self.status_frame = StatusFrame(self, self.fonts, self.collapse_data_frame)


    def manage_layout(self):
        """..."""

        g_sticky = 'nswe'

        self.status_frame.grid(row=0, column=0, sticky=g_sticky)
        self.data_frame.grid(row=1, column=0, sticky=g_sticky)


    def manage_binds_traces(self):
        """..."""

        self.data_frame.nomenclature_var.trace('w', self.update_status_label)
        #self.data_frame.nomenclature_var.bind('<FocusOut>', self.update_status_label)


    def collapse_data_frame(self):
        """..."""

        frame = self.data_frame
        if frame.winfo_viewable():
            frame.grid_remove()
        else:
            frame.grid()


    def update_status_label(self, *args):
        """..."""

        nomenclature = self.data_frame.nomenclature_var.get()
        wheel_type = self.data_frame.wheel_type_var.get()
        rot_dir = self.data_frame.rot_dir_var.get()
        mb_count = self.data_frame.blade_count_var.get()
        sb_count = self.data_frame.blade_count_splitter_var.get()

        status_string = ('{nc}_{wt}_{rd} '
                         '| Count of MB: {mc} '
                         '| Count of SB: {sc}').format(nc=nomenclature,
                                                       wt=wheel_type,
                                                       rd=rot_dir,
                                                       mc=mb_count,
                                                       sc=sb_count)

        self.status_frame.status_label.config(text=status_string)


####################################################################################################

class StatusFrame(tk.Frame):
    """..."""

    def __init__(self, parent, fonts, collapse_main_frame_func):
        super().__init__(parent)
        self.parent = parent
        self.fonts = fonts
        self.collapse_main_frame = collapse_main_frame_func
        self.config(padx=5, pady=5)

        self.grid_columnconfigure(1, weight=1)

        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()


    def create_widgets(self):
        """..."""

        self.collapse_button = tk.Button(self, text='-', width=2,
                                         command=self.collapse_main_frame)
        self.status_label = tk.Label(self, text='defined', relief='ridge')
        self.test_button = tk.Button(self, text='test')


    def manage_layout(self):
        """..."""

        g_sticky = 'nswe'

        self.collapse_button.grid(row=0, column=0, sticky=g_sticky)
        self.status_label.grid(row=0, column=1, sticky=g_sticky)
        self.test_button.grid(row=0, column=3, sticky=g_sticky)


    def manage_binds_traces(self):
        """..."""

        self.collapse_button.bind('<Button-1>', self.toggle_button_text)


    def toggle_button_text(self, event=None):
        """toggle the button text between + and -"""

        btn = self.collapse_button
        if btn.cget('text') == '-':
            btn.config(text='+')
        else:
            btn.config(text='-')


####################################################################################################

class DataFrame(tk.Frame):#tk.LabelFrame):
    """..."""

    def __init__(self, parent, fonts):
        super().__init__(parent)
        self.parent = parent
        self.fonts = fonts
        self.config()#text='Generic Specification', font=self.fonts['header'])

        self.grid_rowconfigure(0, weight=1)
        # config cols
        self.grid_columnconfigure(0, weight=1)
        self.grid_columnconfigure(1, weight=3)
        for col in range(2, 6):
            self.grid_columnconfigure(col, weight=1)
        self.grid_columnconfigure((7, 9), weight=1, uniform='same_width_label')
        self.grid_columnconfigure((8, 10), weight=1, uniform='same_width_entry')

        self.create_widgets()
        self.manage_layout()
        # all ancestors of main frame
        # self.all_removable_widgets = self.grid_slaves()


    def create_widgets(self):
        """..."""

        self.label_nomenclature = tk.Label(self, text='Nomenclature:', anchor='e')
        self.nomenclature_var = tk.StringVar()
        self.entry_nomenclature = tk.Entry(self, textvariable=self.nomenclature_var,
                                           bg='light yellow', justify='center', width=10)

        self.label_wheel_type = tk.Label(self, text='Type of Wheel:', anchor='e')
        self.wheel_type_var = tk.StringVar()
        self.entry_wheel_type = tk.Entry(self, textvariable=self.wheel_type_var,
                                         bg='light yellow', justify='center', width=5)

        self.label_rot_direction = tk.Label(self, text='Rotation Direction:', anchor='e')
        self.rot_dir_var = tk.StringVar()
        self.rot_dir_var.set('cw') # initial value
        self.r_button_cw = tk.Radiobutton(self, text='cw', variable=self.rot_dir_var,
                                          value='cw')
        self.r_button_ccw = tk.Radiobutton(self, text='ccw', variable=self.rot_dir_var,
                                           value='ccw')

        self.label_blade_count = tk.Label(self, text='Count of Main Blades:', anchor='e')
        self.blade_count_var = tk.IntVar()
        self.entry_blade_count = tk.Entry(self, textvariable=self.blade_count_var,
                                          bg='light yellow', justify='center', width=5)

        self.label_blade_count_splitter = tk.Label(self, text='Count of Splitter Blades:', anchor='e')
        self.blade_count_splitter_var = tk.IntVar()
        self.entry_blade_count_splitter = tk.Entry(self, textvariable=self.blade_count_splitter_var,
                                                   bg='light yellow', justify='center', width=5)


    def manage_layout(self):
        """..."""

        g_padding = {'padx':5, 'pady':5}
        g_sticky = 'nswe'

        # row 0
        self.label_nomenclature.grid(row=0, column=0, sticky=g_sticky, **g_padding)
        self.entry_nomenclature.grid(row=0, column=1, sticky=g_sticky, **g_padding)

        self.label_wheel_type.grid(row=0, column=2, sticky=g_sticky, **g_padding)
        self.entry_wheel_type.grid(row=0, column=3, sticky=g_sticky, **g_padding)

        self.label_rot_direction.grid(row=0, column=4, sticky=g_sticky, **g_padding)
        self.r_button_cw.grid(row=0, column=5, sticky=g_sticky, **g_padding)
        self.r_button_ccw.grid(row=0, column=6, sticky=g_sticky, **g_padding)

        self.label_blade_count.grid(row=0, column=7, sticky=g_sticky, **g_padding)
        self.entry_blade_count.grid(row=0, column=8, sticky=g_sticky, **g_padding)

        self.label_blade_count_splitter.grid(row=0, column=9, sticky=g_sticky, **g_padding)
        self.entry_blade_count_splitter.grid(row=0, column=10, sticky=g_sticky, **g_padding)
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Ich bin´s nochmal:

Viell. war mein Eingang-Post etwas zu abstrakt formuliert und der Code zu unübersichtlich.
Ich versuch es mal im 2. Anlauf.
Also grundsätzlich geht es mir um saubere Objektorientierung meines Programms.
Jedoch bin ich noch unsicher was die Punkte Datenkapselung, private Attribute/ Methoden und den Zugriff (z.b. über Getter/ Setter) darauf von "Außerhalb der Klasse" angeht.
Im Folgenden habe ich zwei Varianten meines Codes vorbereitet.
[Augenemerk auf den Methoden:
update_status_label() (siehe MainFrame und/oder StatusFrame)
create_status_string() (siehe DataFrame)
]
Wie man sieht verwende ich momentan keine privaten Atribute/ Methoden.

Mich würde nun interessieren, welche der beiden Varianten eurer Meinung nach die "bessere" nach den Regeln der OOP wäre?
Und wie ich diese dann noch weiter optimieren könnte, um möglichst sauberen Code zu erhalten?

Gruß Seb

Varainte 1:

Code: Alles auswählen

import tkinter as tk

class MainFrame(tk.LabelFrame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.config(text='Generic Specification')
        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.data_frame = DataFrame(self)
        self.status_frame = StatusFrame(self)

    def manage_layout(self):
        g_sticky = 'nswe'
        self.status_frame.grid(row=0, column=0, sticky=g_sticky)
        self.data_frame.grid(row=1, column=0, sticky=g_sticky)

    def manage_binds_traces(self):
        self.data_frame.nomenclature_var.trace('w', self.update_status_label)

    def update_status_label(self, *args):
        nomenclature = self.data_frame.nomenclature_var.get()
        status_string = '_{nc}_'.format(nc=nomenclature)

        self.status_frame.status_label.config(text=status_string)

###################################################################################################

class StatusFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.status_label = tk.Label(self, text='defined', relief='ridge')

    def manage_layout(self):
        g_sticky = 'nswe'
        self.status_label.grid(row=0, column=1, sticky=g_sticky)

    def manage_binds_traces(self):
        pass

####################################################################################################

class DataFrame(tk.Frame):
 
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.label_nomenclature = tk.Label(self, text='Nomenclature:', anchor='e')
        self.nomenclature_var = tk.StringVar()
        self.entry_nomenclature = tk.Entry(self, textvariable=self.nomenclature_var,
                                           bg='light yellow', justify='center', width=10)

    def manage_layout(self):
        g_padding = {'padx':5, 'pady':5}
        g_sticky = 'nswe'
        # row 0
        self.label_nomenclature.grid(row=0, column=0, columnspan=1, sticky=g_sticky, **g_padding)
        self.entry_nomenclature.grid(row=0, column=1, columnspan=1, sticky=g_sticky, **g_padding)

    def manage_binds_traces(self):
        pass
Variante 2:

Code: Alles auswählen

import tkinter as tk

class MainFrame(tk.LabelFrame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.config(text='Generic Specification')
        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.data_frame = DataFrame(self)
        self.status_frame = StatusFrame(self)

    def manage_layout(self):
        g_sticky = 'nswe'
        self.status_frame.grid(row=0, column=0, sticky=g_sticky)
        self.data_frame.grid(row=1, column=0, sticky=g_sticky)

    def manage_binds_traces(self):
        self.data_frame.nomenclature_var.trace('w', self.update_status_label)

    def update_status_label(self, *args):
        status_string = self.data_frame.create_status_string()
        self.status_frame.update_status_label(status_string)

###################################################################################################

class StatusFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.status_label = tk.Label(self, text='defined', relief='ridge')


    def manage_layout(self):
        g_sticky = 'nswe'
        self.status_label.grid(row=0, column=1, sticky=g_sticky)

    def manage_binds_traces(self):
        pass

    def update_status_label(self, status_string):
        self.status_label.config(text=status_string)


####################################################################################################

class DataFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.create_widgets()
        self.manage_layout()
        self.manage_binds_traces()

    def create_widgets(self):
        self.label_nomenclature = tk.Label(self, text='Nomenclature:', anchor='e')
        self.nomenclature_var = tk.StringVar()
        self.entry_nomenclature = tk.Entry(self, textvariable=self.nomenclature_var,
                                           bg='light yellow', justify='center', width=10)

    def manage_layout(self):
        g_padding = {'padx':5, 'pady':5}
        g_sticky = 'nswe'
        # row 0
        self.label_nomenclature.grid(row=0, column=0, columnspan=1, sticky=g_sticky, **g_padding)
        self.entry_nomenclature.grid(row=0, column=1, columnspan=1, sticky=g_sticky, **g_padding)

    def manage_binds_traces(self):
        pass

    def create_status_string(self):
        nomenclature = self.nomenclature_var.get()
        status_string = '_{nc}_'.format(nc=nomenclature)

        return status_string
BlackJack

@Kniffte: Mir ist das alles zu abstrakt um da etwas zu sagen zu können. Was mir auf jeden Fall gar nicht gefällt ist die Einteilung der Methoden die aus der `__init__()` aufgerufen werden, weil das dazu zwingt *jedes* Widget an das Objekt zu binden, auch solche auf die nach dem initialisieren des Objekts nie wieder zugegriffen wird, die also nicht wirklich etwas sinnvolles zum Zustand beitragen.

Der Namenszusatz `manage_*()` ist ein bisschen Wischi-Waschi und kann entweder weg oder oder sollte durch etwas mit mehr Bedeutung ersetzt werden.

Falls eine Methode nur ``pass`` enthält, ist sie meistens nicht sinnvoll. Ausgenommen Methoden die zum überschreiben in abgeleiteten Klassen gedacht sind und bei denen ”nichts tun” tatsächlich eine sinnvolle Tätigkeit ist, wo also das ableiten auch Sinn macht ohne diese Methode zu implementieren. Habe ich als Beispiel gerade bei Tag 21 vom Advent Of Code, wo es Operationen gibt, die man im zweiten Aufgabenteil umdrehen muss. Und die Basisklasse für Operationen hat bei mir eine `reverse()`-Methode die nichts macht, weil bei einigen Operationen eine Umkehrung nichts machen muss, und bei anderen Code dafür ausgeführt werden muss. Beispiel „vertausche Buchstabe an Index i mit dem am Index j“ wäre vom Effekt das gleiche wie „vertausche Buchstabe an Index j mit dem am Index i“, da muss man also nicht wirklich etwas tun um die Operation umzukehren. Während bei „Rotiere um n Schritte nach rechts“ die Umkehroperation das „rechts“ durch „links“ tauschen muss, beziehungsweise das Vorzeichen bei den Schritten umkehren muss.

So ganz grob würde ich sagen: Da beide Frames die gleichen Daten anzeigen, würde ich diese Daten in ein eigenes Objekt heraus ziehen und das Objekt beiden Frames übergeben. Der eine verändert die Daten, der andere registriert sich als Listener auf Veränderungen und aktualisiert sich entsprechend.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@BlackJack:
Danke erstmal für deine Antwort, trotz der Abstraktion :)
BlackJack hat geschrieben:@Kniffte: Mir ist das alles zu abstrakt um da etwas zu sagen zu können. Was mir auf jeden Fall gar nicht gefällt ist die Einteilung der Methoden die aus der `__init__()` aufgerufen werden, weil das dazu zwingt *jedes* Widget an das Objekt zu binden, auch solche auf die nach dem initialisieren des Objekts nie wieder zugegriffen wird, die also nicht wirklich etwas sinnvolles zum Zustand beitragen.
Wenn ich dich richtig verstanden habe, dann sollte ich die Widgets, welche nicht wieder verändert werden nicht an das Objekt binden
und dann direkt in der __init__ die widgets erzeuge und das layout erstelle?

Code: Alles auswählen

class DataFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

        # create widgets
        label_nomenclature = tk.Label(self, text='Nomenclature:', anchor='e')
        self.nomenclature_var = tk.StringVar()
        self.entry_nomenclature = tk.Entry(self, textvariable=self.nomenclature_var,
                                           bg='light yellow', justify='center', width=10)

        # layout
        label_nomenclature.grid(row=0, column=0)
        self.entry_nomenclature.grid(row=0, column=1)
gleich noch eine Frage, die mir dazu einfällt:
wenn ich ein widget außerhalb der Klasse nicht benötige und nur innerhalb der Klasse verarbeite in einer Methode,
sollte ich dann das widget trotzdem an die Instanz binden und so in der Methode verarbeiten:

Code: Alles auswählen

class DataFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

        self.label_nomenclature = tk.Label(self, text='Nomenclature:')
        self.label_nomenclature.grid()

        self.do_something()

    def do_something(self):
        self.label_nomenclature.config(text='abcdefg')
oder das widget als Parameter in die Methode übergeben?

Code: Alles auswählen

class DataFrame(tk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent

        label_nomenclature = tk.Label(self, text='Nomenclature:')
        label_nomenclature.grid()

        self.do_something(label_nomenclature)

    def do_something(self, widget):
        widget.config(text='abcdefg')
BlackJack hat geschrieben: Der Namenszusatz `manage_*()` ist ein bisschen Wischi-Waschi und kann entweder weg oder oder sollte durch etwas mit mehr Bedeutung ersetzt werden.
Ok...verstanden...werde ich aussagekräftiger gestalten.
BlackJack hat geschrieben: Falls eine Methode nur ``pass`` enthält, ist sie meistens nicht sinnvoll. Ausgenommen Methoden die zum überschreiben in abgeleiteten Klassen gedacht sind und bei denen ”nichts tun” tatsächlich eine sinnvolle Tätigkeit ist, wo also das ableiten auch Sinn macht ohne diese Methode zu implementieren. Habe ich als Beispiel gerade bei Tag 21 vom Advent Of Code, wo es Operationen gibt, die man im zweiten Aufgabenteil umdrehen muss. Und die Basisklasse für Operationen hat bei mir eine `reverse()`-Methode die nichts macht, weil bei einigen Operationen eine Umkehrung nichts machen muss, und bei anderen Code dafür ausgeführt werden muss. Beispiel „vertausche Buchstabe an Index i mit dem am Index j“ wäre vom Effekt das gleiche wie „vertausche Buchstabe an Index j mit dem am Index i“, da muss man also nicht wirklich etwas tun um die Operation umzukehren. Während bei „Rotiere um n Schritte nach rechts“ die Umkehroperation das „rechts“ durch „links“ tauschen muss, beziehungsweise das Vorzeichen bei den Schritten umkehren muss.
Mir ist bewusst, dass die Methode nicht sinnvoll ist. hatte sie nur zur Übersicht drin und hätte sie final entfernt.
Aber danke für das Anwednungsbeispiel zum Überschreiben in abgeleiteten Klassen. Wieder was Neues gelernt...:)
BlackJack hat geschrieben: So ganz grob würde ich sagen: Da beide Frames die gleichen Daten anzeigen, würde ich diese Daten in ein eigenes Objekt heraus ziehen und das Objekt beiden Frames übergeben. Der eine verändert die Daten, der andere registriert sich als Listener auf Veränderungen und aktualisiert sich entsprechend.
Danke...klingt interessant. Das werde ich versuchen umzusetzen.
BlackJack

Ob man einen Wert als Attribut an das Objekt bindet oder nicht hängt davon ab ob es zum Zustand des Objektes gehört oder nicht. Das ist unabhängig davon ob man darauf von aussen oder von Methoden der (Sub)Klasse darauf zugreift.

Wobei in Deinem zweiten Beispiel `do_something()` keine Methode ist, sondern eine Funktion. Da würde ich das `self` weglassen und die ”Methode” mit ``@staticmethod`` dekorieren. Dann weiss man beim Lesen auch sofort das es sich nicht um eine Methode handelt.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

BlackJack hat geschrieben:Ob man einen Wert als Attribut an das Objekt bindet oder nicht hängt davon ab ob es zum Zustand des Objektes gehört oder nicht. Das ist unabhängig davon ob man darauf von aussen oder von Methoden der (Sub)Klasse darauf zugreift.
Also ichh hoffe, dass ich das richtig verstanden habe:
Du meinst, wenn ein Wert einer Klasse einen individuellen Zustand/Eigenschaft einer Instanz der Klasse beschreiben soll (jede Instanz der Klasse kann sich in diesem Wert von anderen Instanzen der Klasse unterscheiden), dann wird dieser Wert als Attribut an die Instanz/das erstellte Objekt gebunden. Richtig so?
BlackJack hat geschrieben: Wobei in Deinem zweiten Beispiel `do_something()` keine Methode ist, sondern eine Funktion. Da würde ich das `self` weglassen und die ”Methode” mit ``@staticmethod`` dekorieren. Dann weiss man beim Lesen auch sofort das es sich nicht um eine Methode handelt.
Also wäre dies eine Art "Klassenfunktion" die für alle im Anschluss erstellten Instanzen/Objekte der Klasse gleich ist/abgearbeitet wird? Bzw. worin liegt dann der Unterschied zum Dekorator @classmethod?
BlackJack

@Kniffte: Jup, die Erklärung klingt richtig. Wobei man noch bedenken muss ob die Attribute wirklich zu *dem* Objekt gehören. Deine Erklärung lässt ja noch Attribute zu, die semantisch nicht zusammen in eine Klasse gehören, bis hin zu Gottklassen als Extrem davon.

`staticmethod()`\s sind letztendlich wirklich nur Funktionen die ”nichts” mit den Exemplaren (Instanzen) oder der Klasse zu tun haben. Nichts stimmt natürlich nicht, denn sonst würde man sie ja nicht in der Klasse sondern ausserhalb definieren, aber ihr Code ist unabhängig von der Klasse oder dem Exemplar auf dem sie aufgerufen wird. Man kann sie also auf jedem Exemplar, oder auch der Klasse aufrufen, ohne das das einen Einfluss auf das Ergebnis hat. Das hängt wie bei Funktionen alleine von den Argumenten ab. (Mal irgendwelche Seiteneffekte wie externe Daten oder Zufallszahlen ausgenommen.)

Klassenmethoden machen irgend etwas mit der Klasse die als erstes Argument übergeben wird. In der Regel ein Exemplar davon erstellen, weil die wohl meistgenutzte Anwendung von Klassenmethoden die Implementierung von alternativen Konstruktoren sind.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@BlackJack:
Dank dir für deine ausführlichen Erklärungen.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

BlackJack hat geschrieben:So ganz grob würde ich sagen: Da beide Frames die gleichen Daten anzeigen, würde ich diese Daten in ein eigenes Objekt heraus ziehen und das Objekt beiden Frames übergeben. Der eine verändert die Daten, der andere registriert sich als Listener auf Veränderungen und aktualisiert sich entsprechend.
@BlackJack
Ich habe nochmal eine Frage bzgl. der Art dieses Objektes.
Spricht was dagegen, dieses Daten_Objekt in Form einer Klasse zu schreiben auch wenn vorerst keine Methoden oder Funtkionen, sondern nur Attribute enthalten wären? Oder sollte ich das eher über ein/ e Dictionary/ Liste in der Klasse MainFrame(tk.LabelFrame) behandeln?
BlackJack

@Kniffte: Ich würde dem Objekt ja Logik mitgeben damit man sich dort als Listener für Veränderungen registrieren kann.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

BlackJack hat geschrieben:@Kniffte: Ich würde dem Objekt ja Logik mitgeben damit man sich dort als Listener für Veränderungen registrieren kann.
Ok...als "Listener registrieren" verstehe ich in Bezug auf Tkinter wie folgt:
In meinem Daten-Objekt weise ich den Attributen tkinter-variablen (IntVar(), StringVar(), ...) zu.
Dieses Objekt übergebe ich zum einen der Klasse DataFrame(), die die Attribute im Datenobjekt entsprechend den Eingaben des Users verändert
und
zum anderen der Klasse StatusFrame(), die die Attribute im Datenobjekt (d.h. die tkinter-variablen) liest bei Veränderung.
Im StatusFrame() wende ich die .trace()-Methode auf jedes Attribut des Daten-Objekts an, um die Änderungen zu "verfolgen".
Das für mich dann als "Listener registrieren" bedeutet.

In meinem Fall hab ich die Logik aber nicht im Datenobjekt, sondern in der der Klasse StatusFrame(), die "sich registriert"/ "Änderungen vefolgt".
Ich vermute, dass ich dich noch nicht ganz richtig verstanden, wie die Logik im Datenobjekt aussehen sollte?
BlackJack

@Kniffte: Ich würde dem Datenobjekt die Möglichkeit verpassen das man sich dort als Listener registriert. Also nicht von aussen bei allem Komponenten einzeln, sondern eine Methode bei der man sagen kann „informier mich mal wenn sich irgendwas ändert“.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Nach ein wenig Kopfzerbrechen, wie ich´s im Code umsetzen kann, hat der folgende Link mir weiter geholfen. https://en.wikipedia.org/wiki/Observer_pattern
Gibt es viell. noch andere Pattern, die das umsetzen?
Antworten