Hallo Allerseits,
ich arbeite verzweifelt daran einen Multithread, genauer gesagt einen Dualthread unter Tkinter zu realisieren. Die beiden Threads sollen mit unterschiedlicher Taktung laufen, die durch die Funktion "sleep" definiert werden.
Das Problem ist, dass immer nur ein Thread lauft. Ziel wäre beide Threads mit unterschiedlicher Taktung gleichzeitig nebeneinander laufen zu lassen.
Dazu habe ich einen Beispielcode erstellt um zu verdeutlichen was ich genau meine.
#!/usr/bin/python
import Tkinter
import time
import threading
import random
import Queue
from threading import Thread
class GuiPart:
global th3
def __init__(self, master, queue, endCommand):
self.queue = queue
self.root=master
self.button1 = Tkinter.Button(master, text='Done', command=endCommand)
self.button1.place(x = 160, y = 50, width=80, height=20)
self.label1 = Tkinter.Label(self.root, text='label1' ,bg="white", fg="red",anchor = Tkinter.W)
self.label1.place(x = 60, y = 100, width=250, height=20)
self.label2 = Tkinter.Label(self.root, text='label2' ,bg="white", fg="red",anchor = Tkinter.W)
self.label2.place(x = 60, y = 150, width=250, height=20)
self.root.geometry("%dx%d+0+0" % (400, 400))
def processIncoming(self):
while self.queue.qsize():
try:
msg = self.queue.get(0)
print msg
except Queue.Empty:
pass
class ThreadedClient:
def __init__(self, master):
self.master = master
self.queue = Queue.Queue()
self.gui1 = GuiPart(master, self.queue, self.endApplication)
self.running = 1
self.thread1 = threading.Thread(target=self.workerThread1)
self.thread1.start()
self.periodicCall()
def periodicCall(self):
self.gui1.processIncoming()
if not self.running:
import sys
sys.exit(1)
self.master.after(100, self.periodicCall)
def workerThread1(self):
while self.running:
time.sleep(1)
msg = rand.random()
#self.queue.put(msg)
self.gui1.label1.config(text=msg)
def endApplication(self):
self.running = 0
class ThreadedClient2:
def __init__(self, master):
self.master = master
self.queue = Queue.Queue()
self.gui2 = GuiPart(master, self.queue, self.endApplication)
self.running = 1
self.thread2 = threading.Thread(target=self.workerThread2)
self.thread2.start()
self.periodicCall()
def periodicCall(self):
self.gui2.processIncoming()
if not self.running:
import sys
sys.exit(1)
self.master.after(100, self.periodicCall)
def workerThread2(self):
while self.running:
time.sleep(0.5)
msg = rand.random()
#self.queue.put(msg)
self.gui2.label2.config(text=msg)
def endApplication(self):
self.running = 0
rand = random.Random()
root = Tkinter.Tk()
client = ThreadedClient(root)
client2 = ThreadedClient2(root)
root.mainloop()
Ich hoffe, dass mir Jemand helfen kann.
Vielen Dank im voraus!
Multithreading in Tkinter
@UnixX: neue Programme sollte man nicht mehr mit Python2 schreiben. Python3.7 ist aktuell.
`global` sollte man nicht benutzen, an der Stelle ist es auch sinnlos und `th3` wird auch nirgends verwendet.
Warum erzeugst Du zweimal `GuiPart`-Instanzen für beide Threads, die Knöpfe und Labels exakt übereinander malen?
Für boolsche Werte gibt es True und False, das sollte man statt 1 oder 0 verwenden.
Mitten in `periodicCall` sollte kein sys.exit vorkommen. Gerade GUI-Programme wollen vielleicht noch aufräumen und nicht hart abgebrochen werden. Warum rufst Du periodicCall in Deiner Threadklasse per after immer wieder auf, statt das in der GUI mit processIncoming zu machen?
Aus einem Thread heraus, wie in `workerThread1` darf die GUI nicht verändert werden. Dafür hast Du doch die Queues und dieses komische periodicCall eingebaut.
`global` sollte man nicht benutzen, an der Stelle ist es auch sinnlos und `th3` wird auch nirgends verwendet.
Warum erzeugst Du zweimal `GuiPart`-Instanzen für beide Threads, die Knöpfe und Labels exakt übereinander malen?
Für boolsche Werte gibt es True und False, das sollte man statt 1 oder 0 verwenden.
Mitten in `periodicCall` sollte kein sys.exit vorkommen. Gerade GUI-Programme wollen vielleicht noch aufräumen und nicht hart abgebrochen werden. Warum rufst Du periodicCall in Deiner Threadklasse per after immer wieder auf, statt das in der GUI mit processIncoming zu machen?
Aus einem Thread heraus, wie in `workerThread1` darf die GUI nicht verändert werden. Dafür hast Du doch die Queues und dieses komische periodicCall eingebaut.
Ich habe nicht viel Ahnung von python, oder programmieren allgemeinSirius3 hat geschrieben: Donnerstag 6. Juni 2019, 17:15 @UnixX: neue Programme sollte man nicht mehr mit Python2 schreiben. Python3.7 ist aktuell.
`global` sollte man nicht benutzen, an der Stelle ist es auch sinnlos und `th3` wird auch nirgends verwendet.
Warum erzeugst Du zweimal `GuiPart`-Instanzen für beide Threads, die Knöpfe und Labels exakt übereinander malen?
Für boolsche Werte gibt es True und False, das sollte man statt 1 oder 0 verwenden.
Mitten in `periodicCall` sollte kein sys.exit vorkommen. Gerade GUI-Programme wollen vielleicht noch aufräumen und nicht hart abgebrochen werden. Warum rufst Du periodicCall in Deiner Threadklasse per after immer wieder auf, statt das in der GUI mit processIncoming zu machen?
Aus einem Thread heraus, wie in `workerThread1` darf die GUI nicht verändert werden. Dafür hast Du doch die Queues und dieses komische periodicCall eingebaut.

Wäre nett, wenn du den Code mal richtigstellen würdest.
Der eine Thread aktualisiert die Bilder eines Videostreams, der andere überträgt die Mausposition und die Signale gedrückter Tasten an einen Roboter.__deets__ hat geschrieben: Donnerstag 6. Juni 2019, 17:06 Bitte benutz die Code-Tags, so ist dein Quellcode nicht lesbar. Im vollstaendigen Editor der "</>"-Button.
Und dann erlaube ich mir die Frage: warum reicht after nicht? Was sollen die Threads machen?
Wo genau müsste der Befehl "after" sinngemäß im Code stehen?
- __blackjack__
- User
- Beiträge: 14044
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@UnixX: Man sollte keine Programme mehr in Python 2 anfangen. Da läuft der Support am Ende des Jahres endgültig aus.
Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das bedeutet dann auch, das der Code nicht mehr einfach so auf die globale Variable `rand` zugreifen kann. Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen.
`place()` sollte man nicht benutzen weil das nur auf dem Rechner auf dem man das programmiert hat, oder solchen mit ähnlicher Anzeigeauflösung und Einstellungen funktioniert. Die Fenstergrösse sollte man nicht hart vorgeben. Muss man ja auch nur weil `place()` verwendet wurde. Die Stelle an der die Grösse des Hauptfensters festgelegt wird ist auch kaputt. Das passiert beim erstellen von jedem `GuiPart`-Objekt und immer mit der gleichen Grösse – egal wie viele man davon erstellen würde.
``global`` hat in einem sauberen Programm nichts zu suchen. Das hat an der Stelle wo es steht auch gar nicht die gewünschte Wirkung – denke ich – hab das noch nie auf Klassenebene gesehen. Der Name wird ja sowieso nirgends verwendet.
Der ``from threading import Thread``-Import wird nirgends verwendet.
Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).
`processIncoming()` verwendet unnötigerweise `qsize()`. Da würde man einfach solange ein nicht-blockierendes `get()` aufrufen bis man `Queue.Empty` auslöst und dann die ”Endlosschleife” abbrechen. Die 0 bei `get()` ist irreführend weil an der Stelle eine Wahrheitswert erwartet wird und wenn man weiss das die Methode ein Flag und einen Timeout-Wert als optionale Argumente erwartet, könnte man irrtümlich denken die 0 wäre ein Timeout-Wert. Da sollte `False` stehen, oder man ruft `get_nowait()` auf, was für den Leser deutlicher ist, wenn er keine Ahnung hat was das `False` bei `get()` bedeutet.
`ThreadedClient` und `ThreadedClient2` enthalten fast den gleichen Code. Das die Namen nummeriert sind, ist schon ein Hinweis, dass da etwas nicht stimmt. Auch an anderen Stellen werden Namen durchnummeriert – nicht machen! Entweder passende Namen ausdenken, oder vielleicht auch eine Datenstruktur wenn das Sinn macht, und natürlich auch überlegen ob man die Namen überhaupt braucht, beziehungsweise wie lange. Man muss nicht alles an Objekte binden, wenn man es danach nie wieder benötigt.
Beim `running`-Attribut gilt auch wieder: das sollte nicht 0 oder 1 sein – Python hat `False` und `True` für Wahrheitswerte.
`sys.exit(1)` sollte man nicht dort aufrufen wo es aufgerufen wird. Bei einem GUI-Programm sollte das beenden so gestaltet sein, dass Code nach dem `mainloop()`-Aufruf noch ausgeführt würde. Sonst hat man den Programmfluss nicht sauber unter Kontrolle.
Importe gehören an den Anfang des Moduls, nicht tief in Methoden versteckt und dort auch noch in einem ``if``-Zweig.
Deine Threads verändern Labeltexte – das ist ja gerade das was man verhindern will/muss und weshalb es die periodisch abgefragte(n) Queue(s) gibt.
Ich denke das ist noch zwei Nummern zu gross und Du solltest erst einmal die Grundlagen lernen.
Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Das bedeutet dann auch, das der Code nicht mehr einfach so auf die globale Variable `rand` zugreifen kann. Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen.
`place()` sollte man nicht benutzen weil das nur auf dem Rechner auf dem man das programmiert hat, oder solchen mit ähnlicher Anzeigeauflösung und Einstellungen funktioniert. Die Fenstergrösse sollte man nicht hart vorgeben. Muss man ja auch nur weil `place()` verwendet wurde. Die Stelle an der die Grösse des Hauptfensters festgelegt wird ist auch kaputt. Das passiert beim erstellen von jedem `GuiPart`-Objekt und immer mit der gleichen Grösse – egal wie viele man davon erstellen würde.
``global`` hat in einem sauberen Programm nichts zu suchen. Das hat an der Stelle wo es steht auch gar nicht die gewünschte Wirkung – denke ich – hab das noch nie auf Klassenebene gesehen. Der Name wird ja sowieso nirgends verwendet.
Der ``from threading import Thread``-Import wird nirgends verwendet.
Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).
`processIncoming()` verwendet unnötigerweise `qsize()`. Da würde man einfach solange ein nicht-blockierendes `get()` aufrufen bis man `Queue.Empty` auslöst und dann die ”Endlosschleife” abbrechen. Die 0 bei `get()` ist irreführend weil an der Stelle eine Wahrheitswert erwartet wird und wenn man weiss das die Methode ein Flag und einen Timeout-Wert als optionale Argumente erwartet, könnte man irrtümlich denken die 0 wäre ein Timeout-Wert. Da sollte `False` stehen, oder man ruft `get_nowait()` auf, was für den Leser deutlicher ist, wenn er keine Ahnung hat was das `False` bei `get()` bedeutet.
`ThreadedClient` und `ThreadedClient2` enthalten fast den gleichen Code. Das die Namen nummeriert sind, ist schon ein Hinweis, dass da etwas nicht stimmt. Auch an anderen Stellen werden Namen durchnummeriert – nicht machen! Entweder passende Namen ausdenken, oder vielleicht auch eine Datenstruktur wenn das Sinn macht, und natürlich auch überlegen ob man die Namen überhaupt braucht, beziehungsweise wie lange. Man muss nicht alles an Objekte binden, wenn man es danach nie wieder benötigt.
Beim `running`-Attribut gilt auch wieder: das sollte nicht 0 oder 1 sein – Python hat `False` und `True` für Wahrheitswerte.
`sys.exit(1)` sollte man nicht dort aufrufen wo es aufgerufen wird. Bei einem GUI-Programm sollte das beenden so gestaltet sein, dass Code nach dem `mainloop()`-Aufruf noch ausgeführt würde. Sonst hat man den Programmfluss nicht sauber unter Kontrolle.
Importe gehören an den Anfang des Moduls, nicht tief in Methoden versteckt und dort auch noch in einem ``if``-Zweig.
Deine Threads verändern Labeltexte – das ist ja gerade das was man verhindern will/muss und weshalb es die periodisch abgefragte(n) Queue(s) gibt.
Ich denke das ist noch zwei Nummern zu gross und Du solltest erst einmal die Grundlagen lernen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Das ganze System ist ziemlich unsinnig, die vielen Klassen unnötig, das aktualisieren viel zu verquer. Beispiele, wie man mit Threads und Tkinter arbeitet, gibt es hier im Forum genug.
Hier auf das nötigste reduziert (für Python3):
Hier auf das nötigste reduziert (für Python3):
Code: Alles auswählen
import tkinter as tk
import time
import random
from queue import Queue, Empty
from threading import Thread
class Gui:
def __init__(self, master, queue):
self.queue = queue
self.root = master
tk.Button(master, text='Done', command=master.quit).pack()
self.labels = [
tk.Label(self.root),
tk.Label(self.root),
]
for label in self.labels:
label.pack()
self.process_incoming()
def process_incoming(self):
try:
while True:
label, msg = self.queue.get_nowait()
self.labels[label]['text'] = f"{msg:.2f}"
except Empty:
pass
self.root.after(100, self.process_incoming)
def thread1(queue):
while True:
time.sleep(1)
msg = random.random()
queue.put((0, msg))
def thread2(queue):
while True:
time.sleep(1)
msg = 2 * random.random()
queue.put((1, msg))
def main():
queue = Queue()
Thread(target=thread1, args=(queue,), daemon=True).start()
Thread(target=thread2, args=(queue,), daemon=True).start()
root = tk.Tk()
gui = Gui(root, queue)
root.mainloop()
if __name__ == '__main__':
main()
Sirius3 hat geschrieben: Donnerstag 6. Juni 2019, 17:47 Das ganze System ist ziemlich unsinnig, die vielen Klassen unnötig, das aktualisieren viel zu verquer. Beispiele, wie man mit Threads und Tkinter arbeitet, gibt es hier im Forum genug.
Hier auf das nötigste reduziert (für Python3):Code: Alles auswählen
import tkinter as tk import time import random from queue import Queue, Empty from threading import Thread class Gui: def __init__(self, master, queue): self.queue = queue self.root = master tk.Button(master, text='Done', command=master.quit).pack() self.labels = [ tk.Label(self.root), tk.Label(self.root), ] for label in self.labels: label.pack() self.process_incoming() def process_incoming(self): try: while True: label, msg = self.queue.get_nowait() self.labels[label]['text'] = f"{msg:.2f}" except Empty: pass self.root.after(100, self.process_incoming) def thread1(queue): while True: time.sleep(1) msg = random.random() queue.put((0, msg)) def thread2(queue): while True: time.sleep(1) msg = 2 * random.random() queue.put((1, msg)) def main(): queue = Queue() Thread(target=thread1, args=(queue,), daemon=True).start() Thread(target=thread2, args=(queue,), daemon=True).start() root = tk.Tk() gui = Gui(root, queue) root.mainloop() if __name__ == '__main__': main()
Das werde ich mir mal zu Gemüte ziehen.
Vielen Dank für deine Antwort!