Einfügen von Werten in Listbox lässt App abstürzen

Fragen zu Tkinter.
Antworten
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

Hallo in die Runde.
Ich arbeite gerade an meiner ersten App mit Tkinter.
Um den Grundstein zu legen hatte ich mir Page 6.0.1 zur Hilfe genommen.

Ich habe eine Listbox in die ich von der Serialverbindung Werte eintrage.
Dabei soll die Liste auch gescrollt werden.

Jetzt crasht dabei immer die komplette Applikation,

Meine einzige Vermutung, dass es einfach zu schnell geht.
Ich hatte schon versucht mit .after() ein evtl. zu schnelles insert und scrollen zu verzögern, was auch Besserung brachte, aber trotzdem crasht es doch ab und zu.
Muss ich hier noch etwas blocken, weil das .insert() und scrollen nicht fertig ist?

Code einfügen geht nur als Anhang, weil es einfach zu lang ist: https://filehorst.de/d/dhHfEIef
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ok, ich habe da zwei Sekunden draufgeschaut. Und ganz ehrlich: ich weigere mich, auf einer Seite, die dermassen offensichtlich probiert, mich auf den falschen Link klicken zu lassen, irgendetwas anzuklicken. Ich brauche weder eine Abnehmkur noch eine Penisverlaengerung oder Bitcoin. Pack das in einen github gist oder so.

Wenn es stimmt, dass da 200KB Sourcecode interlegt sind, und man mal von einer Durchschnittszeilenbreite von 40 ausgeht, dann sind das 5000 Zeilen Code. Das ist absurd. Darin kann niemand etwas finden. Ich weiss nicht, wie du die geschrieben haben willst, aber die Erwaehnung von Page laesst nichts gutes vermuten.

Produzier ein kleines, geschlossenes Beispiel. Und noch ein Rat: solltest du mit Threading arbeiten, dann liegt da dein Problem. Das vertraegt sich mit GUIs nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Es sind nur 3.850 Zeilen. Dafür ist die Längste 9.928 Zeichen lang. Und ja es sieht furchtbar aus und es kommen Threads vor.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

Wie mans macht, ist's verkehrt. ;) 1x soll man alles zeigen. Nun ist es wohl zu viel des ganzen.
Einen Upload gibt es hier leider nicht, oder habe ich es übersehen?
pastebin.com bringt irgendwie nen fehler beim Einstellen.
Jetzt hier: https://github.com/harryberlin/SomeStuf ... ngs_pyf.py

Das lesen vom Serialport läuft im Thread.
Kann ich das auch einfach mit after starten?
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

~4000 Zeilen *sind* verkehrt. Das teilt man in Module auf. Wenigstens in zwei: die eigentliche Logik, und Seiten und Seiten und Seiten von "configure"-Aufrufen.

Man kann mit after arbeiten, wenn man die seriellen Ausgaben in eine Queue packt. Da du auch Windows anzusprechen scheinst, ist das wohl das empfohlene Vorgehen. Unter unixoiden wuerde ich mit createfilehandler einfach auf eintreffende Nachrichten warten, aber das geht mit Windows alles nicht. Da waere Qt mit QSerialPort wahrscheinlich besser, weil das den Windows-Call WaitForMultipleObjects eher unterstuetzen duerfte. Habe ich aber nicht geprueft. Ein weiterer Vorteil: 95% des "codes" verschwinden in einer XML-ui-Datei.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@harryberlin: Man sollte alles zeigen was relevant ist um das Problem nachzuvollziehen. Der überwiegende Anteil von dem Quelltext wird nichts mit dem Problem zu tun haben.

Du fügst in dem anderen Thread etwas in die GUI ein. Darf man nicht. Übliche Lösung eine Queue in der aus dem GUI-Thread regelmässig mittels `after()` aus dieser Queue gelesen wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

In pycharm finde ich es gar nicht so unübersichtlich.
Ja die langen Zeilen brauchen nen Umbruch. *ascheaufmeinhaupt*

Wie mache ich es dann, um die join() funktion zu haben?
Warum sollte ich es noch in eine Queue packen? Würde noch einen zusätzlichen Thread/after bedeuten, der dann in die Listbox schreibt, oder?
Mit after bleibe ich im GUI Thread?
empty Sig
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@harryberlin: Wenn Du die Daten nicht in die Queue packst, wie würdest Du sie denn sonst zwischen den Threads austauschen wollen?

Und nein, keinen zusätzlichen Thread. Zusätzliche Threads sind ja gerade das Problem. Der `aftert()`-Aufruf und der Rückruf müssen im GUI-Thread sein, weil das ja genau das Problem löst das man nicht aus anderen Threads am Zustand des Tk/Tcl-Interpreters herumfummeln darf.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

also, wenn ich jetzt den reading thread mit after starte beginnt die sanduhr zu laufen. hmm, confuse.
ist after_cancel der rückruf?
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du sollst nichts in after starten. In after schaust du nur, ob Daten in der Queue sind, die aus dem anderen Thread, der lange vorher zu Programmstart erzeugt wurde, herruehren.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ah d.h. threads kann man nutzen, nur für gui manipulationen darf man's nur aus dem guithread heraus?
wenn das so richtig verstanden ist, dann hats vermutlich klick gemacht.
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

So ist das, ja.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ok, danke, dann schraub ich jetzt mal am code.

bei den buttons habe ich z.B. bei Close immer eine Verzögerung (der Button bleibt gedrückt hängen), deswegen hatte ich das mit Thread gemacht.
nach umstellung

von:

Code: Alles auswählen

self.btnClose.configure(command=self.btnClose_click)
auf:

Code: Alles auswählen

self.btnClose.bind('<Button-1>', lambda e: self.btnClose_click)
passiert gar nichts mehr.
mit:

Code: Alles auswählen

self.btnCustom.bind('<Button-1>', lambda e: self.after(1, self.btnCustom_click))
geht es wieder, aber trotzdem eine Verzögerung.
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist doch geraten. Das erste lambda macht natuerlich nichts, weil es die Funktion btnClose_click nunmal nicht aufruft. Das zweite hat die gleiche Verzoegerung, die auch die erste Variante hat - denn was auch immer da verzoegert, es liegt nicht daran, dass tkinter da irgendetwas verspaetet macht. Sondern dein Code macht irgendwas, das eben eine Weile dauert. Nur weil man das mit 1ms Verzoegerung versehen von after aufrufen laesst, passiert da nichts magisch schneller.
harryberlin
User
Beiträge: 227
Registriert: Donnerstag 17. Dezember 2015, 12:17

ja schon, das event wird ja erst nach release das taste ausgelöst. warum bleibt dann der button hängen?
die verzögerung ergibt sich, weil der serial thread aufs beenden wartet join().

wenn tkinter nach release den befehlt ausführt, dann bedeutet das die oberfläche optisch erst nach dem event angepasst wird.
empty Sig
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Weil tkinter erstmal idle erreichen muss, und dann draw-calls abarbeitet. Ich vermute mal, du kannst das mit einem längeren timeout im after lösen. Oder noch besser: der zu beendende thread Steck eine finale message in die Queue, zb None. Und wenn die da ist, joint man im After, was ja dann sofort geht. Und beendet.
Antworten