GUI-Programmierung , Zugriff auf andere Klassen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
frank.ffo
User
Beiträge: 3
Registriert: Montag 17. Mai 2010, 20:24

Hallo,

ich möchte für den Pocket-PC ein Routenauswertungsprogramm schreiben.
Ich hatte schon einmal ein solches mit VB geschrieben, möchte mich aber jetzt an Python versuchen.
Meine Frage: Wie kann ich z.B. von der Klasse "RoutenPage" auf die Klasse "FahrtenbuchPage" zugreifen?

Der folgende Aufruf ist wohl falsch:
FahrtenbuchPage.ed1_append("Gewaehlte Route: %s\n" % sWahl)

Ein Beispielaufruf zum unten angegebenen Code wäre mir sehr hilfreich.

Danke für eure Hilfe.

Code: Alles auswählen

# -*- coding: cp1252 -*-
import ppygui.api as gui
import os, glob, math

sPath = "\\Storage Card\\OziExplorer\\Data"
slFiles = []
slRow = []

def dirglob(dir, pattern):
   fullPattern = os.path.join(dir,pattern)
   return glob.glob(fullPattern)

# Einlesen der Dateinamen der Routen
# und später das Berechnen von Entfernung, verbrauchte Zeit etc.
class RoutenPage(gui.Frame):
    ed1_txt = ""
    def __init__(self, parent):
        gui.Frame.__init__(self, parent)
        self.lb1 = gui.Label(self, "Route waehlen:",
                             align="center",
                             font=gui.Font(size=8,color=(0,0,255)))

        slTracks = dirglob(sPath, "*.plt")
        
        for sDatei in slTracks:
            sDatei = sDatei.split('\\')
            slFiles.append("%s" % sDatei[-1])

        self.li1 = gui.List(self, choices=slFiles)
        self.li1.bind ( itemactivated = self.BerechneRoute )

        sizer = gui.VBox(border=(2,2,2,2), spacing=2)
        sizer.add(self.lb1)
        sizer.add(self.li1)
        self.sizer = sizer

    def BerechneRoute(self, parent):
        iWahl = self.li1.selection
        sWahl = slFiles[iWahl]
        
        # Fahrtenbuch mit Ergebnis der Berechnungen (die noch kommen) neu füllen => klappt nicht so
        FahrtenbuchPage.ed1_append("Gewaehlte Route: %s\n" % sWahl) # Testausgabe

class FahrtenbuchPage(gui.Frame):
    def __init__(self, parent):
        gui.Frame.__init__(self, parent)
        self.lb1 = gui.Label(self, "Fahrtenbuch:",
                             align="center",
                             font=gui.Font(size=8,color=(0,0,255)))
        self.ed1 = gui.Edit(self,"",multiline=True)
        self.ed1.readonly = True

        sizer = gui.VBox(border=(2,2,2,2), spacing=2)
        sizer.add(self.lb1)
        sizer.add(self.ed1)
        self.sizer = sizer
        
    def ed1_append(self, parent, wert):
        self.ed1.append(wert)

class Auswahl(gui.NoteBook):
    PAGES = [ ('Routen', RoutenPage) , ('Fahrtenbuch', FahrtenbuchPage) ]

    def __init__(self, parent):
        gui.NoteBook.__init__(self, parent)
        for title, klass in self.PAGES:
            self.append(title, klass(self))
        self.selection = 0

    def select(self, parent):
        self.selection = 1

class MainFrame(gui.CeFrame):
    def __init__(self):
        gui.CeFrame.__init__(self,
            title="Routenauswertung",
            action=("About", self.on_about),
            menu="Menue")

        for i, (page, klass) in enumerate(Auswahl.PAGES):
            self.cb_menu.append(page, callback=self._menu_cb(i))
        self.nb = Auswahl(self)

        sizer = gui.VSizer()
        sizer.add(self.nb)
        self.sizer = sizer

    def _menu_cb(self, i):
        def cb(event):
            self.nb.selection = i
        return cb

    def on_about(self, ev):
        gui.Message.ok("About", "Routenauswertung", "info", self)

if __name__ == '__main__' :
    app = gui.Application(MainFrame())
    app.run()
Zuletzt geändert von frank.ffo am Dienstag 18. Mai 2010, 07:05, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

frank.ffo hat geschrieben: Meine Frage: Wie kann ich z.B. von der Klasse "RoutenPage" auf die Klasse "FahrtenbuchPage" zugreifen? Habe irgendwie einen Knoten im Kopf.
Du meinst sicherlich Objekte vom Typ XYZ? Aber auch dann kapiere ich Deine Frage noch nicht wirklich. Kannst Du Dein Problem nicht ggf. an einem minimalen (und von der GUI unabhängigen) Beispiel erläutern?

Generell ein paar Anmerkungen:
  • Langen Code am besten im Paste-Bin posten
  • PEP8 besser beachten (berechne_route statt BerechneRoute)
  • Du mischt Deutsch und Englisch bei den Bezeichnern... entscheide Dich am besten für eine Sprache
  • Du mischt GUI und Logik. Du solltest Deine Datenmodelle und Funktionen darüber unabhängig von der GUI implementieren.
  • kaum / keine guten Kommentare. Python bietet ja gerade die Möglichkeit Module, Klasse und Funktionen zu annotieren. So ist es schwer rauszufinden, was Dein Modul überhaupt macht und was es können soll.
Last but not least: Willkommen im Forum :D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
frank.ffo
User
Beiträge: 3
Registriert: Montag 17. Mai 2010, 20:24

Danke für die Antwort,

es handelt sich jetzt noch um ein Gerüst. Ich habe ein gui.Notebook und zu jeder Page existiert eine eigene Klasse.
Die Klasse RoutenPage listet alle Dateien mit der Extension *.PLT auf, was schon funktioniert.
Ein Klick auf einen Dateinamen ruft dann die Funktion BerechneRoute innerhalb der Klasse RoutenPage auf (funktioniert auch).
Dort wird später gerechnet und das Ergebnis soll auf der Seite FahrtenbuchPage ausgegeben werden.
Und das ist halt eine andere Klasse. Meine Frage war nun, wie ich von der Klasse RoutenPage Werte an den gui.Edit von der Klasse FahrtenbuchPage übergeben kann. Sozusagen als Ergebnis der Berechnungen.


Sorry, für den etwas langen Quellcode, aber ich dachte, 150 Zeilen sind noch o.K.


Frank
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

frank.ffo hat geschrieben: Ein Klick auf einen Dateinamen ruft dann die Funktion BerechneRoute innerhalb der Klasse RoutenPage auf (funktioniert auch).
Dort wird später gerechnet ...
Naja, dann musst Du doch nur an der Stelle, wo Du die Berechnung aufrufst einen Rückgabewert erhalten bzw. merken und an ein Objekt der FahrtenbuchPage übergeben!
Sorry, für den etwas langen Quellcode, aber ich dachte, 150 Zeilen sind noch o.K.
Naja, das neue Board scheint es besser zu verkraften ;-) Aber aus Übersichtlichkeitsgründen würde ich so max. 50 Zeilen hier direkt posten. Ist aber sicherlich Geschmackssache - mal gucken, ob in der FAQ dazu etwas steht :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Hyperion hat geschrieben:
  • PEP8 besser beachten (berechne_route statt BerechneRoute)
Hm. Nein. `calculate_route`. :twisted:
problembär

Meine neueste Entdeckung ist, daß man um seine Klassen eine Art World-Objekt herumbauen sollte, das die Objekte zu einer Gesamtheit zusammenfaßt und von dem aus man dann auf jede einzelne Klasse zugreifen kann.

Eine Katze z.B. ist auch nicht im leeren Raum, sondern es gibt da immer die Welt im Hintergrund.
Von dort können manchmal sogar Einflüsse kommen, z.B. wird die Katze aktiv, wenn es in der Welt Nacht wird.
Entsprechend sollte man also auch ein World-Objekt um Cat-Objekte herum bauen.

Wie in der Schöpfungsgeschichte wird dann erst das World-Objekt initialisiert und dann darin, etwa in einer Liste ("World has Cat"-Beziehung) die Cat-Objekte instantiiert.

Dann ergibt sich der Klassen-Zugriff eigentlich.

Wieder eine wichtige Sache, die ich nicht in Büchern gefunden habe.

Gruß
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

cofi hat geschrieben:
Hyperion hat geschrieben:
  • PEP8 besser beachten (berechne_route statt BerechneRoute)
Hm. Nein. `calculate_route`. :twisted:
Hast Du den Punkt danach gelesen? 8)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@problembär: Das was Du da beschreibst klingt für mich ein bisschen nach dem "God-Object" und das findet man in Büchern über Anti-Patterns, also Sachen die man *nicht* machen sollte.

Bei einem Objekt was alle anderen kennt, hast Du ja letztlich sowas wie ein objektorientiertes ``global`` wieder eingeführt.
frank.ffo
User
Beiträge: 3
Registriert: Montag 17. Mai 2010, 20:24

Ich möchte kein "God-Object" erstellen sondern mittels von Methoden Objekte anderer Klassen verändern.
Die erste Klasse - RoutenPage - führt nach Auswahl einer Datei die Berechnungen aus und mittels eines Aufrufes einer Methode (ed1_append) einer anderen Klasse - FahrtenbuchPage - (wo ich nicht genau weiß, wie ich diese aufrufen soll) soll dort ein Objekt verändert werden (z.B. einem Editfeld etwas hinzufügen als Protokoll der Berechnungen).
Das sollte doch machbar sein. :?

Code: Alles auswählen

FahrtenbuchPage.ed1_append("Gewaehlte Route: %s\n"  % sWahl) # Testausgabe funtioniert nicht
BlackJack

@frank.ffo: Du scheinst den Unterschied zwischen Klassen und Exemplaren/Objekten von diesen Klassen nicht zu verstehen. Du willst nicht auf die andere Klasse zugreifen, sondern auf ein Exemplar das diese Klasse als Typ hat. Und dazu musst Du das Exemplar irgendwie bekannt machen. Das `RoutenPage`-Objekt muss also entweder selbst das `FahrtenbuchPage`-Objekt kennen, oder eine andere Möglichkeit haben da heran zu kommen.

Wobei das IMHO aber sowieso die falsche Ebene ist. Du vermischt GUI und Programmlogik. In `RoutenPage` sollten keine Berechnungen durchgeführt werden und das Objekt sollte auch das `FahrtenbuchPage`-Exemplar nicht kennen müssen. GUI-Klassen sollten nur Daten dem Benutzer präsentieren und seine Eingaben erfassen. Berechnungen sollten separat durchgeführt werden und auch "lose" an die GUI gekoppelt werden. Zum Beispiel mittels "callbacks" oder allgemeiner dem Observer-Entwurfsmuster. Schau Dir mal das Model-View-Controller-Entwurfsmuster an.
problembär

BlackJack hat geschrieben:@problembär: Das was Du da beschreibst klingt für mich ein bisschen nach dem "God-Object" und das findet man in Büchern über Anti-Patterns, also Sachen die man *nicht* machen sollte.

Bei einem Objekt was alle anderen kennt, hast Du ja letztlich sowas wie ein objektorientiertes ``global`` wieder eingeführt.
Oh, da hast Du wohl recht:

http://de.wikipedia.org/wiki/God_object

Schade. Aber danke für den Hinweis! Aus Fehlern kann man ja lernen ...
BlackJack hat geschrieben:Das `RoutenPage`-Objekt muss also entweder selbst das `FahrtenbuchPage`-Objekt kennen, oder eine andere Möglichkeit haben da heran zu kommen.
Tja, das ist oft genau mein Problem. Über das "God-Object" hatte das eine Objekt dann nämlich genau diese Möglichkeit, an das andere Objekt heranzukommen. Wenn das nun also auch nicht gut ist, weiß ich auch nicht mehr :( ...

Gruß
problembär

Hab nochmal über das God-Object nachgedacht:

1. Ich find' schon merkwürdig, daß es kein Objekt geben soll, das alle anderen Objekte kennt. Was ist mit dem "Controller-" und dem "Model"-Objekt? Müssen die nicht auch Zugriff auf alle Unterobjekte haben?

2. Ok, ein "God-Object" führt gewissermaßen "global" wieder ein. Aber es ist schon ein bißchen anders als mit globalen Variablen: Da wird es unübersichtlich, weil einem da schnell die Variablennamen ausgehen. Das ist bei Klassen ja nicht so, weil die für die Variablen wie Container / Pakete wirken.

3. Im Wiki-Eintrag heißt es, ein "God-Object" kann sogar die Performance verbessern:
Wikipedia hat geschrieben:Während ein God object generell als Merkmal eines schwachen Programmaufbaus gilt, ist es gängige Praxis innerhalb begrenzter Umgebungen wie dem Mikrocontroller, bei dem eine schnelle Performance wichtiger ist als die Wartung.
Ergo: Ich glaube, ich schreibe nicht so große Programme, daß ein "God-Object" zum Wartungsproblem wird. Dafür erleichtert es manchen Objekten den Zugriff auf andere, hat also auch Vorteile.
Daher werde ich für mich bis auf weiteres mein "God-Object", bzw. "World-Object" weiter verwenden, solange es nicht allzu "big and hairy" wird ;).

Gruß
BlackJack

@problembär: Vielleicht reden wir ja auch ein bisschen aneinander vorbei, aber Du hattest von einem Objekt gesprochen das auf *alle* anderen Objekte zugreifen kann. Natürlich muss ein Controller irgendwie mit *seinen* GUI- und Modell-Objekten verbunden sein, aber eben nur mit denen die er unbedingt kennen muss, und nicht mit allen.

God-Objekt in Python ist keine gute Idee. Ich hatte eher den Eindruck der Wikipedia-Artikel ist da ein wenig aus Java-Sicht geschrieben, wo man eben unbedingt Klassen einsetzen *muss*. In Python würde man da wieder bei Modulen und ``global`` landen. Und so ein Programm in eine Klasse zu verschieben nur um ``globale`` "loszuwerden", ändert an der schlechten Struktur ja nichts. Die Probleme die durch ``global`` entstehen, bleiben auch ohne das Schlüsselwort in solchem Code.

Die Leistungsvorteile bei kleinen Systemen die da genannt werden, kommen bei Python auf Desktoprechnern kaum zum tragen. Auf diesen kleinen Systemen hat man "schwache" Prozessoren und wenig Speicher, also will man dort möglichst viele indirekte Zugriffe sparen, weil die Zeit und Speicher für die Zeiger kosten. Das gegen schlechtere Wartbarkeit einzutauschen ist auf einem PC ein schlechtes Geschäft, weil man nicht wirklich etwas gewinnt. Ein paar Taktzyklen und ein paar Kilobyte sind da nicht wirklich wichtig.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zu 1) Das Problem tritt auf, wenn die Klasse ueber die Innereien der anderen Klassen Bescheid weis, d.h. man nicht die anderen Klassen austauschen kann (gegen Klassen mit passenden Interfaces) und nicht unbedingt, wenn es einen Ueberblick ueber den restlichen Aufbau hat.

Dass eine Klasse mit mehreren tausend Zeilen Code ein Wartungsalptraum ist, muss man wohl nicht erwaehnen.

Zu 2) Der Problem ist der Zustand. Nebeneffekte, schlechte Parallelisierbarkeit etc.

Zu 3) Dass die Performance ausserhalb des embedded Bereichs signifikant ist wuerde ich bezweifeln, embedded ist nunmal eine ganz andere Welt.

Die Frage ist, ob du bei deinen nicht so grossen Programmen da ueberhaupt einen Vorteil hast. Und man sollte auch an die Zukunft denken ;)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

problembär hat geschrieben:1. Ich find' schon merkwürdig, daß es kein Objekt geben soll, das alle anderen Objekte kennt. Was ist mit dem "Controller-" und dem "Model"-Objekt? Müssen die nicht auch Zugriff auf alle Unterobjekte haben?
So ein Objekt hat natürlich Zugriff auf eine Reihe von Unterobjekten aber diese Unterobjekte haben wieder Unterobjekte und auf diese muss dein Objekt keinen Zugriff haben und selbst wenn es den direkt hat sollte es davon nicht gebrauch machen.

Dadurch erreichst du eine Baumstruktur in deiner Anwendung, die dir erlaubt problemlos einzelne Blätter bishin zu Ästern auszutauschen ohne irgendeinen anderen Teil der Anwendung zu verändern.
problembär

Na gut, nochmal vielen Dank für die weiteren Hinweise. Ich brauche offenbar einige Zeit, um mich mit richtiger OOP wirklich anzufreunden :roll:.

Gruß
Antworten