Seite 1 von 1

Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Montag 2. Mai 2011, 08:37
von mutetella
Hallo,

ich hab' da mal wieder so ein Konstrukt im Kopf, von dem ich mir nicht sicher bin, ob es 'sauber' ist... :wink:

Ich habe ein Objekt, das mehrmals am Bildschirm angezeigt werden soll. Wird das Objekt geändert, sollen natürlich auch die verschiedenen Anzeigen darauf reagieren. Um das zu erreichen, würde ich die Methode zum Anzeigen des Objektes einfach im selbigen hinterlegen. So eine view-Liste könnte beispielsweise folgende Elemente enthalten:

Code: Alles auswählen

[(view1.show, (0, 0)), (view2.show, (5, 2))]
Sobald das Objekt sich ändert, übergibt es sich selbst und die hinterlegten Koordinaten an die jeweilige view-Methode. Im gezeigten Beispiel würde das Objekt also in view1 auf 0, 0 und in view2 auf 5, 2 angezeigt werden.

Was mich ein wenig daran stört ist, dass hier eine Anzeige-Methode in einem Datenobjekt hinterlegt wird.
Sind solche Vermischungen von Anzeige und Daten nicht eher unsauber?
Können dabei Probleme entstehen, von denen ich noch nichts weiß?
Oder ist das ok und kann so gemacht werden?

mutetella

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Montag 2. Mai 2011, 12:01
von mkiever
Hallo,

da sollte man mal das Observer Pattern nachlesen.
Wo die Verknüpfung gemacht wird, ist aber umstritten
(von einem View oder von einem Controller aus ist üblich).
Ich selbst baue solche Observer Verknüpfungen von außen,
einem übergeordneten Applikationsobjekt, auf.
Also in etwa:

Code: Alles auswählen

   app._data.addObserver(app._view1)
   app._data.addObserver(app._view2)
wobei _data das Datenobjekt und _view1/2 die Anzeigen sind
und dem Applikationsobjekt app gehören und auch dort erzeugt werden.

Ich hoffe das hilft weiter,
Matthias Kievernagel

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Montag 2. Mai 2011, 12:08
von BlackJack
@mutetella: Was in Ordnung ist, wäre ein Rückruf bei dem sich irgendwer registrieren kann, wenn das Datenobjekt irgendwem eine Änderung mitteilen will. Wie man an "irgendwer" und "irgendwem" vielleicht erkennen kann, ist das unabhängig von GUI. Natürlich kann sich da *auch* GUI-Code registrieren. Solange die Implementierung des Daten-/Logikobjekts kein Wissen darüber braucht, um wen es sich bei dem zurück gerufenen handelt, ist das sauber.

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 07:44
von mutetella
BlackJack hat geschrieben:Solange die Implementierung des Daten-/Logikobjekts kein Wissen darüber braucht, um wen es sich bei dem zurück gerufenen handelt, ist das sauber.
Ich verstehe nicht so recht, wie Du das meinst. Wenn das Datenobjekt eine Methode zum Aufnehmen eines Rückrufes besitzt, hat das ja erstmal sowieso nichts mit einem potentiell zurückgerufenen Objekt zu tun. Anderst gefragt: Wie sähe eine Implementierung aus, die Wissen über das zurückgerufene Objekt braucht?

Ich habe mir jetzt auch bereits ein paar Beispielmodelle angeschaut. Allerdings handelt es sich bei den zurückzurufenden Objekten in diesen Beispielen immer um Objekte wie Buttons, TextCtrl's etc., die Informationen ihrer Bildschirmposition in sich speichern.
Wenn ich aber meine Daten in einer Konsole ausgebe, habe ich solche weiterverwendbaren Objekte ja erstmal nicht.
Ist es in einem solchen Fall in Ordnung, wenn ich Parameter wie z. B. Koordinaten, die genau genommen nicht zum Datenobjekt gehören, zusammen mit der zurückzurufenden Methode ablege. Eben so, wie in meinem kleinen Beispiel?

mutetella

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 10:01
von BlackJack
@mutetella: Naja wenn das Datenobjekt zum Beispiel wüsste das es sich um GUI-Objekte handelt und am ende darauf auf noch eine `update()`-Methode aufrufen würde um die Anzeige zu aktualisieren, dann wäre das nicht sauber.

Das mit den Argumenten ist auch in Ordnung denn Dein Datenobjekt weiss ja nicht was die bedeuten.

Man kann das alles übrigens ein wenig abstrahieren durch ein `Event`-Objekt, wo man Rückruffunktionen registrieren kann. Ungetestet:

Code: Alles auswählen

class Event(object):
    def __init__(self):
        self.callbacks = set()
    
    def __call__(self, *args, **kwargs):
        for callback in self.callbacks:
            callback(*args, **kwargs)
    
    def add(self, listener):
        self.callbacks.add(listener)
    
    def remove(self, listener):
        self.callbacks.remove(listener)
Das kann man wiederverwenden. Und bei APIs die nur eine einfache Rückruffunktion erwarten, kann man es verwenden um mehrere Funktionen daran zu binden. Dann muss man es allerdings ausserhalb der API verwalten. Man kann die Dinger sogar schachteln, also ein `Event`-Exemplar einem anderen als "listener" hinzufügen.

Argumente wie Deine Koordinaten müsste man hier zum Beispiel mit `functools.partial()` integrieren: ``data.changed_event.add(partial(view2.show, 5, 2))``.

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 13:46
von mutetella
@BlackJack: Vielleicht eine dumme, für mein Verständnis aber wichtige Frage: Wie wird denn ein solches Event ausgelöst? Ich kenne das von wxPython, wo ich mittels 'Bind()' ein event an ein bestimmtes widget binden kann.
Hier würde ich folgendermaßen vorgehen:

1. Jedesmal, wenn mein data-Objekt angezeigt wird, registriert sich die view, so oder so ähnlich wie in Deinem Beispiel: 'data.changed_event.add(partial(view2.show, 5, 2))'
2. Wenn das data-Objekt geändert werden soll, wird die entsprechende view zur Änderung aufgerufen.
3. Diese view kann ja entweder abgebrochen oder mit einem 'OK' beendet werden. Wird über 'OK' beendet, wird 'data.changed_event()' aufgerufen.

Ist das so korrekt?

mutetella

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 15:02
von BlackJack
@mutetella: Das musst Du "auslösen" in dem Du es aufrufst -- siehe die `__call__()`-Methode. Wenn Dein Datenobjekt die Views informieren will, bzw. allgemeiner eben alle die eine Funktion registriert haben, dann ruft es ``self.changed_event()`` auf. Das hat nichts mit wxWidgets-Events zu tun.

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 19:04
von mutetella
@BlackJack: Schon klar, dass das nichts mit wx zu tun hat. Ich habe das nur ins Spiel gebracht, weil ich dort eben ein 'data.changed_event()' an den OK-Button binden würde, mit dem ich eine Änderung der Daten bestätige.
Wäre folgende Vorgehensweise in Bezug auf die Trennung von Daten und Anzeige korrekt?

Code: Alles auswählen

class View(object):
    ...
    def change_data(self, data):
        ....
        ok = window.getkey()
        if ok in 'jJ':
            data.changed_event()
Mich hat das 'self' in Deiner letzten Antwort verwirrt.

Sorry für meine Korinthenkackerei, aber diese kleinen Details sind für mich immer sehr wichtig...

mutetella

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Dienstag 3. Mai 2011, 19:15
von BlackJack
@mutetella: Events werden eigentlich von den Objekten "ausgelöst" auf denen sie definiert sind. Darum auch das `self`. "Listener" oder "observer" registrieren sich ja auf dem Objekt um von dem Objekt informiert zu werden, wenn sich etwas Interessantes getan hat. Wenn Du auf eine "Ok"-Schaltfläche in der GUI reagieren willst, dann ist das ja eine reine GUI Geschichte, die hat mit den Daten erst einmal nichts zu tun.

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Mittwoch 4. Mai 2011, 09:29
von mutetella
@BlackJack: Ok... vielen herzlichen Dank! Das war mir mal wieder eine große Hilfe!

mutetella

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Mittwoch 4. Mai 2011, 10:59
von mutetella
@BlackJack:
Ok, eine Frage habe ich noch: Weshalb benutzt Du in Deinem Event()-Beispiel statt einer Liste ein set?

Re: Anzeigemethode im angezeigten Objekt ablegen?

Verfasst: Mittwoch 4. Mai 2011, 11:30
von BlackJack
@mutetella: Warum nicht!? :-) Ist halt die Frage was für Eigenschaften man haben möchte. Also zum Beispiel Reihenfolge der Aufrufe und was passieren soll wenn man die gleiche Rückruffunktion mehrfach registriert und wieder entfernt.