Bindings, Menü, ... in Funktionen auslagern... Gute Idee?

Plattformunabhängige GUIs mit wxWidgets.
Antworten
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Ich les' mich im Moment wieder ein wenig in wxPython ein und bin gerade beim Event-driven und der Übersichtlichkeit halber habe ich die einzelnen Bestandteile in eigene Funktionen ausgelagert. Das ganze sieht nun so aus:

Code ausgelagert

Meine Frage ist: Ist das eine gute Idee oder macht es zuviele Probleme? Ich würde anschließend auch meine Sizers mit den Widgets in eine eigene Funktion packen. Bei sehr vielen Sizern und vielen Widgets erhöht das die Übersichtlichkeit enorm. Jedoch braucht man bei vielen Sachen, wie eben dem self.menuQuit das self noch davor, vielleicht stört das... wenn es im Init wäre, bräuchte man das ja nicht. Ist das guter Stil oder soll ich es lassen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackVivi hat geschrieben:die einzelnen Bestandteile in eigene Funktionen ausgelagert.
Hallo BlackVivi!

Ich kann dir nur sagen was ich davon halte und wie ich es mache. Das soll nur eine Empfehlung sein.

Ich brauche länger um Dinge wieder zu finden wenn ich die Erstellung der GUI auf mehrere Funktionen verteile. Für mich ist es also viel schwerer, das Programm zu durchschauen und Fehler zu finden, wenn ich den GUI-Aufbau auf mehrere Funktionen verteile. Außerdem ist es auch mehr Schreibarbeit. Du musst Vieles in Instanzvariablen halten und musst dich immer darum kümmern, dass die benötigten Objekte auch wirklich an die Funktionen übergeben werden. -- Es macht die GUI-Erstellung, in meinen Augen, komplizierter, wenn diese auf mehrere Funktionen aufgezeilt wird.

Ein Frame bekommt bei mir immer eine eigene Klasse. Oft verwende ich für Panels, mit vielen Widgets oder einer eigenständigen Funktion, ebenfalls eigene Klassen. Also immer dann, wenn etwas gut abgetrennt werden kann (z.B. die Panels, die in ein Notebook gelegt werden), dann bekommt es eine eigene Klasse spendiert. Ich schreibe dir dafür noch ein kleines Beispiel. Später.

Zu diesem Codeschnipsel habe ich auch noch etwas zu sagen:

Code: Alles auswählen

class Frame(wx.Frame):

    def __init__(self, parent=None, id=-1, pos=wx.DefaultPosition,
                     size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
                     title="", name="frame" ):
        wx.Frame.__init__(self, parent, id, title, pos, size, style)   
Dir sollte klar sein, dass "Frame" kein guter Name für ein Frame ist. :roll:

Code: Alles auswählen

def __init__(self, parent = None, name = "Beispiel")
Mehr braucht man meistens nicht zu übergeben. Es ist nicht notwendig jeden Müll rum zu schleppen, wenn er sowiso kaum oder nie gebraucht wird.
- id: Wird nie gebraucht
- pos: selten. Und wenn doch, dann kann man es immer noch in die __init__ integrieren
- size: Wenn man alles richtig macht, dann braucht man size so gut wie nie. Die Größe des Frames wird meistens dynamisch von den darin enthaltenen Widgets bestimmt. Man braucht size nur in Ausnahmefällen. Und in diesen Fällen kann man es immer noch in die __init__ integrieren.
- style: (wie pos)
- name: Wird so gut wie nie gebraucht

Code: Alles auswählen

    def Menu(self):
        ...
        
    def Bindings(self):
        ...

    def OnMenuQuit(self, event):
        ...
Dazu auch noch ein Wort:

"Menu" ist ein Ding, ein Objekt. Aber kein Name für eine Funktion, die etwas macht. Was macht sie denn?

"Bindings" ist irgendetwas, aber keine Funktion, die Bindings erstellt. Das ist aus diesem Namen nicht ersichtlich. Gute Namen erhöhen die Lesbarkeit deines Codes.

Du willst sicher von einem Menüpunkt aus das Frame schließen. Du willst evt. auch von einem Button einer Toolbar aus dein Frame schließen. Vielleicht möchtest du nach getaner Arbeit dein Frame auch automatisch schließen. Schade, dass du "OnMenuQuit(self, event)" nur als Eventhandler für das Menü verwenden kannst. ;-) Wenn du deine Methode so definierst, dann kannst du sie für alle Anwendungsfälle verwenden, in denen du das Frame schließen willst.

Code: Alles auswählen

def close_frame(self, event = None)
``event = None`` --> damit lässt du auch zu, dass die Methode direkt, also nicht als Eventhandler aufgerufen werden kann.

Meine Vorschläge: Wobei ich aber zu bedenken gebe, dass ich persönlich das Menü und die Bindings in __init__ erstelle um beim Durchlesen des Codes ein flüssigeres Bild vor Augen zu haben (ohne ständig hin und her springen zu müssen).

Code: Alles auswählen

    def create_menu(self):
        ...
        
    def create_bindings(self):
        ...

    def close_frame(self, event = None):
        self.Close(True)
Aber jetzt mal Schluss.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Hallöschen Gerold,

das Frame war nur'n Beispiel, ich hab den richtigen Namen rausgelöscht, weil ich das für'n Projekt für mein Praktikum machen, Datenschutz etc... Also, in einem wirklichen Frame würde ich den Namen eher weniger benutzen. Ich arbeite momentan das Buch "wxPython in Action" durch und da stehen halt diese Empfehlungen, dass man diese Argumente benutzen soll und sowas. Also, dass man pos, size und so weiter überhaupt nicht braucht, wusste ich gar nicht. Deswegen bin ich dir für diesen Ratschlag überaus dankbar!

Die Sache mit dem Menu und Binding hatte ich bereits vorhin geändert, nachdem ich einen Gedanken daran verschwendet habe, hab ich mir gedacht: "Hmpf oO' einfach nur Menu ist unklug." Aber trotzdem danke. Ich benutze keine Unterstriche, weil ich gern in wxPython Programmen deren Konvention behalten möchte. Aber darüber denke ich nochmal nach :) Die Sache mit dem close_frame hatte ich auch schon vorhin gemacht, aber so sinnvoll ist es in meinem Fall nicht, da ich es sonst nicht aufrufen möchte.

Zu deiner persönliche Vorliebe: Ich hab einen tollen Codesnippet im Buch gefunden, der die Teiel sozusagen "dynamisch" erstellt und das ganze ohne redundanzen wesentlich flüssiger und schöner für mich darstellt. Der Codesnippet ist fast komplett aus dem Buch übernommen!

Code ausgelagert...

(In dem Code hab ich jetzt die Namenskonventionen gemischt, dass ist mir klar. Ich bin mir noch nicht sicher, wie ich es genau mache :/ Ich tendiere aber natürlich zu PEP8, obwohl wxPython sagt, dass man sich an der Namenskonvention von wx orientieren soll...)

Eigentlich ist es Geschmackssache, so gefällt es mir aber recht gut. Naja... ich schau noch, wie ich das mache. Dynamisch ist für mich immer besser als redundant. Die restlichen Widgets werde ich wohl dann auch so ähnlich erstellen, damit komm ich dann besser zurecht.

Vielen dank für die zahlreichen Ratschläge :) (Bin offen für weiteres, keine Frage!)
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackVivi hat geschrieben:Ich tendiere aber natürlich zu PEP8, obwohl wxPython sagt, dass man sich an der Namenskonvention von wx orientieren soll...
Hallo BlackVivi!

- Wenn du dich an PEP8 hälst, dann gibt es keine ungewünschten Namensüberschneidungen. Z.B.: "close" überschreibt nicht die eingebaute Methode "Close". "on_close" überschreibt nicht "OnClose". So können weniger unerklärliche Fehler passieren.

- wxWidgets ist in C++ geschrieben. Dort werden die Namen groß/klein geschrieben. Der Entwickler von wxPython kommt auch aus der C++-Schiene. Niemand erwartet, dass er ständig umdenken muss. Und dass seine Empfehlung "groß/klein" lautet, verwundert mich auch nicht.

- Ich halte mich an die Empfehlung für Python-Programme.

- Wenn du eine Erweitung für wxPython schreiben möchtest, dann musst du dich dabei an die Forderung von Dunn halten. Wenn nicht, dann wird er deinen Code nicht in wxPython integrieren. Sonst gibt es keinen Grund dafür.

Wenn ich etwas hasse, dann das Mischen der Namenskonventionen. Warum soll ich beim Schreiben von Python-Programmen, zwei Konventionen benutzen und im Kopf behalten müssen, welche Methode nun klein und welche groß/klein geschrieben wurde... :K

Das ist gleich wie bei JavaScript. "innerHTML" und "getElementById" sind so typische Negativbeispiele für diese Schreibweise. Warum wird ID nicht auch komplett groß geschrieben? Warum wird HTML groß geschrieben? (innerHtml) So muss ich mir nicht nur den Namen, sondern auch noch die exakte Schreibweise des Befehls merken! Nicht mir mir! Nicht bei meinem miesen Gedächtnis. "inner_html" und "get_element_by_id" --> Da müsste ich mir nur den Namen bzw. die Worte merken, aus denen der Befehl besteht. Aber warum einfach, wenn es kompliziert auch geht?

lg
Gerold
;-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

ich bevorzuge eher diese Variante: http://www.python-forum.de/topic-6891.html
D. h. auslagern des Codes für best. Elemente, wie Menüs, Buttons des Hauptframes, usw. in Extramodule, die möglichst übersichtlich gestaltet werden.

Gleich drei Funktionen zur Menubar im Hauptframe zu haben, finde ich - wegen der Verschachtelung -- eher unübersichtlich. Man muß ja ständig suchen in welcher Funktion welche Funktionalität ist. Insbesondere bei größeren Projekten wird das schnell unübersichtlich.

Gruß,
Christian

PS Sorry für die Eigenwerbung ;-)
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

Hallöschen Gerold ^_^,

fantastische Argumente, alle ersichtlich. Noch mehr Gründe zu pep8 zu tendieren, ich denke ich werde den Ratschlag aus wxPython in Action einfach übergehen und wirklich für Funktionen lowercase_with_underscores benutzen usw...

Du wolltest du mir noch ein Beispiel geben, ich hoffe das machst du noch, ich würde mich sehr freuen.

@CM
Dein Beispiel sieht sehr schick aus und ist auch recht logisch gecoded. Aber für mein Projekt ist es doch wohl zu unübersichtlich. 3 Funktionen zu benutzen für die Menubar erscheint etwas viel, aber es ist ja klar ersichtlich, was jeder Teil tut, zumindest für mich. Ich will jetzt nicht dein Beispiel runter machen oder meins zum absolut tollsten machen, immerhin stammt es nicht einmal von mir, aber das ganze so auszulagern find ich doch für mich etwas zu krass. Bei mir seh ich sofort welche Menüpunkte es gibt und dank der Einrückung auch was wozu gehört. Toll außerdem, das ich direkt das Binding dazuschreib.

Also tendiere ich eher zu Gerolds Vorschlag (bei so wenig Menüpunkten, es im _init_ einfach zu halten) oder eben das was ich jetzt habe, immerhin macht es das erweitern relativ einfach.

Nochmals danke für die Hilfe :) Vielleicht bau ich das nochmal auf 2 Funktionen um und versuch nochmal bessere namen zu finden für die Variablen.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

BlackVivi, hast schon recht, wenn Du die Vor- und Nachteile abgwiegst, wie Du's machst, aber hierzu
BlackVivi hat geschrieben: aber das ganze so auszulagern find ich doch für mich etwas zu krass.
noch eine Randnotiz:
GUIs tendieren dazu rel. viel Codezeilen zu provozieren. Und so kommt es, daß auch in Projekten von halbwegs moderater Größe leicht einige tausend Zeilen mehr oder weniger direkt mit dem Mainframe verbunden sein können. (Und ich bemühe mich kurz zu schreiben ;-) .) Ohne Auslagerung ist das nicht zu machen. So hat man in __init__ eine importierte Funktion,die einmal aufgerufen wird und bei der klar ist, was sie macht und wo man sie findet - und der Mainframe bleibt in einer Datei, die nicht allzu aufgeblasen ist.

Gruß,
Christian
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BlackVivi hat geschrieben:Der Codesnippet ist fast komplett aus dem Buch übernommen! Code ausgelagert...
Hallo BlackVivi!

Das betrifft jetzt nur die Menüerstellung.

Hier bleibt es wohl dem Programmierer überlassen. Das Beispiel oben ist nicht schlecht. Hier überwiegen eher die persönlichen Vorlieben.

Das hier habe ich einfach so, ohne irgendwo nachsehen zu müssen, hingeschrieben. Es ist also, nur für mich persönlich, die einfachere Variante.:

http://paste.pocoo.org/show/9114/

Wenn ich die in deinem Beispiel gezeigten Funktionen nicht irgendwoher kopieren kann, dann bin ich mit meinem Beispiel sicher schneller -- da ich es nicht mehr entwickeln muss.

Wie schon geschrieben. Hier entscheidet eher die Vorliebe des Programmierers. :D

mfg
Gerold
:-)
Zuletzt geändert von gerold am Montag 5. November 2007, 16:30, insgesamt 2-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

@CM
Das ist ein sehr greifendes Argument, wie oft hab ich schon durch Quelltexte gesehen und nicht direkt gefunden, wo das erstellt wird usw. Aber ich sehe hier einen direkten Unterschied zwischen Gerold und dir:

Gerold: "So offensichtliche Sachen muss man so schnell wie möglich finden, Redundanz wird einfach "weggescrollt". Sowas entwickelt sich schneller und ist im Notfall auch leichter zu entwirren.

CM: "Menübars oder Statusbars müssen offensichtlich getrennt werden vom Frame, da sonst das wesentliche nicht mehr so in's Auge fällt. Ich sehe: "Aha, das importierte Modul macht die Menübar" und schon öffne ich die und hab den ganzen Code."

Beide Varianten sind für mich super, wenn ich ehrlich bin. Und im Moment mach ich'n Zwischending, finde ich. Aber naja,.... Ich werd einfach noch weiter entwickeln und wenn ich merke: "Ach Mist, ich scroll und such mich zu tode"... dann weiß ich ja, dass ich was ändern muss. Aber vielen Dank! Ich komm nochmal drauf zurück. Der Faden ist bestimmt auch hilfreich für die meisten anderen WX Programmierer...

(Nochmal ohne Namensmist)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Ja, BlackVivi, mit Deiner Beschreibung triffst Du den Nagel auf den Kopf. Und ich habe mir gerade Gerolds Beispiel angeschaut und gedacht: "Nee, bloß nicht!" ;-) -- ist eben doch Geschmackssache. Auf "meine" Variante bin ich auch erst verfallen, als bei mir erste Probleme wegen Unübersichtlichkeit aufkamen. Bei kleinen GUIs spielt das wohl gar keine Rolle. (Inzwischen ist bei mir daheim der Code auch PEP8-konform *hust*)

Gruß,
Christian
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

gerold hat geschrieben:Ein Frame bekommt bei mir immer eine eigene Klasse. Oft verwende ich für Panels, mit vielen Widgets oder einer eigenständigen Funktion, ebenfalls eigene Klassen. Also immer dann, wenn etwas gut abgetrennt werden kann (z.B. die Panels, die in ein Notebook gelegt werden), dann bekommt es eine eigene Klasse spendiert. Ich schreibe dir dafür noch ein kleines Beispiel.
Hallo BlackVivi!

Ich bin mir im Moment nur nicht so sicher, ob dir dieses Beispiel jetzt schon etwas bringen wird. Außerdem habe ich jetzt nicht sonderlich auf Sauberkeit und Kommentare geachtet. :K

http://paste.pocoo.org/show/9123/

Ich habe das Grundgerüst aus http://www.python-forum.de/topic-5722.html und die Events aus http://www.python-forum.de/topic-9977.html genommen und leicht angepasst.

Wenn es dir nichts bringt oder du einfach noch nicht damit klar kommst -- einfach ignorieren und vergessen.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
BlackVivi
User
Beiträge: 762
Registriert: Samstag 9. Dezember 2006, 14:29
Kontaktdaten:

gerold hat geschrieben:http://paste.pocoo.org/show/9123/

Ich habe das Grundgerüst aus http://www.python-forum.de/topic-5722.html und die Events aus http://www.python-forum.de/topic-9977.html genommen und leicht angepasst.

Wenn es dir nichts bringt oder du einfach noch nicht damit klar kommst -- einfach ignorieren und vergessen.

mfg
Gerold
:-)
Dankeschön Gerold :) Einen Großteil davon versteh ich schon, nur mit den eigenen Events und Panels, daran arbeite ich halt noch ganz vehement ^_^ Aber dankeschön. Ich werde mir das Beispiel bookmarken.

Ich glaube, ich werde mal eine Seite in's Wiki schreiben mit dem Thema und den ganzen Codesnippets hier und auch die Sache mit den Namenskonvention. Ich denke, dass könnte ganz interessant sein.

Ihr seid die besten, und auch nochmal ein spezielles dank an CM und Gerold :)
Antworten