Frage eines Anfängers

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

kbr hat geschrieben:@gulash, sofern Du noch nicht erfolgreich vertrieben bist: don't do this at home and especially not at work. Dieser Code ist mal wieder ... Mist.
Also, es geht ja nicht darum, wie man es mit threads und dieser Eingabe richtig machen sollte - ich könnte dies auch zeigen - und mein Programm noch komplexer machen, es geht um Deine Funktionen zum 'zeichnen'. Ich lasse mal die Eingabe weg. Zum Testen kannst ja das Beispiel in __init__ verändern. Und keiner kann da sagen, es wäre darin Mist.:

Code: Alles auswählen

# -- python 3 ------
import tkinter as tk
from math import sqrt
  
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()

        # Beispiel ========
        self.zeichne('quadrat',300)
        self.zeichne('dreieck',300)
        
      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 # für Gleichseitigkeit: 1/2 * Wurzel 3 sind 60° und damit Gleichseitigkeit
            x1 = x0 + laenge
            y1 = y0
            x2 = x0 + laenge/2
            y2 = ybase
            self.canvas.create_line(x0,y0,x1,y1,x2,y2,x0,y0)
 
Application().mainloop()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Alfons Mittelmeyer hat geschrieben:Also, es geht ja nicht darum, ...
Es ging um die Anwendung von Threads und Queues in einer Form, die zu erratischem Verhalten führen kann, im Rahmen einer Anfängerfrage, die beides nicht erfordert und zudem den Fragesteller zu der fälschlichen Annahme verleiten könnte, dies wäre in der gezeigten Form guter Programmierstil.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons Mittelmeyer:
Professionell wäre, den OP nicht mit einem fehlerhaften Monster mit unbekannten Konzepten zuzumüllen. Du bringst da GUI-Programmierung rein, was Kenntnis von OOP und Ereignissteuerung braucht und dann noch Threads. Und das Ganze noch fehlerhaft. Das hilft weder dem OP noch anderen Leuten, die ebenfalls über die Frage stolpern und das noch nicht (einschätzen) können. Didaktik 6, setzen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:Du bringst da GUI-Programmierung rein, was Kenntnis von OOP und Ereignissteuerung braucht und dann noch Threads.
Die Gui braucht von alledem nichts. Es wäre aber gut, wenn man das auch testen könnte, ohne jedesmal das Script zu verändern oder aber daß man in der Gui noch Elemente wie Checkboxen, Eingabefelder und Buttons reinmacht, um das Verhalten der Gui zu testen. Damit hat man aber die Gui verändert, was nicht sein soll. Am einfachsten ist doch, wenn man einfach eintippen kann oder ein Script für die Testfälle aufrufen kann. Und dafür sollte man wohl kein solches Monster schreiben, wenn es auch mit wenig Aufwand geht, auch wenn das eventuell nicht ganz Threadsicher ist, oder den Thread auf die Gui über ein MinimaiInterface zugreifen läßt und auch nicht auf gültige Parameter abbrüft.

Wenn man Funktionen schreibt, dann prüft man in denen doch auch nicht immer ab, ob die Parameter gültige Werte sind. Oder wenn man eine Funktion aufruft, werden auch nicht noch vorher die Parameter auf Gültigkeit überprüft
Zuletzt geändert von Alfons Mittelmeyer am Samstag 29. April 2017, 13:49, insgesamt 1-mal geändert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons Mittelmeyer:
Du erfasst des Pudels Kern nicht - es ist (fast) egal, ob Deine Lösung fehlerhaft ist oder nicht, sie ist zu komplex, um dem OP helfen zu können.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Alfons Mittelmeyer hat geschrieben:
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.
Diese Denke setzt voraus, dass der Anfaenger erkennt, was Anwendungszwecken genuegt, und was nur ausprobieren ist. Wenn er diese Faehigkeit hat, ist er per Definition kein Anfaenger mehr. Deine Begruendung ist also - nicht weiter ueberraschend - in sich falsch.

Sich nicht darum zu scheren ob jemand anderes falsche Dinge lernt ist zwar bedauernswert, aber hey - dein gutes Recht. Zu glauben das haette keine negativen Effekte oder waere gar gut ist allerdings fortgeschritten ignorant. Aber auch das ist ja nix neues...
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:@Alfons Mittelmeyer:
Du erfasst des Pudels Kern nicht - es ist (fast) egal, ob Deine Lösung fehlerhaft ist oder nicht, sie ist zu komplex, um dem OP helfen zu können.
Die erste Lösung war nicht zu komplex, sie war überschaubar, wenn auch nicht threadsicher.

Natürlich war sie nicht so, wie man es normalerweise in Anwendungen machen sollte. Wenn man das bekrittelt, kommt ein solches Monster heraus, das dem User nichts nützt, weil es für einen Testzweck overengeneered und für den User viel zu komplex ist.
BlackJack

@Alfons Mittelmeyer: Die ganze GUI ist für Anfänger schon Unsinn weil das ziemlich sicher nicht als Antwort auf die Aufgabe gefordert ist. Bei Anfängern die gerade eben erst angefangen haben bedeuten solche ”zeichnen”-Aufgaben in der Regel `print()`-Ausgaben mit Sternchen die die entsprechende Figur ergeben oder Turtle-Grafik um den Umgang mit Schleifen und von der Laufvariablen abhängige Werte zu lernen und wie man so etwas selbst entwickelt. Ziemlich sicher nicht GUI-Programmierung die dem Anfänger gleich OOP und ereignisbasiertem Programmfluss abverlangt, wo er doch gerade erst bei Schleifen ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@gulash: für Zeichnen nimmst Du am Besten keine Gui
Am Besten wäre für das Zeichnen wohl matplotlib geeignet

Code: Alles auswählen

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

def main():

    fig = plt.figure()
    ax = fig.add_subplot(111)

    def zeichne(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

            x = (x0,x1,x2,x3,x0)
            y = (y0,y1,y2,y3,y0)

            line = Line2D(x, y)
            ax.add_line(line)

    # Beispiel
    for i in range(10,90,10):
        zeichne('quadrat',i)

    ax.set_xlim(0,100)
    ax.set_ylim(0,100)
    plt.show()

main()
Evtl. zuvor matplotlib noch installieren, sofern nicht bereits vorhanden
Antworten