Gui-Anderung einer Eigenschaft in einem anderen Modul

Fragen zu Tkinter.
Benutzeravatar
snafu
User
Beiträge: 6861
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:@snafu: `Tkinter`\s benutzerdefinierte Ereignisse haben allerdings den Nachteil das man keine Daten damit übermitteln kann.
Das hatte ich wohl verdrängt. Wäre ja zu schön, wenn TKinter in Python ein vollwertiges Event-System hätte...

@wuf: Ich meinte, dass die eigentlichen Operationen auf der Datei streng genommen bereits Geschäftslogik sind und somit IMHO nichts im Modell zu suchen haben. Mein Vorschlag war, diese zu abstrahieren (was du im Grunde ja bereits gemacht hast) und die konkrete Implentierung des Ladens und Speicherns außerhalb des Modells zu realisieren. Wenn das Modell nun etwas laden soll, würde es die Aufgabe via ``helpers.load(self.filename)`` (oder ähnliche Benennung) an die Geschäftslogik delegieren. Ist es jetzt klarer für dich?

Also ich sehe ein Modell im Sinne von MVC halt noch als Teil der GUI an. Insofern wäre es für mich konsequent, Zeilen wie ``with open(path) as f: f.do_something()`` nicht direkt im Modell stehen zu haben, sondern in die Geschäftslogik auszugliedern.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

snafu hat geschrieben:
BlackJack hat geschrieben:@snafu: `Tkinter`\s benutzerdefinierte Ereignisse haben allerdings den Nachteil das man keine Daten damit übermitteln kann.
Das hatte ich wohl verdrängt. Wäre ja zu schön, wenn TKinter in Python ein vollwertiges Event-System hätte...

@wuf: Ich meinte, dass die eigentlichen Operationen auf der Datei streng genommen bereits Geschäftslogik sind und somit IMHO nichts im Modell zu suchen haben. Mein Vorschlag war, diese zu abstrahieren (was du im Grunde ja bereits gemacht hast) und die konkrete Implentierung des Ladens und Speicherns außerhalb des Modells zu realisieren. Wenn das Modell nun etwas laden soll, würde es die Aufgabe via ``helpers.load(self.filename)`` (oder ähnliche Benennung) an die Geschäftslogik delegieren. Ist es jetzt klarer für dich?

Also ich sehe ein Modell im Sinne von MVC halt noch als Teil der GUI an. Insofern wäre es für mich konsequent, Zeilen wie ``with open(path) as f: f.do_something()`` nicht direkt im Modell stehen zu haben, sondern in die Geschäftslogik auszugliedern.
Sorry, keine Daten übermitteln? Siehe execute_gui.py in meinem Post vorher.

Mein Vorschlag ist Message. Es wird eine Message empfangen, die Daten zu lesen. Sie werden gelesen und dann mit einer anderen Message weitergesendet. An wen? Spielt keine Rolle! Irgenwer wird sie schon brauchen! Und wenn es die GUI ist.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi snafu

Danke für deine Antwort. Da ich nicht professioneller Programmierer bin interpretiere ich das MVC-Modell sicher aus einer einfacheren Sicht. Habe aber schon einiges aufbauend auf diesem Model erfolgreich programmiert. Es ermöglicht einen einfacheren Wechsel von Tkinter auf wxPython oder PyQt. Die Bezeichnung Geschäftslogik ist ein neuer Begriff für mich. Ich dachte immer dies sei wieder eine neue Bezeichnung für Model oder Logik. Ist Geschäftslogik ein viertes Standbein im MVC-Modell? Meine Anwendungen haben mehr mit Steuerungsaufgaben zu tun als mit geschäftlichem.

@Alfons Mittelmeyer Danke noch für deine Mitteilung execute_gui.py

Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
snafu
User
Beiträge: 6861
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Alfons Mittelmeyer: BlackJack meinte, dass die TKinter-Anbindung in Python das Übermitteln von Nachrichten nicht unterstützt. Das Event kann halt nur ausgelöst werden, aber man kann mit dem Event keine weiteren Parameter übergeben. Das war mir in dem Moment nicht bewusst. Und das richtete sich wohl auch weniger an dich, sondern an mich.

Im Übrigen würde ich mit Vollzitaten ja sparsam umgehen. Besonders wenn das Zitat zu einem Großteil Text enthält, der für die konkrete Antwort völlig irrelevant ist, aber egal...
Benutzeravatar
snafu
User
Beiträge: 6861
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@wuf: Mit Geschäftslogik meint man normalerweise das Backend. Das Frontend würde sich bei einem rein textuellen Programm z.B. um das Parsen der Kommandozeile und um die Ausgabe in der Shell kümmern (also: schlichtweg `print` benutzen). Es übersetzt sozusagen die vom Benutzer übermittelten Kommandos in Methodenaufrufe des Backends und liefert die Rückgaben dieser Aufrufe auf dem Bildschirm aus. Bei einer grafischen Anwendung sind es die Events, die dazu führen, dass die GUI-Logik entsprechende Aufrufe auf dem Backend tätigt. Das Backend sollte niemals eine feste Bindung zum Frontend haben. Nur die Frontends müssen umgekehrt natürlich Kenntnisse von der Schnittstelle des Backends haben, damit die Sache überhaupt funktionieren kann.

Wenn du sehr sauber programmieren willst, dann dürfte in den GUI-Modulen also nichts passieren, was nicht direkt mit GUI-Komponenten oder mit der verwendeten GUI-Bibliothek zu tun hat. Alles was außerhalb dieser "Welt" ablaufen soll, müsste vom Backend über eine entsprechende API realisiert werden. In der Praxis hält man sich sicherlich nicht immer 100%ig an dieses Konzept. Es wäre aber sozusagen die Idealvorstellung.
BlackJack

@wuf: Geschäftslogik hat nicht unbedingt etwas mit „geschäftlich“ zu tun sondern bezeichnet einfach den Teil des Programms der die eigentliche Aufgabe erledigt, also ohne GUI. Der Kram für den Domänenwissen aus der ”realen Welt” oder dem Anwendungsgebiet benötigt für das die Anwendung geschrieben ist. Das ist also sozusagen GUI vs. Geschäftslogik. Was man ab und zu da noch als eigenen Bereich herauszieht ist die Datenhaltung. Wie sich das dann auf ”das” MVC-Modell abbildet ist noch einmal eine ganz andere Frage, denn von MVC gibt es eine ganze Menge verschiedener Interpretationen die sich auch teilweise widersprechen. Wenn man MVC sagt, muss man also in der Regel auch dazu sagen welche konkrete Ausprägung gemeint ist.

@snafu: Vollzitate machen Sinn wenn man Angst hat jemand könnte seine Antwort noch mal neu/umschreiben. Warum sollte man vor so etwas Angst haben‽ Nun es gibt Leute die haben halt Angst vor Sachen die sie selber schon mal gemacht haben. ;-)
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

OK sanfu & BlackJack

Vielen Dank für eure hilfreichen Erklärungen.

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

snafu hat geschrieben:Das Event kann halt nur ausgelöst werden, aber man kann mit dem Event keine weiteren Parameter übergeben.
Das macht ja nichts, wenn man keine weiteren Parameter übergeben kann. Wenn man die Daten zuvor in einer Queue hinterlegt hat, dann können diese Events die Abarbeitung der Queue triggern. Also, kein Manko für den, der weiß wie. Daten in Queue ablegen - können ganze Funktionsaufrufe sein -, Event für Abarbeitung generieren: event_generate()
BlackJack

@Alfons Mittelmeyer: Man kann dann aber nur einen Listener für das Ereignis registrieren, sonst bekommt man Probleme oder muss dann doch wieder eine Art Dispatcher schreiben bei dem man dann auch gleich alles selber schreiben könnte oder eine andere fertige Lösung einbauen könnte.
Benutzeravatar
kbr
User
Beiträge: 1506
Registriert: Mittwoch 15. Oktober 2008, 09:27

Alfons Mittelmeyer hat geschrieben:Daten in Queue ablegen - können ganze Funktionsaufrufe sein -, Event für Abarbeitung generieren: event_generate()
Folgendes Szenario: eine Funktion generiert Daten, die für ein Event benötigt werden, schreibt diese in eine Queue und löst das Event aus. In einem anderen Thread passiert das gleiche. Welches Event wird zuerst abgearbeitet und zieht welche Daten aus der Queue?
Wenn schon so kompliziert, dann wäre beispielweise ein entsprechend angepasstes Dictionary (oder auch Redis) die bessere Wahl.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@snafu Ja wenn die Anwendung relativ einfach ist schon. Aber man denke an mehrere Widgets in einer Anwendung. Mit dem GUI Designer wählt man eines aus. Da gibt es dann die Basis Layout Module Pack, Grid. Place Layout, die alle darauf reagieren müssen und anzeigen müssen, ob es jetzt dieses oder jenens Layout ist. Und das jeweilige Modul soll dann auch die entsprechenden Koordinaten, oder row, column oder left,top,bottom,right anzeigen. Dann gibt es das Modul das die Config Eigenschaften anzeigt und editieren läßt. Dann gibt es das Modul das die Layout Eigenschaften (pack_info, grid_info oder place_info) anzeigt und editieren läßt und dann gibt es das Selektionsmodul, das anzeigt, welches Widget ausgewählt ist, das die Buttons zur Auswahl der andern Widgets im Container erzeugt, zum Wechseln in Unterverzeichnisse, zum Wechsel in das übergeordnete Verzeichnis.

Hier nur einfach senden: send(''SELECTION_CHANGED") und für das Navigationsmodul: send('SHOW_SELECTION')

Und wenn man ein Widget mit Place Layout mit der Maus in der Anwendung bewegt, sollen die Koordinaten x,y sowohl im Basis Layout Modul PlaceLayout angezeigt werden wie auch in den Detail Layout Optionen.
Mit Message System kein Problem, ohne ein solches sehr problematisch. Keine Ahnung, wie man dann so etwas sonst noch überschaubar lösen könnte. Hier ist es eben nur:

send('POSITION_CHANGED') # für PlaceLayout Modul
send('LAYOUT_VALUES_REFRESH') # für Detailed Layout Option Modul

Ich habe keine Ahnung, was ein Widget in der Anwendung bei Mousemove Event sonst aufrufen sollte, um auf einen etwaigen GUI Designer in einem TopLevel Window zuzugreifen. Wenn man ihn versehentlich schließt und damit restlos beseitigt, käme es anders wohl zum Crash.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

kbr hat geschrieben:
Alfons Mittelmeyer hat geschrieben:Daten in Queue ablegen - können ganze Funktionsaufrufe sein -, Event für Abarbeitung generieren: event_generate()
Folgendes Szenario: eine Funktion generiert Daten, die für ein Event benötigt werden, schreibt diese in eine Queue und löst das Event aus. In einem anderen Thread passiert das gleiche. Welches Event wird zuerst abgearbeitet und zieht welche Daten aus der Queue?
Wenn schon so kompliziert, dann wäre beispielweise ein entsprechend angepasstes Dictionary (oder auch Redis) die bessere Wahl.
Wenn es sich um ein und dieselbe Queue handelt, dann soll sie auch nur ein und denselben Zweck haben und nur eine Art Event die Abarbeitung auslösen. Angenommen es ging darum, Messages an Message Callbackfunktionen in der Hauptanwendung zu senden. Dann kann man die send('MessageID',message) Funktion nach mainloop Start mithife eines generierten Events etwa <<SEND>> implementieren. Und solche Messages dürfen dann auch von einem anderen Thread aus gesendet werden. Man kann sogar direct GUI Funktionen von einem andern Thread aus aufrufen über send('execute_function*,lambdacommand). Und wer jetzt eher dran ist und wer nicht, ist völlig egal. Was zuerst abgelegt wurde, wird zuerst abgearbeitet. Ob der Trigger dazu aus der Hauptanwendung oder einem thread stammt, ist egal. Es müssen nur soviel Trigger sein, wie Queue Einträge. Und wenn man verschiedene Dinge mit Daten tun will, dann ist das auch kein Problem, denn mit der MessageID hat man ja seine eigenen Events und kann verschiedene Empfänger - wie etwa mein Empfänger 'execute_function' - registrieren, die unterschiedliches mit den Daten tun. Der generierte event <<SEND>> ist nur der Trigger. Der eigentliche Event ist die MessageID.

Für eine Queue gilt: keine verschiedenen Events als Trigger verwenden, nur immer denselben. Und herunterzuholen hat irgenwer anders, auch kein anderer Thread, nichts.

Will man von der GUI in einen anderen Thread senden, dann müßte das über eine andere Queue geschehen, die durch ein Python Event für diesen anderen Thread getriggert wird und nicht durch ein GUI event. Wer so etwas haben will muss sich eben entsprechende Callbackfunktionen mit so einem zum Thread gehörigen Event überlegen.

Da würde es auch Sinn macken, Nachrichten zu packen, etwa send('TO_THREAD1',('DO_SOMETHING',message))

Also die Callbackfunktionen für einen anderen Thread müssten dann so etwas beinhalten: sendToThread1(messageID,message). Und diese Funktion sendToThread1 müsste dann mit einer anderen Queue und einem zu diesem Thread gehörigen Event implementiert werden.

Dann können andere Threads und die GUI beliebig Daten austauschen. Ein anderer Thread könnte dann Dinge tun, die etwas länger dauern - etwa etwas aus dem Internet laden - und blockert dann nicht die GUI
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Habe nochmals proxy.py überarbeitet. Jetzt threadsicher nach mainloop und arbeitet auch schon vorher. Enthält auch GUI Funktionsaufruf von anderem Thread aus: execute_lambda
Sorry wegen Tab und nicht vier Leerzeichen. Mein Editor macht es leider nicht anders (für python3):

Code: Alles auswählen

import queue

_mainloop_started = False
Proxy=None

class MessageProxy:

	def __init__(self,app):
		self.reset(app)

	def reset(self,app):
		self.app = app
		self.Dictionary = {}
		self.Queue = queue.Queue()
		self.owners = {}
		self.counter = 0
		self._register("execute_function",lambda msg: msg())
		self.app.bind("<<SEND>>",self._send)


	def send(self,msgid,msgdata=None):
		global _mainloop_started
		if _mainloop_started:
			self.Queue.put((msgid,msgdata))
			self.app.event_generate("<<SEND>>", when="tail")
		else:
			if self.counter == 0: self._sendImmediate(msgid,msgdata)
			else: self.Queue.put((msgid,msgdata))

	def execute_lambda(self,cmd): self.send("execute_function",cmd)

	# used before mainloop has started - not thread secure - and the first has to wait until all messages are executed
	def _sendImmediate(self,msgid,msgdata=None):
		while True:
			if msgid in self.Dictionary:
				receivers = self.Dictionary[msgid].items()
				self.counter += 1
				for receive,active in receivers:
					if active: receive(msgdata)
				self.counter -= 1

			if self.counter > 0: return
			if self.Queue.empty(): return
			data = self.Queue.get()			
			msgid = data[0]
			msgdata = data[1]

	def _send(self,*args):
		try:
			data = self.Queue.get()
			msgid = data[0]
			msgdata = data[1]
			if msgid in self.Dictionary:
				receivers = self.Dictionary[msgid].items()
				for receive,active in receivers:
					if active: receive(msgdata)
		except: print("sendQeue error")

	def _register(self,msgid,receive):
		if msgid not in self.Dictionary: self.Dictionary[msgid] = {}
		self.Dictionary[msgid][receive] = True

	def do_receive(self,owner,msgid,receive):
		if not owner in self.owners: self.owners[owner] = {}
		self.owners[owner][receive]=msgid
		self._sendImmediate('execute_function',lambda: self._register(msgid,receive))

	def activate_receive(self,msgid,receive,flag):
		if msgid in self.Dictionary:
			receivers = self.Dictionary[msgid]
			if receive in receivers:
				receivers[receive] = flag

	def _unregister2(self,msgid,receive):
		if msgid in self.Dictionary:
			receivers = self.Dictionary[msgid]
			if receive in receivers:
				receivers.pop(receive,None)
				if len(receivers) == 0: self.Dictionary.pop(msgid,None)
				
	def _unregister1(self,msgid,receive):
		self.activate_receive(msgid,receive,False)
		self._sendImmediate('execute_function',lambda: self._unregister2(msgid,receive)) 

	def undo_receive(self,owner,msgid,receive):
		if owner in self.owners:
			if receive in self.owners[owner]: self.owners[owner].pop(receive,None)
		self._unregister1(msgid,receive)
		
	def undo_receiveAll(self,owner):
		if owner in self.owners:
			messages = self.owners[owner]
			self.owners.pop(owner,None)
			for receive,msgid in messages.items():
				self._unregister1(msgid,receive)

# define message callback - the callback function must have minimum one parameter for the message - same as event callbacks with message instead of event
def do_receive(owner,msgid,receive): Proxy.do_receive(owner,msgid,receive)

# send a message to registered receivers
def send(msgid,message=None): Proxy.send(msgid,message)

# calling a GUI command - thread secure, after mainloop has started
def execute_lambda(cmd): Proxy.execute_lambda(cmd)

# this you should call, if you destroy the owner - normally a container frame or an instance of a class
def undo_receiveAll(owner): Proxy.undo_receiveAll(owner)

# undefine message callback - normally not used
def undo_receive(owner,msgid,receive): Proxy.undo_receive(owner,msgid,receive)
 
# ==========  Initialising the Message System ============================

 # call this when oder after initialising your tk (root = tk.Tk())
def init_proxy(root):
	global Proxy
	_mainloop_started = False
	Proxy = MessageProxy(root)
	return root

# call this immediately before you start your mainloop
def mainloop_starts():_mainloop_started = True

# ==========================================================================
Also irgendwie scheinen mir die normalen command und event callbacks nicht sicher zu sein. Wenn ich etwa einen zweiten GUI Designer nachlade, stürzt das Programm ab.
Wenn ich allerdings commands und events nochmals mittels execute_lambda über die Queue schicke, läuft alles wunderbar ohne Probleme.

Das hatte ich bei anderen Gelegenheiten auch schon gemerkt. Ob das ein Ubuntu Problem sein könnte? Aber über die Queue als Message alles problemlos.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Die Routine hatte ich gestern neu geschrieben. Doch hat sie eine Unsicherheit im Code. Wenn man nämlich während einer Message noch einen weiteren Receiver für dieselbe deklariert, würde sie crashen. Da hatte ich voreilig diese Zeile geändert:

Code: Alles auswählen

self._sendImmediate('execute_function',lambda: self._register(msgid,receive))
Ursprünglich war es:

Code: Alles auswählen

self.send('execute_function',lambda: self._register(msgid,receive))
oder anders formuliert:

Code: Alles auswählen

self.execute_lambda(lambda: self._register(msgid,receive))
Wenn man nicht immediate sondern normal aufruft, befindet man sich in einem eigenenständigen Queueeintrag für die Message 'execute_function'. Und an dieser ändert man nichts. Das war bei do_receive und _unregister1 der Fall. Daher nochmals berichtigt und diesmal mit Leerzeichen statt Tab:

Code: Alles auswählen

import queue
 
_mainloop_started = False
Proxy=None
 
class MessageProxy:
 
    def __init__(self,app):
        self.reset(app)

    def reset(self,app):
        self.app = app
        self.Dictionary = {}
        self.Queue = queue.Queue()
        self.owners = {}
        self.counter = 0
        self._register("execute_function",lambda msg: msg())
        self.app.bind("<<SEND>>",self._send)


    def send(self,msgid,msgdata=None):
        global _mainloop_started
        if _mainloop_started:
            self.Queue.put((msgid,msgdata))
            self.app.event_generate("<<SEND>>", when="tail")
        else:
            if self.counter == 0: self._sendImmediate(msgid,msgdata)
            else: self.Queue.put((msgid,msgdata))

    def execute_lambda(self,cmd): self.send("execute_function",cmd)

    # used before mainloop has started - not thread secure - and the first has to wait until all messages are executed
    def _sendImmediate(self,msgid,msgdata=None):
        while True:
            if msgid in self.Dictionary:
                receivers = self.Dictionary[msgid].items()
                self.counter += 1
                for receive,active in receivers:
                    if active: receive(msgdata)
                self.counter -= 1

            if self.counter > 0: return
            if self.Queue.empty(): return
            data = self.Queue.get()                
            msgid = data[0]
            msgdata = data[1]

    def _send(self,*args):
        try:
            data = self.Queue.get()
            msgid = data[0]
            msgdata = data[1]
            if msgid in self.Dictionary:
                receivers = self.Dictionary[msgid].items()
                for receive,active in receivers:
                    if active: receive(msgdata)
        except: print("sendQeue error")

    def _register(self,msgid,receive):
        if msgid not in self.Dictionary: self.Dictionary[msgid] = {}
        self.Dictionary[msgid][receive] = True

    def do_receive(self,owner,msgid,receive):
        if not owner in self.owners: self.owners[owner] = {}
        self.owners[owner][receive]=msgid
        self.execute_lambda(lambda: self._register(msgid,receive))

    def activate_receive(self,msgid,receive,flag):
        if msgid in self.Dictionary:
            receivers = self.Dictionary[msgid]
            if receive in receivers:
                receivers[receive] = flag

    def _unregister2(self,msgid,receive):
        if msgid in self.Dictionary:
            receivers = self.Dictionary[msgid]
            if receive in receivers:
                receivers.pop(receive,None)
                if len(receivers) == 0: self.Dictionary.pop(msgid,None)
                           
    def _unregister1(self,msgid,receive):
        self.activate_receive(msgid,receive,False)
        self.execute_lambda(lambda: self._unregister2(msgid,receive)) 

    def undo_receive(self,owner,msgid,receive):
        if owner in self.owners:
            if receive in self.owners[owner]: self.owners[owner].pop(receive,None)
        self._unregister1(msgid,receive)
           
    def undo_receiveAll(self,owner):
        if owner in self.owners:
            messages = self.owners[owner]
            self.owners.pop(owner,None)
            for receive,msgid in messages.items():
                self._unregister1(msgid,receive)
 
# define message callback - the callback function must have minimum one parameter for the message - same as event callbacks with message instead of event
def do_receive(owner,msgid,receive): Proxy.do_receive(owner,msgid,receive)
 
# send a message to registered receivers
def send(msgid,message=None): Proxy.send(msgid,message)
 
# calling a GUI command - thread secure, after mainloop has started
def execute_lambda(cmd): Proxy.execute_lambda(cmd)
 
# this you should call, if you destroy the owner - normally a container frame or an instance of a class
def undo_receiveAll(owner): Proxy.undo_receiveAll(owner)
 
# undefine message callback - normally not used
def undo_receive(owner,msgid,receive): Proxy.undo_receive(owner,msgid,receive)
 
# ==========  Initialising the Message System ============================
 
 # call this when oder after initialising your tk (root = tk.Tk())
def init_proxy(root):
    global Proxy
    _mainloop_started = False
    Proxy = MessageProxy(root)
    return root
 
# call this immediately before you start your mainloop
def mainloop_starts():_mainloop_started = True
 
# ==========================================================================
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@snafu Du hattest etwas über andere Threads geschrieben. Langlaufende Funktionen über Threads ausführen:

Code: Alles auswählen

import threading

class Thread_Execute(threading.Thread):
   
    def __init__ (self, execute_lambda):
        self.execute = execute_lambda
        threading.Thread.__init__ (self)

    def run(self): self.execute()


do_receive(someowner,'THREAD_EXECUTE',lambda message: Thread_Execute(message).start())
Die Message enthält einfach den Funktionsaufruf als lambda Expression. Mit threadsicherem send kann der Thread dann das Ergebnis schicken
Zuletzt geändert von Alfons Mittelmeyer am Dienstag 1. September 2015, 11:02, insgesamt 1-mal geändert.
BlackJack

`Thread_Execute` erscheint mir überflüssig. Da könnte man statt ``Thread_Execute(message).start()`` auch einfach ``Thread(target=message).start()`` schreiben.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:`Thread_Execute` erscheint mir überflüssig. Da könnte man statt ``Thread_Execute(message).start()`` auch einfach ``Thread(target=message).start()`` schreiben.
Und was ist mit den Argumenten, die bei dieser message bereits enthalten sind? Bei target glaube ich, muss man die Argumente gesondert übergeben.

Unterschied: bei message() wird eine Funktion mit Parametern aufgerufen. target ist aber nur eine Funktion ohne Parameter.
BlackJack

@Alfons Mittelmeyer: Welche Argumente? In Deinem Beispiel sind keine. Dein `Thread_Execute` erwartet auch ein Argument was ohne Argumente aufgerufen wird.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Welche Argumente? In Deinem Beispiel sind keine. Dein `Thread_Execute` erwartet auch ein Argument was ohne Argumente aufgerufen wird.
Ich hätte gedacht, man könnte daraus erkennen, dass eine lambda Expresson gemeint ist:

Code: Alles auswählen

def __init__ (self, execute_lambda):
Also so etwas wie:

Code: Alles auswählen

message = lambda p1 = par1, p2 = par2, funct=myfunction: funct(p1,p2)
BlackJack

@Alfons Mittelmeyer: Ob Du das nun `lambda_expression` nennst ist eigentlich egal, was da erwartet und verarbeitet wird ist jedes beliebige aufrufbares Objekt das keine Argumente übergeben bekommt oder bekommen muss. Und genau das gleiche macht `Thread` mit `target`-Argument. Und Dein `message` aus dem letzten Beitrag ist so ein aufrufbares Objekt dem man keine Argumente beim Aufruf übergeben muss, also funktioniert das ganz prima mit `Thread` und `target`-Argument.

Edit: Und das würde man heute wohl auch eher als ``message = partial(funct, par1, par2)`` schreiben. Mit `functools.partial()`
Antworten