Dropdown menü deaktivieren

Fragen zu Tkinter.
Marvin75854

Hallo,

ich schreibe gerade meine Bachelorarbeit und muss dafür ein kleines Programm schreiben. Jedenfalls habe ich jetzt eine GUI mit relativ vielen Drop down menüs und Eingabefeldern. Besteht irgendwie die Möglichkeit, dass ich einige Eingabefelder und Dropdown menüs deaktiviere oder ausgraue, sobald ich ein anderes Dropdown menü benutze? Im Hintergrund funktioniert soweit alles. Es geht hier mehr oder weniger nur um die Übersichtlichkeit der GUI. Es soll auch nicht bei einer bestimmten Auswahl deaktiviert werden, sondern einfach sobald ich irgendetwas auswähle.

Kann man die Auswahlmöglichkeiten eines Dropdown menüs in zwei oder mehrere Kategorien unterteilen? Da ich bei mehreren Dropdown menüs teilweise 15+ Auswahlmöglichkeiten habe, wird das doch recht unübersichtlich

Gruß
Marvin
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Ja die Möglichkeit des deaktivierens besteht. Die entsprechenden Eingabeelemente haben einen 'state' den man auf `tk.DISABLED` bzw. `tk.NORMAL` setzen kann.

Menüs in Kategorien unterteilen geht ganz gut mit Untermenüs.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

Danke für die schnelle Antwort. Ich verstehe aber nicht ganz wie ich das umsetzen kann. Bei normalen Buttons funktioniert es. Allerdings will ich die Menüs nicht von Anfang an deaktivieren sondern erst sobald ich ein anderes Menü benutze. Der Code ist beispielsweise:

tkvar1 = StringVar(nb.tab1)

choices1 = ("",50,100)
tkvar1.set("")

popupMenu = OptionMenu(nb.tab1, tkvar1, *choices1)
popupMenu.grid(row=24, column=15)
#
#
#
tkvar2 = StringVar(nb.tab1)

choices2 = (""150,200)
tkvar2.set("") # set the default option

popupMenu = OptionMenu(nb.tab1, tkvar2, *choices2)
popupMenu.grid(row=25, column=15)
#

Jetzt will ich entweder bei dem ersten Menü oder bei dem zweiten Menü etwas auswählen können. Das heißt sobald ich bei dem ersten 50 auswähle, soll das zweite Menü deaktiviert werden und erst wenn ich wieder "" auswähle aktiviert werden und andersrum genauso. Kann man das vielleicht mit einer If abfrage oder ähnlichem umsetzen oder gibt es da sogar eine leichtere Möglichkeit? Ich habe mittlerweile ca. 20 Menüs und nochmal genauso viele Eingabefelder die ich so irgendwie in Verbindung miteinander bringen will.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Verbindung, wann welche Felder aktiv sind und wann nicht, mußt Du entweder in einen Algorithmus, eine Datenstruktur oder viele ifs verpacken. Jedes StringVar hat einen trace-Callback, in dem man dann die Änderungen durchführen kann.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Du kannst ja ein `command` an die `OptionMenu`-Objekte übergeben und da dann prüfen was ausgewählt wurde und entsprechend andere `OptionMenu`-Objekte (de)aktivieren. Man könnte sich eine Klasse schreiben die alle zusammengehörigen `OptionMenu`\s kennt und entsprechend dafür sorgt das alle bis auf eines eine Auswahl haben kann.

Ich hoffe das sind nicht die tatsächlichen Namen in Deinem Programm. Den Leser interessiert ja weder nur was für einen Typ der Wert hat, noch der wievielte Wert dieses Typs das ist.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

OK. Trace-Callback ist einfach ein Rückgabewert? Also in meinem Fall eben das was ich in dem Menü ausgewählt habe? Ich denke ich verstehe das Vorgehen, habe aber keine Ahnung wie ich das umsetze. Wie komme ich an den trace-Callback?

Wenn ich ein command mit beispielsweise einer Funktion anhänge, passiert einfach gar nichts. Ich kann nicht mal mit print etwas ausgeben.

Doch so oder so ähnlich sind die Namen.
Ich habe halt keine Ahnung von Programmieren. Vor Jahren ein bisschen c++ gehabt und jetzt versuch ich mir das mit Google irgendwie beizubringen. Habe mir jetzt schon über 1000 Zeilen zusammenkopiert und das irgendwie hingefuscht. Learning by Doing. Habe diese Funktion mit dem Dropdown Menü irgendwo gefunden und die Namen dann einfach ein bisschen angepasst damit ich weiß was was ist und es später irgendwo anders aufrufen kann.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der Trace-Callback ist die Funktion bzw. der Aufruf selbiger, die Du bei dem `*Variable`-Objekt mittels der `trace()`-Methode an ein Ereignis wie Lesen oder Schreiben des Wertes in dem `*Variable`-Objekt bindest. Siehe auch The Variable Classes (BooleanVar, DoubleVar, IntVar, StringVar).

Also so ähnlich wie ein `command` beim `OptionMenu`-Objekt nur eben auf dem `*Variable`-Objekt. Wenn da bei Dir nichts passiert, dann machst Du etwas falsch. Bei mir wird bei jedem Wechsel des Wertes die Rückruffunktion mit der Auswahl als Argument aufgerufen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

Ich habe das jetzt mal mit einer If-Abfrage in der Funktion callback gemacht:
...
tkvarLF1.trace("w", callback)
...

def callback(*args):
if tkvarLF1.get() == 'Barriere - UN R95':
TW_1.config(state="disabled")
popupMenu1.config(state="disabled")

else:
TW_1.config(state="normal")
popupMenu1.config(state="normal")

Funktioniert super. Vielen Dank für eure Hilfe!

Habe mir natürlich schon Gedanken darüber gemacht was ein echter Programmierer denkt, wenn er mal einen Blick in meinen Code wirft. Habt ihr da irgendwelche Tipps was die Namen angeht? Gibt es da eine bestimmte Nomenklatur oder kann ich die benennen wie ich will, solange es möglichst übersichtlich bleibt?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Namen sind ja für den menschlichen Leser, dementsprechend sollte der möglichst viel davon haben ihn zu lesen. Generische, durchnummerierte Namen bringen ihm da sehr wenig. Kryptische Abkürzungen auch nicht. Statt `popupMenu1` möchte der Leser beispielsweise `target_planet_menu` lesen wenn das ein Menü ist in dem man den Zielplaneten auswählen kann. Mal so als Beispiel.

Ansonsten gibt es auch noch allgemeine Richtlinien was die Namensschreibweisen angehen, die pro Sprache oder Projekt eingehalten werden sollten. Bei Python ist das: alles ausser Konstanten (KOMPLETT_IN_GROSSBUCHSTABEN) und Klassen (MixdCase) wird klein_mit_unterstrichen benannt. Siehe auch den Style Guide for Python Code.

Wenn ich mir die Namen so anschaue, in Betracht ziehe was Du über die Menge der Widgets gesagt hast, und dann sehe dass das offenbar alles auf Modulebene existiert, würde ich behaupten ein Programmierer würde, nachdem er den Schock überwunden hat, ganz schnell den Editor schliessen. ;-)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

Ich werde es mehr berücksichtigen und mir den Link auch mal angucken. Habe als Anfänger halt einfach erst mal angefangen und alles irgendwie runtergeschrieben und versucht das Programm zum laufen zu kriegen. Muss generell den ganzen Code nochmal überarbeiten und übersichtlicher machen.
Vielen Dank für die Hilfe!
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Marvin75854

Hier eine Snippet Variante: (In plain python not optimized!)

Code: Alles auswählen

from functools import partial

import tkinter as tk

APP_TITLE = "OptionButton"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 350
APP_HEIGHT = 200


class Application(tk.Frame):

    def __init__(self, main_win):
        self.main_win = main_win
        tk.Frame.__init__(self, main_win)
        
        self.tkvar1 = tk.StringVar(main_win, "")
        self.choices1 = ("", 50, 100, 'Barriere - UN R95')
        self.tkvar1.trace("w", partial(self.callback, "option-1", self.tkvar1))

        self.popupMenu1 = tk.OptionMenu(self, self.tkvar1, *self.choices1)
        self.popupMenu1.grid(row=0, column=0)

        
        self.tkvar2 = tk.StringVar(main_win, "")
        self.choices2 = ["", 150, 200]
        self.tkvar2.trace("w", partial(self.callback, "option-2", self.tkvar2))
        
        self.popupMenu2 = tk.OptionMenu(self, self.tkvar2, *self.choices2)
        self.popupMenu2.grid(row=0, column=1)
    
    def callback(self, opt_menu, var, *args):
        value = var.get()
        if opt_menu == "option-1":
            if value == 'Barriere - UN R95':
                #TW_1.config(state="disabled")
                self.popupMenu1.config(state="disabled")
            else:
                #TW_1.config(state="normal")
                self.popupMenu1.config(state="normal")
            
        elif opt_menu =="option-2":
            print(opt_menu, value)

               
def main():
    main_win = tk.Tk()
    main_win.title(APP_TITLE)
    main_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    #main_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = Application(main_win)
    app.pack(fill='both', expand=True, padx=100, pady=100)
    
    main_win.mainloop()
 
 
if __name__ == '__main__':
    main()
Gruss wuf ;-)
Take it easy Mates!
Marvin75854

Vielen Dank Wuf, habe deine Nachricht gerade erst gesehen. So ähnlich habe ich es jetzt auch. Sieht etwas "improvisierter" aus aber vom Prinzip her gleich und es funktioniert :D

Wo ich deinen Code jetzt gerade sehe, habe ich aber noch mal eine andere Frage. Ich verstehe nicht ganz wofür ich eine main Methode brauche. Im moment habe ich meinen Code einfach so runtergeschrieben. Die GUI mit allen Feldern und buttons usw. erstellt und die Funktionen für Berechnungen im Hintergrund und die Ausgabe der Ergebisse rufe ich immer in anderen Funktionen auf oder über verschiedene Buttons auf. Die Buttons habe ich sowieso und ich kann die Funktionen ja sowieso einfach so aufrufen. Was genau bringt mir eine main methode, abgesehen davon, dass es die ganze Sache oft wahrscheinlich einfach übersichtlicher macht?

Dann sind mittlerweile schon einige Zeilen zusammengekommen und meine GUI hat 3 tabs. Meine Idee war jetzt den Code auf mehrere Datein aufzuteilen um es übersichtlicher zu machen. Beispielsweise für jeden Tab eine neue Datei oder bestimmte Klassen ausgliedern. Allerdings habe ich woanders gelesen, dass es bei python zwar möglich ist, es aber eigentlich nicht macht, sondern alles so runterschreibt. In welchen Fällen kann man das denn trotzdem machen bzw. warum macht es keinen Sinn? Ich sehe bei meinem jetzigen Wissenstand da eigentlich keine Nachteile. Diese ganze Objekt orientierte Programmierung habe ich noch nicht ganz verstanden, habe aber das gefühl, dass wenn ich meinen Code noch verbessern kann, wird es sogar noch mehr Sinn machen einige Teile auszugliedern. Werde am Ende mit Leerzeilen und Kommentaren bestimmt auf 3500-4000 Zeilen kommen.

Gruß
Marvin
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Marvin75854

Wenn hier im Forum Skriptteile platziert werden die nicht ausführbar sind bin ich gezwungen das fehlende selber zu ergänzen. Noch mühsamer wird es, wenn diese Skriptteile (Codefetzen :-)) nicht einmal in den im Editor einfach einsetzbaren Python-Code-Tags korrekt Eingerückt präsentiert werden. Für solche Fälle verwende ich mein folgendes standard Template um ein Skriptproblem auch praktisch nach vollziehen zu können:

Code: Alles auswählen

from functools import partial
import tkinter as tk

APP_TITLE = "TkTemplate_Py3_Idiom_01"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 350
APP_HEIGHT = 200


class Application(tk.Frame):

    def __init__(self, master):
        self.master = master
        tk.Frame.__init__(self, master)
           
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = Application(app_win)
    app.pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()      
Da ich nicht linear (alles an einem Haufen auf Modulebene) sondern möglichst Objekt orientiert in Klassen programmiere verwende ich dieses Template. Um mehr über Objekt orientierte Programmierung zu erfahren gibt es genügend Literatur auf Papier und im Netz. Last but not least in unserem Forum mit seinen insgesamt mehr als 323118 Beiträgen. Da musst du dich schon selber schlau zu machen. Die in meinem Template verwendeten Main-Teile musst du natürlich nicht unbedingt so übernehmen. Solange du dein linear geschriebenes Skript mit bis zu 4000 Zeilen jetzt und in einem halben Jahr noch problemlos überblicken kannst sollte es für dich ja in Ordnung sein.

Gruss wuf ;-)
Take it easy Mates!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Keine Variablen. Wenn das Hauptprogramm also in einer Funktion und nicht auf Modulebene stehen soll, braucht die einen Namen. Üblich ist `main()`. Und die wird dann nicht einfach so aufgerufen, sondern nur wenn der Namen des Moduls (`__name__`) den Wert '__main__' hat. Und das ist nur der Fall wenn das Modul als Programm ausgeführt wird. Es ist *nicht* der Fall wenn man das Modul importiert. In einem anderen Modul oder in einer Python-Shell. Das heisst Du kannst das dann importieren, ohne dass das Programm ausgeführt wird. Aber Du kannst dann einzelne Funktionen aufrufen, Exemplare von Klassen erstellen und so weiter.

Das kann von anderen Modulen aus sinnvoll sein, um automatisierte Tests durchzuführen, oder Funktionen und Klassen in einem anderen Programm zu verwenden.

Und einige Werkzeuge und Bibliotheken erwarten auch das man Module ohne das da irgendwas passiert, importieren kann. Das Standardwerkzeug zur Dokumentation (Sphinx) zum Beispiel wenn man das verwendet um Docstrings direkt aus den Modulen in die Dokumentation zu übernehmen. Oder die `multiprocessing`-Bibliothek.

Man teilt auch in Python grössere Programme in mehrere Module auf. Nur nicht so extrem wie einige Leute, die von anderen Sprachen kommen das machen, also nicht grundsätzlich eine Klasse pro Modul.

3,5 bis 4 KLOC sind schon recht viel. Das würde ich aufteilen.

Kommentare gibt es hoffentlich nicht deswegen viele weil die Namen so schlecht sind‽

Eine gute Achse an der man ein GUI-Programm aufteilen kann ist Programmlogik und GUI. Das hilft auch falsche Abhängigkeiten zu erkennen, denn wenn das Modul mit der Programmlogik etwas aus dem Modul mit der GUI importieren soll, dann hat man etwas falsch gemacht.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

Ja man findet zum Programmieren glücklicherweise extrem viele Infos, ist nur teilweise trotzdem nicht so leicht zu verstehen. Habe ja erst vor 2 Wochen angefangen.
Ich verstehe den Unterschied zwischen Funktionen und Klassen einfach noch nicht so richtig. Ich habe bei mir bisher erst eine Klasse und 4 Unterklassen davon definiert und für mich ist das einfach nur eine Ansammlung von Funktionen. Ich habe eine If Funktion erstellt und je nach dem was auf der GUI eingegeben wird, wird eine andere Unterklasse erstellt und Funktionen daraus aufgerufen. Es wäre doch aber auch möglich diese Funktionen einfach ohne Klasse zu definieren und aufzurufen oder nicht?

Also für mich wäre es egal, allerdings schreibe ich meine Bachelorarbeit nicht nur für den Professor, sondern auch für ein Unternehmen. Die verstehen von Programmieren zwar genauso wenig wie ich, aber es kann ja sein, dass da trotzdem irgendwann mal jemand in den Code guckt. Es ist im Grunde genommen nur wichtig, dass das Programm läuft. Wenn ich dann am Ende noch Zeit habe, kann ich es immer noch optimieren. Ich versuche nur einfach trotzdem schon nicht zu viel zu "pfuschen" und das einigermaßen vernünftig aufzubauen.

Also meine Idee wäre ein Hauptprogramm zu erstellen in dem die main Funktion eine GUI mit allen Buttons und Eingabefeldern usw. erstellt und dann eben die Klassen und Funktionen für die Berechnungen aus anderen Modulen importiere.

Ich habe zwischen den einzelnen Funktionen und Teilen des Programms immer mehrere Kommentarzeilen eingefügt mit einer Überschrift. Einfach damit ich die entsprechende Stelle schneller finden kann. Ich habe einige Namen noch etwas angepasst. Die Abkürzungen die ich benutze machen hier schon Sinn, zumindest für mich.

__blackjack__ hat geschrieben: Freitag 20. Juli 2018, 22:07
Eine gute Achse an der man ein GUI-Programm aufteilen kann ist Programmlogik und GUI. Das hilft auch falsche Abhängigkeiten zu erkennen, denn wenn das Modul mit der Programmlogik etwas aus dem Modul mit der GUI importieren soll, dann hat man etwas falsch gemacht.
Diesen Satz verstehe ich nicht so ganz. Ich muss doch in dem Modul mit der Programmlogik etwas aus der GUI importieren, da ich auf der GUI ja Eingabe- und Auswahlfelder habe und die jeweiligen Werte für die Berechnung und Ausgabe der Ergebnisse brauche. Oder was ist mit Programmlogik gemeint? Oder meinst du damit nur, dass ich in dem Modul mit der GUI die Funktionen aufrufe und damit nur die Variablen übergebe, aber nichts importiere, bzw. nur die Funktion in dem Modul mit der GUI importiere.



Ich habe noch eine ganz andere Frage. Ich gebe auf der GUI verschiedene Sachen ein, führe im Hintergrund eine Berechnung durch und gebe dann Werte auf der GUI aus und schreibe diese in einem bestimmten Format in einer Datei raus. Jetzt würde ich gerne diese rausgeschriebene Datei zusammen mit einem Fahrzeugmodel oder einer Simulationsdatei in einem anderen Programm öffnen. Diese Datei will ich über die GUI manuell auswählen können. Ich arbeite unter LINUX. Ich erwarte natürlich nicht, dass ihr mir hier jetzt den CODE schreibt aber gibt es da irgendwelche Funktionen in python oder habt ihr Stichworte nach denen ich suchen kann? Ich habe da bisher nichts gefunden was ich so bei mir anwenden konnte.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Marvin75854 hat geschrieben: Dienstag 24. Juli 2018, 09:03 Die Abkürzungen die ich benutze machen hier schon Sinn, zumindest für mich.
Vielleicht verstehst Du heute noch, was die Abkürzungen bedeuten, in drei Wochen, z.B. nach dem Urlaub, sieht das aber ganz anders aus.

Nach Deiner Beschreibung hast Du die klassische Aufteilung in Geschäftslogik und GUI: Du schreibst von Berechnungen, die Werte bekommen. Woher die Werte kommen, ist für die Berechnung egal. Die Berechnung liefert ein Ergebnis. Was mit dem Ergebnis gemacht wird, ist für die Berechnung egal, ob das nun in eine Datei geschrieben wird, oder in einem Fenster dargestellt wird.

Die Module, die eine Berechnung durchführen sind also unabhängig von einer GUI, sollten so auch entwickelt werden, damit sie auch ohne GUI testbar sind.

Die GUI (falls sie denn überhaupt nötig ist), sammel also nur ein paar Werte aus Eingabefeldern auf, ruft damit die Berechnung auf, nimmt das Ergebnis und stellt es dar.


Wenn Du nur eine Ansammlung von Funktionen hast, dann hast Du keine Klassen. Was ist denn der gemeinsame Zustand der vier Unterklassen und was die gemeinsamen Methoden?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Zu den Klassen kann man nicht viel sagen ohne sie zu kennen. Aber wenn Du die vier Klassen sogar von einer Basisklasse abgeleitet hast, dann *kann* man das doch gar nicht nur als Funktionen sehen, es sei denn auch die Vererbung macht eigentlich auch keinen Sinn. Bei dem gezeigten Code waren ja lauter globale Variablen, für die gibt es bei Verwendung von Klassen keinen Grund/keine Ausrede mehr.

Zusätzlich zu dem was Sirius3 bereits zu den Abkürzungen gesagt hat, widersprichst Du Dir ein bisschen selbst, denn selbst wenn die Abkürzungen für Dich Sinn machen, schreibst Du ja auch das eventuell jemand von dem Unternehmen in diesen Quelltext schauen wird oder zumindets könnte. Und der muss dann rätseln was die Abkürzungen bedeuten. Bachelor und Masterarbeiten bauen auch nicht selten auf vorhandenen Arbeiten auf. Kann also gut sein, das nach Dir jemand kommt der diesen Code wiederverwenden, anpassen, und erweitern muss.

Man gewinnt auch nicht wirklich etwas durch Abkürzungen. Es ist ja nicht so, dass Buchstaben knapp wären. :-) Ausschreiben muss man es auch nur einmal, danach taucht das bei jedem vernünftigen Editor in der Autovervollständigung auf.

Kommentare habe ich in meinem Programmen eher wenige. Wofür Module, Funktionen, Klassen, Methoden da sind, steht in Docstrings. Wenn Du in den Kommentaren Überschriften drin hast, dann sieht das für mich entweder nach Docstrings aus, die als Kommentare gesetzt sind, oder nach Stellen an denen man über eine Funktion oder ein Modul nachdenken könnte, wo so ein Kommentar als Docstring geeignet wäre.

Die Notwendigkeit für solche Kommentare mit Überschriften um sich zurecht zu finden, könnte eventuell auch an den ganzen Variablen auf Modulebene liegen, also das der Code selbst keine oder sehr wenig Struktur hat.

Zum starten von externen Programmen ist das `subprocess`-Modul da.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Marvin75854

Ok. Das mit den Abkürzungen sehe ich ein. Muss mir da nochmal was einfallen lassen. Das sind teilweise nämlich nicht nur einfache Abkürzungen, sondern sogar mehrere Abkürzungen aneinander gereit. Ausgeschrieben wären das in einigen Fällen 30-40+ Zeichen. Werde mal gucken wie ich das möglichst übersichtlich und eindeutig hinkriege.

Sirius3 hat geschrieben: Dienstag 24. Juli 2018, 09:24 Nach Deiner Beschreibung hast Du die klassische Aufteilung in Geschäftslogik und GUI: Du schreibst von Berechnungen, die Werte bekommen. Woher die Werte kommen, ist für die Berechnung egal. Die Berechnung liefert ein Ergebnis. Was mit dem Ergebnis gemacht wird, ist für die Berechnung egal, ob das nun in eine Datei geschrieben wird, oder in einem Fenster dargestellt wird.

Die Module, die eine Berechnung durchführen sind also unabhängig von einer GUI, sollten so auch entwickelt werden, damit sie auch ohne GUI testbar sind.

Die GUI (falls sie denn überhaupt nötig ist), sammel also nur ein paar Werte aus Eingabefeldern auf, ruft damit die Berechnung auf, nimmt das Ergebnis und stellt es dar.
OK ja das macht sinn. Das habe ich verstanden. Nötig ist die GUI auf jeden Fall, da schon sehr viel eingegeben werden muss. Ist auch so in der Aufgabenstellung gefordert.

Sirius3 hat geschrieben: Dienstag 24. Juli 2018, 09:24 Wenn Du nur eine Ansammlung von Funktionen hast, dann hast Du keine Klassen. Was ist denn der gemeinsame Zustand der vier Unterklassen und was die gemeinsamen Methoden?
__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 10:07 @Marvin75854: Zu den Klassen kann man nicht viel sagen ohne sie zu kennen. Aber wenn Du die vier Klassen sogar von einer Basisklasse abgeleitet hast, dann *kann* man das doch gar nicht nur als Funktionen sehen, es sei denn auch die Vererbung macht eigentlich auch keinen Sinn. Bei dem gezeigten Code waren ja lauter globale Variablen, für die gibt es bei Verwendung von Klassen keinen Grund/keine Ausrede mehr.
Also in der Basisklasse habe ich 3 Funktionen. Eine um die Ergebnisse in einem bestimmten Format in eine Datei zu schreiben, eine für die Ausgabe auf der GUI und eine die etwas kleines berechnet. Und in den Tochterklassen habe ich zusätzlich in jeder Tocherklasse noch eine Funktion für eine spezifische Berechnung, deren Ergebnisse dann in der Basisklasse ausgegeben werden. Die Klassen werden dann mit einigen Variablen aufgerufen die auf der GUI eingeben werden.

__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 10:07 Kommentare habe ich in meinem Programmen eher wenige. Wofür Module, Funktionen, Klassen, Methoden da sind, steht in Docstrings. Wenn Du in den Kommentaren Überschriften drin hast, dann sieht das für mich entweder nach Docstrings aus, die als Kommentare gesetzt sind, oder nach Stellen an denen man über eine Funktion oder ein Modul nachdenken könnte, wo so ein Kommentar als Docstring geeignet wäre.
Sowohl als auch. Das meiste sind Docstrings die als Kommentare gesetzt sind. Das kann ich ja relativ schnell ändern, wenn es so üblicher ist. Habe aber auch noch ein paar "Platzhalter" bzw. Notizen wo ich zu einem späteren Zeitpunkt noch was einfügen muss. Die werden später ja sowieso gelöscht.

__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 10:07 Die Notwendigkeit für solche Kommentare mit Überschriften um sich zurecht zu finden, könnte eventuell auch an den ganzen Variablen auf Modulebene liegen, also das der Code selbst keine oder sehr wenig Struktur hat.
Naja wie gesagt. Es sind inzwischen fast 3000 Zeilen. Einige davon sind eben die Überschriften, aber auch ohne wäre das schon relativ viel. Ich kenne die Variablen und weiß was passiert, wenn ich die Codeabschnitte sehe. So ist es dann aber doch wesentlich übersichtlicher. Und ja mein Code hat absolut keine Struktur. Bin ja gerade schon dabei daran zu arbeiten. Denke, wenn ich das Programm auf mehrere Module aufteile und generell noch überarbeite, sieht das schon wesentlich besser aus. Ist nur nicht so einfach als Anfänger von anfang an alles sauber aufzubauen. Hatte da noch überhaupt keinen Überblick wie ich das Programm aufbauen soll und hab mich einfach über jede neue funktionierende Funktion gefreut.

__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 10:07 Zum starten von externen Programmen ist das `subprocess`-Modul da.
Werde ich mir mal angucken. Danke
Marvin75854

__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 10:07 @Marvin75854: Zu den Klassen kann man nicht viel sagen ohne sie zu kennen. Aber wenn Du die vier Klassen sogar von einer Basisklasse abgeleitet hast, dann *kann* man das doch gar nicht nur als Funktionen sehen, es sei denn auch die Vererbung macht eigentlich auch keinen Sinn. Bei dem gezeigten Code waren ja lauter globale Variablen, für die gibt es bei Verwendung von Klassen keinen Grund/keine Ausrede mehr.
Aber habe ich nicht sowieso trotzdem noch globale Variablen? Wenn ich einen Wert auf der GUI eingebe und den in mehreren Funktionen verwenden will? Wenn ich das richtig verstehe, habe ich innerhalb einer Klasse lokale Variablen, die eben nur in der Klasse verwendet werden können. Wenn ich jetzt aber einen Wert auf der GUI eingebe, will ich doch, dass der Wert gobal ist, damit ich ihn überall mit .get() aufrufen kann oder habe ich da einen Denkfehler?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Zu lang sollten Namen auch nicht werden. Ich hoffe da einfach mal das die so lang sind weil Du Namenspräfixe als ”Namensräume” verwendest. Was man eigentlich nicht machen muss weil Python ja Objekte hat, die Namensräume darstellen.

Deine Klassen klingen immer noch nicht so wirklich überzeugend. Weil Du beispielsweise von Funktionen sprichst und nicht von Methoden. Irgendetwas muss diese Funktionen doch mit dem Objekt verbinden das durch die Klasse modelliert wird. Damit es eben nicht einfach nur Funktionen sind. Wird denn das `self`-Argument für irgend etwas anderes verwendet als andere Funktionen auf dem Objekt aufzurufen? Und haben die Unterklassen wirklich nur eine Methode? Verwendet die `self` sinnvoll? Falls nicht wären das ja eigentlich auch nur drei Funktionen von denen man der Basisklasse eine als Argument hätte mitgeben können.

Wenn man Kommentare die eigentlich Docstring-Material enthalten leicht zu Docstrings machen kann, dann sollte man das machen. Nur noch mal zur Sicherheit: Eine literale Zeichenkette ist nur an bestimmten Stellen ein Docstring. Also normalerweise nur am Modulanfang und direkt nach ``class`` und ``def``. Einige Werkzeuge, wie Sphinx, behandeln Zeichenketten direkt *unter* Zuweisungen auch als Docstring. Irgendwo mitten im Code sind es keine Docstrings.

Docstrings bekommt man in der Python-Shell bei `help()` angezeigt, oder auch in IPython und Jupyter-Notebooks, und IDEs können die in der Regel auch (auf Wunsch) als Tooltips o.ä. anzeigen. Und man kann die mit der `autodoc`-Erweiterung mit Sphinx aus dem Quelltext direkt in die generierte Dokumentation übernehmen.

Von wo willst Du denn überall auf die `get()`-Methode von Eingabeelementen zugreifen? Es ist ja üblicherweise so, dass man eine Schaltfläche drückt oder irgendwie anders eine Berechnung anstösst. Und die Behandlung dafür sammelt dann alle Werte ein und ruft damit dann irgendwas von der Programmlogik auf.

Eventuell kann es sein das man innerhalb der GUI noch einmal Unterteilungen in eigene Widgetklassen hat, aber da braucht man ja auch keine globalen Werte, denn Widgets die solche Widgets enthalten, kennen/merken sich diese enthaltenen Widgets ja in der Klasse die sie beschreibt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten