Gui-Anderung einer Eigenschaft in einem anderen Modul

Fragen zu Tkinter.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@pillmuncher Ich glaube, da sollte ich langsam so etwas wie eine Dokumentation schreiben. Aber heute wird es nichts mehr.

Ich kann ja mal beginnen und zwar mit dem Zweck. Es geht um komplexe Anwendungen. Ein Teil der Anwendung soll eine Aktion im anderen Teil der Anwendung auslösen.
Wo dieser andere Teil ist, welche Widgets das sind und ob es überhaupt Widgets sind, ist diesem Teil aber nicht bekannt, und ob dieser bereits importiert oder sonstwie da ist, ist auch nicht bekannt.

Daher legt man so etwas fest, wie ein virtuelles Event, das eine Message ID hat und Daten die dabei mitgesendet werden. Sagen wir einmal, in der GUI, wo auch immer, soll ein Name und zwar mit Vorname und Nachname getrennt angezeigt werden.

Dann legt man eben fest, dass es eine Message gibt, die das tut, etwa mit:

Code: Alles auswählen

proxy.send('NAME',(vorname,nachname))
Dann legt man fest, dass es dafür dort, wo die Anzeige gemacht wird, eine Funktion existiert, die das Gewünsche macht. Das ist so etwas wie ein Event Callback, nur statt GUI Event mit klicken geschieht das mit proxy.send.

Also definiert man einen Callback, vielleicht auf zwei Label und das geschieht mit proxy.do_receive, etwa:

Code: Alles auswählen

def show_name(message,vname=label_vname,nname=label_nname):
    vname['text'] = message[0]
    nname['text'] = message[1]

proxy.do_receive(parent,'NAME',show_name)
Wenn man jetzt irgendwo diesen send aufruft, dann wird dieser Callback aufgerufen und der Name wird angezeigt. Die parent Angabe ist nich wichtig für dieses Senden. Aber wenn wir etwa vorhaben den Frame zu löschen, in dem diese Anzeige stattfindet, dann sollten wir auch die dazugehörigen callbacks löschen, damit es nicht crasht. Das erreicht man am Besten, indem man eine __destroy__() methode für die Frames definiert:

Code: Alles auswählen

def __destroy__self(): proxy.undo_receiveAll(self)
Um das zu tun muss man den proxy zuerst einmal erzeugen:

Code: Alles auswählen

import proxy as myproxy
proxy = myproxy.Proxy()
Wenn man auch mit anderen Threads kommunizieren wollte, müsste man dabei auch noch den Parameter für den externen Proxy angeben.

Dann erklären wir einmal den Code. Um auch mit andern Tasks zu kommunizieren zu können, habe ich threadsichere Queues eingerichtet. Die eine ist für das normale Senden, die andere wird vorrangig behandelt für das Registrieren von Callbacks und das Deregistrieren.

Widmen wir uns einmal dem Senden:

Code: Alles auswählen

    def send(self,msgid,msgdata=None):
        self.Queue.put((msgid,msgdata))
        self.trigger()
Die Message ID und die Daten werden in eine Queue gelegt und ein Trigger gesetzt, damit diese Message abgeholt und weiterverarbeitet wird. Was ist jetzt dieser Trigger, wenn man nichts aners einstellt, ist das der Aufruf einer Funktion:

Code: Alles auswählen

        self.trigger = self.do_work
Und das ist die Funktion:

Code: Alles auswählen

    def do_work(self,*args):
        if self.running: return
        self.running = True
        while self.work(): pass
        self.running = False
Am Anfang ist running auf False, das heißt dann, dass die Messages auf der Queue behandelt werden, bis die Queue leer ist. Wenn eine Message behandelt wird, die weitere Messages sendet und wieder do_work aufgerufen wird, dann wird dieser Aufruf gleich wieder zurückgeschickt, denn der erste Aufruf behandelt ja alles, was in der Queue ist. Und die anderen sukzessiven send Befehle sollen nicht während des ersten behandelt werden, sondern danach. Mit den Messages machen wir das:

Code: Alles auswählen

    def work(self,*args):
        if not self.Queue_HighPrio.empty(): data = self.Queue_HighPrio.get()
        elif not self.Queue.empty(): data = self.Queue.get()
        else: return False
Wir holen einen Queue Eintrag ab, es sei denn es ist keiner mehr da. Und wenn wir einen haben, dann verarbeiten wir ihn:

Code: Alles auswählen

        msgid = data[0]
        msgdata = data[1]
        if msgid in self.Dictionary:
            receivers = self.Dictionary[msgid].items()
            for receive,packed in receivers:
                if packed: receive((msgid,msgdata))
                else: receive(msgdata)
        return True
Dazu schauen wir, ob im Dictionary die Message ID registriert ist. Das ist der key. Wenn ja holen wir den value - das ist ein weiteres Dictionary von Callbackfunktionen für diese Message ID - und arbeiten alle Funktionen ab. Als Value haben wir ein flag, das packed bedeutet. Dieses packed brauchen wir zum Senden und Empfang in Verbindung mit anderen Threads. Der Normafall ist aber meistens:

Code: Alles auswählen

receive(msgdata)
Wenn wir etwa für 'NAME' den Callback registriert haben, wird dieser dadurch aufgerufen.

Das wäre es mal im Groben. Und dann dürfte auch klar sein, dass do_receive Callbacks in dieses Dictionary einträgt und undo_receive sie wieder austrägt.

Hab gemerkt, dass da noch etwas fehlt für das Senden zu anderen Tasks. Muss die Registrierfunktionen doppelt machen für intern und extern. Beziehungsweise auch wieder nicht, da ich ja nicht zu anderen tkinter GUI Threads sende. Jedenfalls mal noch überdenken, ob man noch etas braucht, etwa wenn es nicht über den externen Proxy geht, sondern direkt mit einem anderen Thread. Naja, schreiben wir es einfach noch hinein, falls es doch mal jemand braucht.
Zuletzt geändert von Alfons Mittelmeyer am Freitag 4. September 2015, 18:46, insgesamt 4-mal geändert.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Alfons
Alfons Mittelmeyer hat geschrieben:Dann widme ich mich wieder meinem GUI Designer. Andere Tasks brauch ich da nicht. Und sorry, dass ich von Vorstellungen ausging, dass so etwas wie tkinter professionelle Software wäre und ich da wohl mit einigen zusammenstieß. Konnte mir eben nicht vorstellen, dass es da so eine Menge an Bugs gibt und dass man deswegen so unnötig erscheinendes Polling machen muss.
Ich glaube der Gedanke einen GUI Designer für Tkinter zu gebären musst du fallen lassen. Eventuell wäre es auch besser in ein anderes Subforum zu wechsel da dieses Forum hauptsächlich mit Tkinter zu tun hat.

Gruss wuf :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@wuf Der Guidesigner hat ja diese Probleme nicht, da er nur im GUI Thread läuft. Aber wenn ich noch mit dem Proxy weitermache, dann kostet das noch ein wenig Zeit. Und der ist ja nicht tkinter spezifisch. Man kann ihn aber für tkinter einstellen. Und nicht für tkinter ist keinerlei Problem.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@pillmuncher Ich glaube, da sollte ich langsam so etwas wie eine Dokumentation schreiben. Aber heute wird es nichts mehr.

Ich kann ja mal beginnen und zwar mit dem Zweck. Es geht um komplexe Anwendungen. Ein Teil der Anwendung soll eine Aktion im anderen Teil der Anwendung auslösen.
Wo dieser andere Teil ist, welche Widgets das sind und ob es überhaupt Widgets sind, ist diesem Teil aber nicht bekannt, und ob dieser bereits importiert oder sonstwie da ist, ist auch nicht bekannt.

Daher legt man so etwas fest, wie ein virtuelles Event, das eine Message ID hat und Daten die dabei mitgesendet werden. Sagen wir einmal, in der GUI, wo auch immer, soll ein Name und zwar mit Vorname und Nachname getrennt angezeigt werden.

Dann legt man eben fest, dass es eine Message gibt, die das tut, etwa mit:

Code: Alles auswählen

proxy.send('NAME',(vorname,nachname))
Dann legt man fest, dass es dafür dort, wo die Anzeige gemacht wird, eine Funktion existiert, die das Gewünsche macht. Das ist so etwas wie ein Event Callback, nur statt GUI Event mit klicken geschieht das mit proxy.send.

Also definiert man einen Callback, vielleicht auf zwei Label und das geschieht mit proxy.do_receive, etwa:

Code: Alles auswählen

def show_name(message,vname=label_vname,nname=label_nname):
    vname['text'] = message[0]
    nname['text'] = message[1]

proxy.do_receive(parent,'NAME',show_name)
Wenn man jetzt irgendwo diesen send aufruft, dann wird dieser Callback aufgerufen und der Name wird angezeigt. Die parent Angabe ist nich wichtig für dieses Senden. Aber wenn wir etwa vorhaben den Frame zu löschen, in dem diese Anzeige stattfindet, dann sollten wir auch die dazugehörigen callbacks löschen, damit es nicht crasht. Das erreicht man am Besten, indem man eine __destroy__() methode für die Frames definiert:

Code: Alles auswählen

def __destroy__self(): proxy.undo_receiveAll(self)
Um das zu tun muss man den proxy zuerst einmal erzeugen:

Code: Alles auswählen

import proxy as myproxy
proxy = myproxy.Proxy()
Wenn man auch mit anderen Threads kommunizieren wollte, müsste man dabei auch noch den Parameter für den externen Proxy angeben.

Dann erklären wir einmal den Code. Um auch mit andern Tasks zu kommunizieren zu können, habe ich threadsichere Queues eingerichtet. Die eine ist für das normale Senden, die andere wird vorrangig behandelt für das Registrieren von Callbacks und das Deregistrieren.

Widmen wir uns einmal dem Senden:

Code: Alles auswählen

    def send(self,msgid,msgdata=None):
        self.Queue.put((msgid,msgdata))
        self.trigger()
Die Message ID und die Daten werden in eine Queue gelegt und ein Trigger gesetzt, damit diese Message abgeholt und weiterverarbeitet wird. Was ist jetzt dieser Trigger, wenn man nichts aners einstellt, ist das der Aufruf einer Funktion:

Code: Alles auswählen

        self.trigger = self.do_work
Und das ist die Funktion:

Code: Alles auswählen

    def do_work(self,*args):
        if self.running: return
        self.running = True
        while self.work(): pass
        self.running = False
Am Anfang ist running auf False, das heißt dann, dass die Messages auf der Queue behandelt werden, bis die Queue leer ist. Wenn eine Message behandelt wird, die weitere Messages sendet und wieder do_work aufgerufen wird, dann wird dieser Aufruf gleich wieder zurückgeschickt, denn der erste Aufruf behandelt ja alles, was in der Queue ist. Und die anderen sukzessiven send Befehle sollen nicht während des ersten behandelt werden, sondern danach. Mit den Messages machen wir das:

Code: Alles auswählen

    def work(self,*args):
        if not self.Queue_HighPrio.empty(): data = self.Queue_HighPrio.get()
        elif not self.Queue.empty(): data = self.Queue.get()
        else: return False
Wir holen einen Queue Eintrag ab, es sei denn es ist keiner mehr da. Und wenn wir einen haben, dann verarbeiten wir ihn:

Code: Alles auswählen

        msgid = data[0]
        msgdata = data[1]
        if msgid in self.Dictionary:
            receivers = self.Dictionary[msgid].items()
            for receive,packed in receivers:
                if packed: receive((msgid,msgdata))
                else: receive(msgdata)
        return True
Dazu schauen wir, ob im Dictionary die Message ID registriert ist. Das ist der key. Wenn ja holen wir den value - das ist ein weiteres Dictionary von Callbackfunktionen für diese Message ID - und arbeiten alle Funktionen ab. Als Value haben wir ein flag, das packed bedeutet. Dieses packed brauchen wir zum Senden und Empfang in Verbindung mit anderen Threads. Der Normafall ist aber meistens:

Code: Alles auswählen

receive(msgdata)
Wenn wir etwa für 'NAME' den Callback registriert haben, wird dieser dadurch aufgerufen.

Das wäre es mal im Groben. Und dann dürfte auch klar sein, dass do_receive Callbacks in dieses Dictionary einträgt und undo_receive sie wieder austrägt.

Hab gemerkt, dass da noch etwas fehlt für das Senden zu anderen Tasks. Muss die Registrierfunktionen doppelt machen für intern und extern. Beziehungsweise auch wieder nicht, da ich ja nicht zu anderen tkinter GUI Threads sende. Jedenfalls mal noch überdenken, ob man noch etas braucht, etwa wenn es nicht über den externen Proxy geht, sondern direkt mit einem anderen Thread. Naja, schreiben wir es einfach noch hinein, falls es doch mal jemand braucht, bzw. damit es schön symmetrisch ist und man bei den anderen event Threads auch den internen und externen Trigger richtig aufruft, obwohl es da egal ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Habe das mit destroy falsch geschrieben. Richtig muss es etwa so heißen:

Code: Alles auswählen

def destroy(self):
    proxy.undo_receiveAll(self)
    self.tkClass.destroy(self)
Das ist dann eine abgeleitete Klasse und man hat sich gemerkt, dass tkClass tk.Frame ist.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Alfons Mittelmeyer: Hier habe ich rekonstruiert, was ich von deinem Code glaube verstanden zu haben. Es tut nur fast dasselbe wie dein Code und auch die Namen sind andere. Gratulation! Du wurdest gerade gePEP8et. ;)

Was du Proxy nennst würde ich eher einen Event Bus nennen. Folglich heißt meine Klasse auch EventBus.

Die Verwaltung der Events in den Queues habe ich herausgezogen in eine Klasse EventStream. Statt einer öffentlichen receive() Methode stellt sie einen Iterator bereit, der die Events aus der Queue holt und sie nach außen weitergibt. Damit braucht man sich in EventBus nicht mehr mit dieser Komplexität herumschlagen.

Das Aufrufen des Event Handlings geschieht über einen Decorator, was die Kompositionaliät und damit Übersichtlichkeit verbessert.

Ich habe allen LBYL Code durch EAFP Code ersetzt. Also statt:

Code: Alles auswählen

if <some important precondition holds>:
    do_foo()
else:
    print('dude wtf??')
das hier:

Code: Alles auswählen

try:
    do_foo()
except <some important precondition violated>:
    print('dude wtf??')
Das ist ein übliches Python-Idiom. Gerade in so einem Fall:

Code: Alles auswählen

if not self.queue.empty():
    x = self.queue.get()
kann LBYL auch in die Hose gehen, wenn nämlich zwischen der ersten und der zweiten Zeile in einem anderen Thread die Queue bereits geleert wurde.

Bei mir werden Events anhand ihres Typs ihren Subscribern zugeordnet. Dank collections.namedtuple() ist das Erzeugen von Eventklassen je nur ein Einzeiler. Aber man kann es leicht so umbauen, dass man statt dessen explizit eine EventID angibt.

Die do_work() Methode habe ich weggelassen, weil man das, was dort passiert IMO einfach in einen Subscriber packen kann. Oder in einen Decorator für Subscriber.

Man kann einen Subscriber für einen Event-Typ mehrfach registrieren. Wenn ein Event dieses Typs vorkommt, wird der Subscriber aber nur einmal darauf aufgerufen, nicht so oft, wir er registriert wurde. Die Registrierung wird erst dann gelöscht, wenn dazu genau so oft unsubscribe() wie vorher subscribe() aufgerufen wurde.

Das Event Handling sollte je EventBus immer im selben Thread stattfinden. Die Subskriptionsverwaltung und das Veröffentlichen von Events kann aus anderen Threads heraus erfolgen. Es gibt dazu eine Klasse EventBusProxy, die man bequemlichkeitshalber mittels der Methode EventBus.spawn(target, *args, **kwargs) automatisch erzeugen kann. Dabei ist target ein Callable, das dann in einem neuen Thread gestartet wird und dem dabei ein EventBusProxy übergeben wird, der alle Events an den EventBus weiterreicht, der ihn erzeugt hat. Das Event Handling findet also dort statt, nicht in dem Thread, in dem target läuft. Wenn man dort einen eigenen EventBus braucht, kann man sich ja einfach einen erzeugen. Man kann auch sehr einfach Subscriber bauen, die Events von einem zu einem anderen EventBus durchreichen.

Dass das Event Handling immer aufgerufen wird, wenn neue Events in der Queue landen, ist allerdings nicht garantiert, denn wenn ein Event von einem EventBusProxy aus einem anderen Thread heraus geschickt wurde, triggert das kein Event Handling. Deswegen würde ich, statt alle Events, die gerade in der Queue stecken auf einmal zu behandeln, eine EventBus.step() Methode schreiben, die den jeweils nächsten Event behandelt und die ich dann in einem externen Event Loop regelmäßig aufrufen würde (in tkinter etwa mittels after() oder after_idle()). Oder ich würde mir asyncio, RXPython, Twisted oder GEvent ansehen. Für alle diese gibt es AFAIR Anbindungen an den tkinter Event Loop.
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

pillmuncher hat geschrieben:@Alfons Mittelmeyer: Hier habe ich rekonstruiert, was ich von deinem Code glaube verstanden zu haben. Es tut nur fast dasselbe wie dein Code und auch die Namen sind andere.
...
Bei mir werden Events anhand ihres Typs ihren Subscribern zugeordnet. Dank collections.namedtuple() ist das Erzeugen von Eventklassen je nur ein Einzeiler. Aber man kann es leicht so umbauen, dass man statt dessen explizit eine EventID angibt.
@pillmucher: Sorry, ich verstehe nicht, wozu Du eine eingeschränkte Fassung meines Proxies implementiert hast, denn was Du implementiert hast, ist das:

Code: Alles auswählen

def subscribe(self, event, subscriber): proxy.do_receive(None, event.type, subscriber)
def unsubscribe(self, event, subscriber): proxy.undo_receive(None, event.type, subscriber)
def publish(self,event): proxy.send(event.type,event)
Du hast da nichts anderes getan, als event.type als Message ID zu verwenden. Kannst Du natürlich tun. Aber wozu diese Einschränkung hart kodieren und keine andere Art von Message ID zuzulassen?

Ich sollte dann bei mir noch implementieren, dass None nicht als Owner berücksichtigt wird und nicht ins Owners Dictionary aufgenommen wird. Es würde zwar nichts ausmachen, ergibt jedoch auch keinen Sinn. Am Besten wäre der Owner hinten als optionaler Parameter, aber dann müsste ich beim GUI Designer viel Code ändern.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben:@pillmucher: Sorry, ich verstehe nicht, wozu Du eine eingeschränkte Fassung meines Proxies implementiert hast [...] Du hast da nichts anderes getan, als event.type als Message ID zu verwenden. Kannst Du natürlich tun. Aber wozu diese Einschränkung hart zu implementieren und keine andere Art von Message ID zuzulassen?
Hab ich ja geschrieben, dass das leicht geändert werden kann. Ich mag einfach gerne Datentypen und ich mag Type Dispatching. Es liefert IMO klare Schnittstellen und ein klares Programmiermodell. YMMV.

Auf was ich uA. hinauswollte, war, dass es ein Event Bus ist. Unter Proxy versteht man normalerweise einen Stellvertreter für ein anderes Object, idR. mit gleichem oder eingeschränktem Interface (wie zB. mein EventBusProxy), aber dein Proxy steht ja für sich selbst und ist ein PubSub-Mechanismus, um Ereignisse durch das System zu schleusen. Ein Event Bus eben.

Nachprogrammiert habe ich es, weil ich manchmal ein Programm erst verstehe, wenn ich es selbst implementiert habe, und ~150 Zeilen Code sind ja schnell zusammengezimmert.
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

pillmuncher hat geschrieben:Hab ich ja geschrieben, dass das leicht geändert werden kann. Ich mag einfach gerne Datentypen und ich mag Type Dispatching. Es liefert IMO klare Schnittstellen und ein klares Programmiermodell. YMMV.
Ja, kannn auch leicht geändert werden. Nur Type Dispatching schränkt natürlich die Funktionalität ein. Ich bevorzuge Scripts, die eine GUI und Callbacks aufbauen und alles Sonstige nur während dem Aufbau lokal da ist und dann weg ist. Damit die Typen dann nicht weg sind, bräuchte man dann extra ein Modul zum Importieren, in dem diese Events drin sind, oder ein Dictionary, in dem man etwa über Namen die Events findet. Es nützt aber nichts, wenn dann nur ein Eventbus dieses Dictionary kennt und andere nicht. Da könnte man ein Modul für so ein Dictionary machen. Aber da finde ich praktischer, gleich mit Namen zu arbeiten.
pillmuncher hat geschrieben:Auf was ich hinauswollte, war, dass es ein Event Bus ist. Unter Proxy versteht man normalerweise einen Stellvertreter für ein anderes Object, idR. mit gleichem oder eingeschränktem Interface (wie zB. mein EventBusProxy), aber dein Proxy steht ja für sich selbst und ist ein PubSub-Mechanismus, um Ereignisse durch das System zu schleusen. Ein Event Bus eben.
Ja da hast Du recht. Ich hatte die Idee an Nachrichten und einen Proxy für Messages im Hinterkopf. Da registriert man auch seine Callbacks. Dieser echte Proxy behandelte externe Nachrichten, die dann über ein Netzwerk übertragen wurden. Aber diese internen Nachrichten die ich jetzt habe, sind keine Nachrichten, sondern Events. Allerdings sollte man die Nachrichtenübertragung nicht aus den Augen lassen.

Ein Thread, denn man mit anbindet, könnte bestimmte Messages ja serialisieren und dann an die Welt außerhalb der Applikation übertragen. Dann hätte man mit einem Zusatzthread die Ergänzung zum Proxy und die Umwandlung interner Messages in übertragene Nachrichten und umgekehrt. Wären interne Events, die als Nachrichten an die Außenwelt gehen nun Events oder Nachrichten? Das ist mir dann ein wenig unklar. Message ID und Parameter würden dabei als Nachrichten serialisiert.

Und ist auch klar, dass ein anderes System nicht viel mit einem Event-Typ aus einem Pythonprogramm anfangen kann. Normalerweise gibt es für eine solche Kommunikation einen Message Katalog mit Message IDs und Parametern. Vom Inhalt her haben wir eine Message ID und Parameter, damit sind es Nachrichten. Realisiert intern sind diese Nachrichten als Events. Kann man daher wohl so oder so sehen. Zu Events werden sie aber erst, wenn man Callbacks darauf registriert.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben: [...] diese internen Nachrichten die ich jetzt habe, sind keine Nachrichten, sondern Events. Allerdings sollte man die Nachrichtenübertragung nicht aus den Augen lassen. [...] Wären interne Events, die als Nachrichten an die Außenwelt gehen nun Events oder Nachrichten? Das ist mir dann ein wenig unklar. [...]
Ob Events Nachrichten sind, kann man so oder so sehen. Microsoft sieht sie als Nachrichten an, neben Command-, Document- und Request-Reply-Messages. Ich selber bau gerade an einem System mit Events und Commands, und beide sind Messages.

Kleiner Philosophischer Exkurs:

Ob man so eine Unterscheidung trifft, sollte man IMO ausschließlich anhand des praktischen Nutzens festlegen, also daran, wie sich diese Unterscheidung hinterher auf das praktische Handeln auswirkt. Der Philosophische Pragmatismus, die erste originär amerikanische Ausprägung der Philosophie, hat ein Analogon dazu sogar zum obersten Grundsatz erhoben:
William James, Pragmatism, 1907 hat geschrieben:Pragmatism [...] asks its usual question. "Grant an idea or belief to be true," it says, "what concrete difference will its being true make in anyone's actual life? How will the truth be realized? What experiences will be different from those which would obtain if the belief were false? What, in short, is the truth's cash-value in experiential terms?"

The moment pragmatism asks this question, it sees the answer: TRUE IDEAS ARE THOSE THAT WE CAN ASSIMILATE, VALIDATE, CORROBORATE AND VERIFY. FALSE IDEAS ARE THOSE THAT WE CANNOT. That is the practical difference it makes to us to have true ideas; that, therefore, is the meaning of truth, for it is all that truth is known-as.
Ich formuliere das für mich so, dass jede Unterscheidung, die ich treffe, eine praktische Konsequenz haben muss in Bezug darauf, was ich tun soll. Damit wird dieser Grundsatz auf das erweitert, was man gemeinhin Das Richtige Handeln nennt. Beim Programmieren, wo es um Konstruktionen und nicht um Wahrheit geht, muss sich so eine Unterscheidung daran messen, ob sich der Code durch sie besser implementieren/dokumentieren/verstehen/erklären lässt, nicht daran, ob es sich in einem metaphysischen Sinn tatsächlich um unterschiedliche Dinge handelt. Als Philosophischer Pragmatist muss ich sogar jeden Begriff des Metaphysischen ablehnen, der nicht dem og. Grundsatz genügt.
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@pillmuncher: Wenn Du ein Pilosoph bist, dann haben wir etwas gemeinsam. Und unsere Diskussionen werden nicht so sein, dass jemand versucht den anderen nierderzumachen, weil er nicht dieselben Überzeugungen teilt oder nicht dasselbe glaubt. Denn Glauben ist ein Fremdwort für Philosophen. Da gilt der Grundsatz des Sokrates: "scio, nescio"

Ein Philosoph weiß viel. Er weiß bei Vielem, ob es richtig ist oder ob es falsch ist, denn er hat es erkannt. Und ob sonst noch etwas stimmt oder nicht, das weiß er nicht und läßt es dabei. Andere aber wollen sich mit derartigem Unwissen nicht abfinden und glauben dies oder jenes, weil vielleicht jemand, dem sie glauben, es gesagt oder geschrieben hat.

Vielleicht sind Viele hier im Forum nicht glücklich mit mir, dass sie mich nicht überzeugen oder belehren können. Aber ich erkenne es einfach nicht als wahr an, solange es nicht zweifelsfrei bewiesen ist.
Vielleicht halten mich Viele jetzt für stockkonservativ, weil ich alles bezweifle, aber richtig bezweifeln heißt doppelt bezweifen, nämlich, ob die eigenen Zweifel angebracht sind, und ob es nicht sein kann, dass das was Viele glauben, dass es falsch ist, nicht doch stimmen könnte.

Auf der einen Seite bezweifle ich alles und auf der anderen Seite halte ich auch alles für möglich und bin daher auch ungeheuer aufgeschlossen und offen für Neues.
Hat das jemand verstanden?

@pillmuncher, bitte entschuldige diesen kleinen Exkurs. Aber unsere Diskussion hat mir sehr viel gebracht. Denn ich hatte nun nachgedacht, ob es ein Eventbus ist oder ein Proxy.

Die Antwort ist, es ist ein Proxy, denn Eventbus ist nur eine Default-Einstellung. Mir ist nämlich in den Sinn gekommen, dass man etwa auch Nachrichten austauschen könnte mit anderen gleichzeitig laufenden Python Anwendungen auf dem selben Computer als auch völlig woanders. Serialisieren einer Message ist in Python ganz einfach. Eine serialisierte Message sieht man mit 'print' und bekommt man mit 'str' und die Rückverwandlung geschieht ganz einfach mit 'eval'. Daher ist das Senden von Nachrichten gar kein Problem.

Eine klitzekleine Änderung meines Proxies machte Nachrichtenaustausch mit anderen Threads möglich. Es war das packed Flag. Und eine weitere kleine Änderung macht auch Kommunikation über Netzwerke und dergleichen möglich. Dazu betrachten wir einmal folgende Zeilen aus der Methode 'work':

Code: Alles auswählen

for receive,packed in receivers:
    if packed: receive((msgid,msgdata))
    else: receive(msgdata)
Hier stand 'receive' für eine Callback Funktion. Aber das ist viel zu einschränkend. Richtig muss es heißen 'receiver' statt 'receive' und was wir damit tun ist 'transmit':

Code: Alles auswählen

for receiver,packed in receivers:
    if packed: self.transmit(receiver,(msgid,msgdata))
    else: self.transmit(receiver,msgdata)
Und die default Einstellung für transmit ist ein Callback. Das erreichen wir mit folgenden Definitionen im Proxy:

Code: Alles auswählen

def callback(self,receiver,message): receiver(message)

self.transmit = self.callback
Und transmit können wir je nach Bedarf umstellen. Dann könnte receiver etwa auch eine IP Adresse oder ein File Descriptor sein.

Allerdings fehlt da noch etwas. Dieser Proxy würde nur Gleichartiges mit Gleichartigem verbinden. Wir brauchen aber noch die Umwandlung von Callbacks oder was auch immer in Transmissions und umgekehrt.
Das könnte man zwar mit einem weiteren Flag regeln, aber das erscheint mir zu wenig. Ich denke eher an an eine Umwandlung von Transmission Typ X in Transmission Typ Y.

Aber vielleicht ist das Callback Prinzip auch gar nicht verkehrt und wir brauchen evtl. nur noch einen zusätzlichen Parameter für die Registrierung?

Wenn wir bei der ursprünglichen Implementierung bleiben und das packed Flag modifizieren, könnte es vielleicht bereits die Lösung sein:

Code: Alles auswählen

for receive,packed in receivers:
    if type(packed) is bool:
        if packed: receive((msgid,msgdata))
        else: receive(msgdata)
    else: receive((packed,(msgid,msgdata)))
Das schaut nicht schlecht aus: Angenommen die Empfänger wären verschiedene Files oder Pipes oder auch IP Adressen, dann würden wir die entsprechenden Callbacks angeben und als packed File-Deskriptoren oder IP Adressen. Und umgekehrt hätten wir verschiedene Trigger und damit verbundene Funktionen, welche dann die serialisierten Messages in Message ID und Parameter konvertieren und dann mit send auf die Queue legen.

Sieht doch gut aus, oder?

Aber mit dieser Implementierung wäre es doch wohl wieder Eventbus Prinzip.
Antworten