kbr hat geschrieben:Es ging um die Anwendung von Threads und Queues in einer Form, die zu erratischem Verhalten führen kann
Soll man dem Anfänger stets Code zeigen, der Anwendungszwecken genügt, wenn es nur um das Ausprobieren geht.
Auch das Ausprobieren solle man doch zeigen, ohne gleich eine Implementieren zu machen, die fast alle Fehler abfängt.
Hier kann vom Thread nicht auf Interna der Anwendung zugegriffen werden, weil nur bestimmte Messages mit bestimmten Parameter Typen vom Interface akzeptiert werden.
Aber warum immer gleich so etwas implementieren, wenn man nur auf die Schnelle etwas ausprobieren will? Und der Thread kann nur senden und nicht sonstwie auf die Gui ohne Regelverletzung zugreifen. self._queue zeigt an, dass man da nicht von außen zugreifen soll.
Code: Alles auswählen
# -*- coding: utf-8 -*-
# -- python 3 ------
import tkinter as tk
from threading import Thread
import queue
from math import sqrt
# === Applikation ============
class Application(tk.Tk):
def __init__(self,**kwargs):
tk.Tk.__init__(self,**kwargs)
self.canvas = tk.Canvas(self,bg = 'white',width=400,height=400)
self.canvas.pack()
def zeichne(self,figur,laenge):
if figur == "quadrat":
x0 = 10
y0 = 10
x1 = x0 + laenge
y1 = y0
x2 = x0 + laenge
y2 = y0 + laenge
x3 = x0
y3 = y0 + laenge
self.canvas.create_line(x0,y0,x1,y1,x2,y2,x3,y3,x0,y0)
# einfacher ginge es mit
# self.canvas.create_rectangle(x0,y0,x0+laenge,y0+laenge)
# aber mit dem Dreieck muss man es mit create_line machen
elif figur == "dreieck":
# eigentlich selbermachen
x0 = 10
ybase = 10
y0 = ybase + laenge*sqrt(3)/2
x1 = x0 + laenge
y1 = y0
x2 = x0 + laenge/2
y2 = ybase
self.canvas.create_line(x0,y0,x1,y1,x2,y2,x0,y0)
# ================= Inderface Queue nach GUI ============================
class Execute_Queue:
def __init__(self,queue,gui):
self.queue = queue
self.gui = gui
self.trigger_messages()
def trigger_messages(self):
self.execute_queue()
self.gui.after(100,self.trigger_messages)
def execute_queue(self):
while not self.queue.empty():
message = self.queue.get()
msg_id = message[0]
if not isinstance(msg_id,str):
print("Error: message id muss vom typ str sein und nicht {0}".format(type(msg_id)))
continue
msg_par = message[1]
par_count = len(msg_par)
if msg_id == 'zeichne':
if par_count != 2:
print("Error: falsche Parameteranzal '{0}' fuer message {1}".format(par_count,msg_id))
continue
if not isinstance(msg_par[0],str):
print("Error: Parameter 1 für message '{0}' muss vom Typ str sein und nicht {1}".format(msg_id,type(msg_par[0])))
continue
if not isinstance(msg_par[1],int):
print("Error: Parameter 2 für message '{0}' muss vom Typ int sein und nicht {1}".format(msg_id,type(msg_par[1])))
continue
self.gui.zeichne(msg_par[0],msg_par[1])
continue
if msg_id == 'erase':
self.gui.canvas.delete(tk.ALL)
continue
if msg_id == 'quit':
self.gui.quit()
continue
print("Error: message id '{0}' ist nicht definiert",format(msg_id))
# ====== Interface für Sender =====
class Send_Message:
def __init__(self,app_queue):
self._queue = app_queue
def send(self,*args):
if not len(args):
print("Send Error: Leere Message ohne Message ID")
else:
message_id = args[0]
parameters = args[1:]
self._queue.put((message_id,parameters))
# == Eingabe ====================
def eingabeschleife(message_interface):
figuren = { 'q' : 'quadrat', 'd' : 'dreieck', 'e' : 'erase' }
while True:
while True:
figur_buchstabe = input("quadrat = q, dreieck = d, e = erase : ")
if figur_buchstabe == '':
return
if figur_buchstabe in figuren:
figur = figuren[figur_buchstabe]
break
else:
print('\n? Fehler: falsche Eingabe\n')
if figur == 'erase':
message_interface.send("erase")
print()
continue
while True:
laenge_string = input("laenge: ")
if laenge_string == '':
return
try:
laenge = int(laenge_string)
break
except ValueError:
print('\n? Fehler: das war keine Zahl\n')
message_interface.send('zeichne',figur,laenge)
print()
def eingabe(message_interface):
# -- Beispiel ---
message_interface.send('zeichne','quadrat',300)
message_interface.send('zeichne','dreieck',300)
# ---------------
eingabeschleife(message_interface)
message_interface.send('quit')
# === Start =====================================
def main():
app_queue = queue.Queue()
message_interface = Send_Message(app_queue)
Thread(target=eingabe,args=(message_interface,)).start()
root = Application()
Execute_Queue(app_queue,root)
root.mainloop()
main()
Es ist doch ein Unding, es so zu machen, wenn es nur um das Ausprobieren geht.
Ganz professionell ist das aber auch noch nicht. Professionell werden nicht nur die Parametertypen sondern auch noch die Wertebereiche überprüft. Das würde man dann in Konfigurationsfiles verwalten und daraus die Abprüfung generieren.