Klassenübergreifend

Fragen zu Tkinter.
Antworten
rasbilly
User
Beiträge: 3
Registriert: Freitag 14. April 2017, 23:29

Hallo Leute,

ich habe eine Klasse die aus dem Netzwerk eine Zahl bekommt und mit einer IF-Abfrage ein Bild verändern soll.

Stark verkürzt:

Code: Alles auswählen

class Netzwerk(Thread):
	while True:                      
                       if data == '1':
                                # hier soll der Aufruf der Methode hin zb. ausloeser(1)
                        elif data == '2':
                                #
    ...
    
# soll aufgerufen werden (liegt nicht in der selben klasse)                               
def ausloeser(uebergabe):
         Button(master=frame, command=lokomotiveEins(uebergabe))                              
         
Da die Methode aber in einer anderen Klasse liegt, kann ich auf sie nicht zugreifen um sie aufzurufen.
Hat jmd einen Lösungsansatz oder Idee?
Hier nochmal der Komplette Code: http://codepad.org/jomv4QVv

Vielen dank im voraus und einen schönen Abend noch :)
Zuletzt geändert von Anonymous am Samstag 15. April 2017, 00:26, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@rasbilly: Ich sehe da nichts ”klassenübergreifendes”. `ausloeser()` ist eine Funktion die Du an der Stelle theoretisch einfach aufrufen könntest, auch wenn deren Inhalt nicht wirklich Sinn macht und das bei GUIs aus anderen Threads falsch ist.

Der Quelltext hat aber so viele Probleme das ich gar nicht weiss wo ich anfangen sollte. TCP-Kommunikation funktioniert *so* nicht. Die `Netzwerk`-Klasse ist semantisch keine Klasse, da hätte auch eine Funktion gereicht. Die Einrücktiefe und die Namenschreibweisen halten sich nicht überall an den Style Guide for Python Code. Konstanten innerhalb einer Funkion zu definieren ist mindestens ungewöhnlich. Namen zu nummerieren ist ein „code smell“, zumal es neben `thread1` keine weiteren gibt. Funktionen und Methoden sollten ausser auf Konstanten auf nichts zugreifen was nicht als Argument übergeben wurde. Keine globalen Variablen. `lokomotiveEins()` sollte beispielsweise nicht einfach so auf `lok` zugreifen. Ein `Canvas`-Objekt an den Namen `frame` zu binden ist verwirrend. Funktionen werden üblicherweise nach der Tätigkeit benannt die sie verrichten um sie einfacher von ”passiveren” Werten unterscheiden zu können. `lokomotiveEins` oder `ausloeser` sind keine Tätigkeiten. GUI-Rahmenwerke sind in der Regel nicht „thread safe“, man sollte also nicht von anderen Threads auf sie zugreifen ausser von dem in dem die Hauptschleife des GUI-Rahmenwerks läuft. Kommunizieren kann man zwischen Threads mittels `Queue.Queue`-Objekten die man im GUI-Thread dann mittels der `after()` regelmässig abfragen kann.
rasbilly
User
Beiträge: 3
Registriert: Freitag 14. April 2017, 23:29

@ BlackJack: Danke für die schnelle Antwort :)

Das mit der Netzwerkverbindung funktioniert von der Funktion her wunderbar. Ob das jetzt semantisch falsch ist kann ich nicht beurteilen.
Ich hab diese Fake Klasse, die jetzt ein Thread ist auch erst ganz normal eingebunden, aber der mainloop und die schleife vom Netzwerk haben sich gegenseitig blockiert und das hab ich mit meinem wissen nicht anders in den griff bekommen. Die Variante ohne wäre mir natürlich auch lieber.
Das mit den variablen Namen hab ich geändert :)

Ich weiß nicht warum, aber im Moment geht der Code. Gestern hieß es wenn ich "ausloeser(zahl)" in dem if-else aufgerufen hat, das er diese in der GUI nicht kennt, darum das übergreifend.

viele grüße und frohe Ostern
BlackJack

@rasbilly: Das mit der Netzwerkverbindung funktioniert nicht wunderbar, das ist falsch und zwar nicht nur semantisch (wie die Klasse) sondern technisch. Das kann nur dann funktionieren wenn die Daten immer genau ein Byte gross sind und der Sender die Verbindung nach senden des einen Bytes die Verbindung schliesst und nicht etwa weitere Daten sendet. Danach sieht der Code aber nicht aus, das es nur diesen recht eingeschränkten Sonderfall gibt. Alles andere *kann* ”funktionieren”, muss es aber nicht, und somit funktioniert das dann nicht.

TCP ist ein Datenstrom. Keine Übermittlung von Nachrichten/Paketen. Wenn man so etwas will, muss man ein Protokoll dafür implementieren, das sicherstellt, dass erst auf eine komplette Nachricht reagiert wird und auf jede Nachricht reagiert wird. Dazu muss der Empfänger die Daten lesen, gegebenfalls puffern, und in einzelne Nachrichten/Pakete zerlegen. Die `recv(N)`-Methode nimmt dabei 1 bis N Bytes vom Datenstrom. Das kann sowohl weniger als eine Nachricht sein (sofern die Nachrichten nicht alle auf 1 Byte beschränkt sind), also auch mehr als eine Nachricht sein.

Ob Funktion oder Klasse hat nichts mit Thread oder nicht Thread zu tun. Um einen Thread zu starten braucht man keine Klasse, das geht auch mit einer Funktion und der bereits bestehenden `Thread`-Klasse. Davon kann man auch Objekte erstellen und denen beim erstellen eine Funktion als `target`-Argument übergeben.

Und auch zum Schluss noch mal: Das der Code scheinbar geht, ändert nichts daran das er kaputt ist. Das Verändern der GUI aus einem anderen Thread als dem in dem die GUI läuft kann ”funktionieren”, kann aber auch spektakulär auf die Nase fallen. Es ist schlicht falsch das so zu machen.
rasbilly
User
Beiträge: 3
Registriert: Freitag 14. April 2017, 23:29

@BlackJack: es werden pro Sekunde ca 20 Zahlen Empfangen, die alle einen wert zwischen 1 und 99 haben. Dabei gibt es keine Probleme. Die Methode ist nicht dafür vorgesehen Strings oder Dateien zu übertragen und somit ausreichend. Sobald das Programm beendet ist schließt der Server Socket sich, damit alles sauber bleibt. Also treten keine Fehler auf und den rest fangen eh die try-catch Blöcke auf.

Das mein erstes Programm mit Tkinter und Python erstmal funktioniert ist für mich schon mal ein Erfolg.

Trotzdem danke für die kleinen Stichworte.
BlackJack

@rasbilly: Sorry, aber das Programm funktioniert *nicht*. Es ist Dir halt bisher noch nicht um die Ohren geflogen, aber es ist wirklich fehlerhaft.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Wenn Du eine Funktion aufrufen willst, dann muss sie bereits vor dem Aufruf definiert sein. In Deinem Beispiel wird sie erst hinterher definiert und ist somit der aufrufenden Funktion nicht bekannt. So geht das nicht.
Antworten