Programmlogik und GUI verknüpfen

Plattformunabhängige GUIs mit wxWidgets.
Antworten
alan
User
Beiträge: 81
Registriert: Dienstag 10. April 2007, 11:30

Hallo

ich habe bereits einiges dazu gesucht, aber nicht wirklich etwas gefunden:

Wie trennt man Programmlogik und GUI am besten? Wobei die GUI nicht als irgendein Resource-File vorliegt, sondern als ganz normaler Python Code.

Bisher habe ich den GUI-Teil in einem Modul und das eigentlich Programm mit Funktionen und Klassen in einem zweiten.

Wie sollte ich die zwei jetzt miteinander verknüpfen? Im GUI-Modul das Programm-Modul importieren (oder andersherum?), oder ein drittes schreiben, dass die beiden importiert und aneinander bindet?
Francesco
User
Beiträge: 824
Registriert: Mittwoch 1. Dezember 2004, 12:35
Wohnort: Upper Austria

alan hat geschrieben:Hallo

ich habe bereits einiges dazu gesucht, aber nicht wirklich etwas gefunden:

Wie trennt man Programmlogik und GUI am besten? Wobei die GUI nicht als irgendein Resource-File vorliegt, sondern als ganz normaler Python Code.

Bisher habe ich den GUI-Teil in einem Modul und das eigentlich Programm mit Funktionen und Klassen in einem zweiten.

Wie sollte ich die zwei jetzt miteinander verknüpfen? Im GUI-Modul das Programm-Modul importieren (oder andersherum?), oder ein drittes schreiben, dass die beiden importiert und aneinander bindet?
Hallo alan,


Das ist auch unter MVC bekannt (Model-Viewer Control).
Ich glaube Google findet da sicher viele Seiten.

Im konkreten Fall sind vielleicht hilfreich:
http://wiki.wxpython.org/ModelViewController

http://wiki.wxpython.org/wxPython_Patterns
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

alan hat geschrieben:Wie trennt man Programmlogik und GUI am besten?
Hallo alan!

Ich kann dir kein "am besten"-Rezept geben. Ich kann dir nur aufzeigen, wie ich es in meinen Programmen mache.

Es gibt meistens zwei Module. Ein Modul mit dem Programm und ein Modul mit der GUI dazu. Im Programmmodul, ich nenne es einfach mal Hauptmodul, lese ich die Kommandozeilenparameter und die zum Programm gehörenden INI-Dateien aus. Diese Einstellungen/Optionen (wie auch immer), stelle ich in einem globalen "Settings"-Objekt zur Verfügung. Noch bevor ich die Einstellungen einlese, kümmere ich mich darum, dass ein Objekt für das Logging zur Verfügung steht.

Das sieht dann in etwa so aus:

Code: Alles auswählen

class Log(logging.RootLogger):
    """
    Stark angepasster RootLogger. Loggt die Meldungen in die Datei
    GLOBAL_LOGFILE und gibt die Logmeldungen auch im Konsolenfenster aus.
    """
    ...

# Globales Logging
log = Log()


class Settings(object):
    """
    Einstellungen
    
    Liest die INI-Datei(en) aus und kann die Einstellungen zusätzlich auch
    aus der Kommandozeile parsen. Standardeinstellungen sind in einem Dictionary
    vorgegeben. Es macht also nichts, wenn die gewünschte Einstellung nicht in
    einer INI-Datei und auch nicht in der Kommandozeile steht.
    """
    ...

# Globale Einstellungen
settings = Settings()
Natürlich gibt es im Hauptmodul noch eine *main()*-Funktion und für viele Fälle noch eigene Klassen, die z.B. für den Datenbankzugriff, Berechnungen usw. zuständig sind.

Dieses Modul kann direkt aufgerufen werden. Und meist kann man über Kommandozeilenparameter (Optione, Argumente) dem Programm Aufgaben übergeben, die es ohne GUI erledigen kann. Z.B. könnte man als Kommandozeilenoption das Gewicht, die Größe und das Alter übergeben und diese Daten, mit dem aktuellen Datum, versehen in eine Datenbank schreiben lassen.

Statt die oben genannten Daten in die Konsole eingeben zu lassen, könnte man jetzt ein zusätzliches GUI-Modul zur Eingabe in ein GUI-Fenster mit ein paar Textfeldern schreiben. Dieses GUI-Modul **importiert** nun das Hauptmodul und stellt zusätzlich ein Fenster für die Eingabe zur Verfügung.
Vorteil! Die Einstellungen werden automatisch, beim Importieren des Hauptmodules, eingelesen und sind über die Referenz zum Hauptmodul verfügbar. Das betrifft die Kommandozeilenoptionen und die INI-Dateien.

Der Nachteil bei diesem Vorgehen ist der, dass man Einstellungen, die nur die GUI betreffen, ins Hauptmodul einbauen muss. Das ist bei mir aber so selten der Fall, dass es mich auch nicht sonderlich stört, wenn in der INI-Datei auch die Einstellungen für die GUI stehen. Die Fenstergröße und Fensterpositon speichere ich sowiso in eigene INI-Dateien. Das macht jede Klasse automatisch, die von *AutoSizeSave* und *AutoPositionSave* erben. Siehe: http://www.python-forum.de/topic-9701.html

So kann ich mehrere kleine GUI-Programme machen, die jeweils eine Aufgabe für sich erfüllen. Ich kann damit aber auch eine große GUI schreiben. Ein GUI, mit dem man alle Programmfunktionen bedienen kann. -- Einfach wieder vom neuen GUI-Programmmodul aus das Hauptmodul importieren und los legen.

Die kleinen GUI-Progrämmchen könnte man so schreiben, dass diese auch von einem großen GUI-Programmmodul importiert werden können. So könnte man z.B. vom Haupt-GUI-Programm aus das Modul zum Eingeben der Größe, dem Gewicht, usw. importieren und den Dialog direkt aufrufen.

Also so mache ich es.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Ok ein Tipp aus der Praxis, halte deine Widgets dumm. Am besten ist es wenn sie gar nichts über die Business-Logik wissen. So kannst du sie besser wiederverwenden und noch wichtiger du hast die Logik nicht überall verteilt.
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Ich hab in meinem pyGet versucht, eine möglichst saubere Trennung von GUI und Code versucht.

Ich habs so gemacht, dass ich eine Klasse habe, die mein Programm repräsentiert. Diese Klasse beinhaltet jegliche Funktionen die meine Software hat, zusätzlich besitzt sie Funktionen um den aktuellen Status abzufragen.

So kann die GUI dann selbst entscheiden ob und wie oft sie die Anzeige aktualisiert. Somit spielt es auch keine Rolle ob ich als GUI nur die Konsole möchte oder doch lieber wx oder Tk.


Gruss
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich habe eine Trennung von GUI auch in What's On Air drin - es gibt ein Modul das die Programmlogik enthält und ein Kommandozeileninterface bietet und auch ein Modul welches dieses Hauptmodul verwendet (man könnte fast sagen, die ``libwhatsonair``) und dazu eine GUI darstellt (allerdings nicht mit wxPython sondern mit PyGTK, ist aber letztendlich ziemlich gleich).

Für PyGTK gibt es übrigens noch ein Projekt namens PyGTK MVC, du kannst es dir mal ansehen - selbst nutze ich es allerdings nicht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
alan
User
Beiträge: 81
Registriert: Dienstag 10. April 2007, 11:30

Vielen Dank für die ganzen Antworten; ich werde versuchen, zumindest einiges davon umzusetzen. MVC ist mir aber defintiv etwas zu kompliziert, ich glaube, das ist eher für größere Sachen sinnvoll. ;-)
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

alan hat geschrieben:Vielen Dank für die ganzen Antworten; ich werde versuchen, zumindest einiges davon umzusetzen. MVC ist mir aber defintiv etwas zu kompliziert, ich glaube, das ist eher für größere Sachen sinnvoll. ;-)
MVC muss nicht kompliziert sein. Eigentlich ist MVC eh nur ein tolles Akronym für eine ziemlich natürliche Sache ;)

Ansonsten kann ich nur sagen nimm dir den Tipp von Leonidas mit dem Commandlineinterface zu Herzen. Der hilft viel. ;)
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
lunar

veers hat geschrieben:
alan hat geschrieben:Vielen Dank für die ganzen Antworten; ich werde versuchen, zumindest einiges davon umzusetzen. MVC ist mir aber defintiv etwas zu kompliziert, ich glaube, das ist eher für größere Sachen sinnvoll. ;-)
MVC muss nicht kompliziert sein. Eigentlich ist MVC eh nur ein tolles Akronym für eine ziemlich natürliche Sache ;)
Eigentlich schon: Im Prinzip ist das auch nur "einer machts, der andere zeigts" ;)

Generell als Tipp würde ich vor allem sagen: Man sollte nie bei der GUI anfangen. Die Unsitte, die GUI erst zu designen, und dann dieses Doppelklick-Feature zu nutzen, welches gleich eine passende Handler-Methode erstellt, hat schon so manches Programm ruiniert.

Hat man zuerst die Logik, ist es eigentlich relativ einfach, beide zu verknüpfen:

Ich schreibe normalerweise ein Modul mit der Logik. Dann erstelle ich die GUI. Die liegt bei mir in XML Dateien, allerdings macht GUI in Python-Modulen keinen Unterschied. Im Hauptmodul leite ich dann von der (den) GUI Klasse(n) ab, importiere das Logik-Modul, und versehe die GUI mit Funktionalität.
alan
User
Beiträge: 81
Registriert: Dienstag 10. April 2007, 11:30

Ich habe mich für Leonidas' Vorschlag entschieden: Modul mit Programmlogik und ein Modul, das die Logik verwendet und GUI darstellt.

Da bin ich jetzt allerdings auf ein Problem gestoßen:

Zwei Klassen, jeweils abgeleitet von wx.Panel, die beide ein recht komplex sind. Rein "GUI-technisch" ergibt die Trennung in zwei Klassen Sinn und ich will es auch dabei belassen. Allerdings muss jetzt eine Funtion, die ein Event, ausgelöst in Panel #1, behandelt, auch auf Elemente in Panel #2 zugreifen.

Meine erste Idee dazu war, die Elemente, auf die von der "fremden" Klasse zugegriffen werden soll, zu Klassenattributen zu machen. Bin mir aber nicht so ganz sicher, ob das geht, gesehen habe ich so etwas noch nie:

Code: Alles auswählen

class MyPanel(wx.Panel):
    text = wx.TextCtrl(self, -1)
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
Antworten