Tkinter Display clearen

Fragen zu Tkinter.
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

Ha! Vielen Dank für diese nützliche Info. Das man die Canvas Elemente im Nachhinein ändern kann wusste ich ja gar nicht. Bei PyGame musste man immer den gesamten Bildschirm löschen, daher bin ich bei Tkinter auch davon ausgegangen :)

So sieht dann meine Update Funktion und mainWindow klasse aus

Code: Alles auswählen

class mainWindow:
    def __init__(self, parent):
        pass

    def __call__(self, parent):
        self.gauge1 = digitalGauge(root, 0, 1, measuredItemsColor[0], measuredItemsValue[0], 500, measuredItems[0], 0.9)
        self.gauge2 = digitalGauge(root, 5, 1, measuredItemsColor[1], measuredItemsValue[1], 500, "Breite", 0.9)
        self.gauge3 = digitalGauge(root, 6, 1, measuredItemsColor[2], measuredItemsValue[2], 500, "Hoehe", 0.9)
        self.gauge4 = digitalGauge(root, 7, 1, measuredItemsColor[1], 2000, 6000, measuredItems[1], 0.9)

    def update(self):
        self.gauge1.update(measuredItemsValue[0])
        self.gauge2.update(measuredItemsValue[1])
        self.gauge3.update(measuredItemsValue[2])
        self.gauge4.update(measuredItemsValue[3])


class digitalGauge:
    def __init__(self, window, row, column, color, value, maxVal, name, gaugeScale):
        self.G = Canvas(window, bg="black", height=100, width=100)
        if (row <= 4):
            self.G.grid(column=column, row=row, sticky="WENS", rowspan=5)
        else:
            self.G.grid(column=column, row=row, sticky="WENS")
        self.xval = 20
        self.yval = 10
        self.maxVal = maxVal
        self.coord = self.xval + 5, self.yval + 5, (self.xval + 100) * gaugeScale, (self.yval + 100) * gaugeScale

        self.gaugeValue = maxVal / float(value)  # calculate the GaugeValue

        self.hand = self.G.create_arc(self.xval, self.yval, (self.xval + 100 * gaugeScale),
                                      (self.yval + 100 * gaugeScale), start=0,
                                      extent=-(220 / self.gaugeValue), fill=color)  # Draw hand

        self.outline = self.G.create_arc(self.xval - 3, self.yval - 3, (self.xval + 100 * gaugeScale + 3),
                                         (self.yval + 100 * gaugeScale + 3), start=0, extent=-220, style="arc",
                                         outline="white", width=2)  # draw outline

        self.valueBox = self.G.create_rectangle((self.xval + 50 * gaugeScale), self.yval + 20 * gaugeScale,
                                                self.xval + 100 * gaugeScale + 3, self.yval + 50 * gaugeScale,
                                                outline='white',
                                                width=2)  # draw Value Box

        self.value1 = self.G.create_text(self.xval + 54 * gaugeScale, self.yval + 22 * gaugeScale, anchor="nw",
                                         text=value,
                                         fill="white", font=(textFont, int(round(15 * gaugeScale))))

        self.value2 = self.G.create_text(self.xval, self.yval - 8, anchor="nw", text=name, fill="white",
                                         font=(textFont, int(round(19 * gaugeScale))))
    def update(self, valueUpdated):
        self.G.itemconfig(self.value1, text=valueUpdated)
        self.gaugeValue = self.maxVal / float(valueUpdated)
        self.G.itemconfig(self.hand, extent=-(220 / self.gaugeValue))

Läuft schon viel besser. Irgendwo ist jedoch wohl noch ein kleines Leck. Aber erst einmal muss ich mein gesamtes Programm aufräumen und Llassen einrichten :)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@tsaG Wenn ein Meßwert sich geändert hat, dann nur die betreffende Anzeige - vielleicht ein Label - in der GUI updaten, aber nicht die ganze GUI neu aufgauen. Die ist doch kein Movie!
BlackJack

@tsaG: Du könntest die Namen mal dem Style Guide for Python Code anpassen. Insbesondere Klassennamen sollte man deutlich von anderen Werten unterscheiden können.

Bei `mainWindow()` macht das `__call__()` und eine leere `__init__` keinen Sinn.

Wenn man anfängt Namen durchzunummerieren möchte man eigentlich eine Datenstruktur verwenden. Oft eine Liste. So auch im Fall der `gauge*`-Attribute in der Hauptfenster-Klasse.

Auf `measuredItemsColor` und `measuredItemsValue` sollte die Hauptfenster-Klasse nicht einfach so zugreifen. Da muss man erst das ganze Programm absuchen was das ist, wo das herkommt, wo das eventuell verändert wird. Das ist unübersichtlich. Auch für Methoden gilt wie für Funktionen: Alles was die verwenden, ausser Konstanten sollte als Argument übergeben werden und nicht einfach auf magische Weise irgendwie in der ”Umgebung” existieren. Das die Werte in den beiden Listen eigentlich paarweise zusammengehören und deshalb nicht in zwei unabhängigen Listen gespeichert werden sollten, habe ich ja schon mal geschrieben. In meinem Ansatz habe ich die beiden Werte, den Namen und den Maximalwert ja zu einem Objekt zusammengefasst so dass man das alles als *einen* Wert herumreichen kann und sich die Einzelteile nicht aus mehreren Listen zusammensuchen muss.

Klassen die von nichts anderem erben sollten in Python 2 von `object` erben, sonst funktionieren nicht alle Spracheigenschaften so wie sie sollten. Aber so etwas wie `digitalGauge` würde ich direkt von `Canvas` erben lassen. Das ”layouten” gehört nicht in die Wigdet-Klasse selbst. Das macht keines der bereits vorhandenen Widgets und man nimmt der Stelle die ein Exemplar erstellt sonst auch alle Freiheiten das anordnen zu lassen wie *sie* das möchte.

An das Objekt sollte man nur Werte als Attribute binden die auch tatsächlich relevanten Zustand darstellen und nicht einfach *alles* was einem in der `__init__()` so über den Weg läuft.
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

uiui, mittlerweile sieht es schon richtig schick aus :)

eine Frage noch
Klassen die von nichts anderem erben sollten in Python 2 von `object` erben, sonst funktionieren nicht alle Spracheigenschaften so wie sie sollten. Aber so etwas wie `digitalGauge` würde ich direkt von `Canvas` erben lassen. Das ”layouten” gehört nicht in die Wigdet-Klasse selbst. Das macht keines der bereits vorhandenen Widgets und man nimmt der Stelle die ein Exemplar erstellt sonst auch alle Freiheiten das anordnen zu lassen wie *sie* das möchte.
Ja, daran habe ich auch schon gedacht. Leider weiss ich nicht genau wie man das macht, also die Klasse digitalGauge als Objekt vom Typ Canvas zu erstellen.
Ich habe es mal so getestet, jedoch bekomme ich den Fehler:
AttributeError: digitalGauge instance has no attribute 'tk'

Code: Alles auswählen

class digitalGauge(Canvas):
    def __init__(self, window, row, column, color, value, maxVal, name, gaugeScale):
        self.G = Canvas(window, bg="black", height=100, width=100)
        xval = 20
        yval = 10
        self.maxVal = maxVal
        #coord = xval + 5, yval + 5, (xval + 100) * gaugeScale, (yval + 100) * gaugeScale

        gaugeValue = maxVal / float(value)  # calculate the GaugeValue

        self.hand = self.G.create_arc(xval, yval, (xval + 100 * gaugeScale),
                                      (yval + 100 * gaugeScale), start=0,
                                      extent=-(220 / gaugeValue), fill=color)  # Draw hand

        self.outline = self.G.create_arc(xval - 3, yval - 3, (xval + 100 * gaugeScale + 3),
                                         (yval + 100 * gaugeScale + 3), start=0, extent=-220, style="arc",
                                         outline="white", width=2)  # draw outline

        self.valueBox = self.G.create_rectangle((xval + 50 * gaugeScale), yval + 20 * gaugeScale,
                                                xval + 100 * gaugeScale + 3, yval + 50 * gaugeScale,
                                                outline='white',
                                                width=2)  # draw Value Box

        self.value1 = self.G.create_text(xval + 54 * gaugeScale, yval + 22 * gaugeScale, anchor="nw",
                                         text=value,
                                         fill="white", font=(textFont, int(round(15 * gaugeScale))))

        self.value2 = self.G.create_text(xval, yval - 8, anchor="nw", text=name, fill="white",
                                         font=(textFont, int(round(19 * gaugeScale))))


    def update(self, valueUpdated):
        self.G.itemconfig(self.value1, text=valueUpdated)
        gaugeValue = self.maxVal / float(valueUpdated)
        self.G.itemconfig(self.hand, extent=-(220 / gaugeValue))
Mittels

Code: Alles auswählen

    def hide(self):
        self.gauge1.grid_remove()
        self.gauge2.grid_remove()
        self.gauge3.grid_remove()
        self.gauge4.grid_remove()
    def show(self):
        self.gauge1.grid()
        self.gauge2.grid()
        self.gauge3.grid()
        self.gauge4.grid()
In der mainWindow Klasse werde ich dann die einzelnen Elemente Ausblenden, sodass Sie dann bei Displaywechsel einfach wieder eingeblendet werden.
Man könnte sie natürlich auch löschen und beim aufrufen von Mainwindow (entweder __call__() oder ein einfaches show()) wieder aufbauen, aber ich denke das geht auch erstmal so :)

Nebenbei werde ich mir dann auch mal den Style Guide anschauen :)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Code: Alles auswählen

class mainWindow:
    def __init__(self, parent): self.gauge = [None,None,None,None]
 
    def __call__(self, parent):
        self.gauge[0] = digitalGauge(root, 0, 1, measuredItemsColor[0], measuredItemsValue[0], 500, measuredItems[0], 0.9)
        self.gauge[1] = digitalGauge(root, 5, 1, measuredItemsColor[1], measuredItemsValue[1], 500, "Breite", 0.9)
        self.gauge[2] = digitalGauge(root, 6, 1, measuredItemsColor[2], measuredItemsValue[2], 500, "Hoehe", 0.9)
        self.gauge[3] = digitalGauge(root, 7, 1, measuredItemsColor[1], 2000, 6000, measuredItems[1], 0.9)
 
    def update(self):
        for i in range(len(self.gauge)): self.gauge[i].update(measuredItemsValue[i])
Und für hide und show auch so
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@tsaG: digitalGauge ist dann ein Objekt das von Canvas abgeleitet wird, self.G ist dann nicht mehr nötig, aber natürlich muß man in __init__ das ursprüngliche __init__ von Canvas aufrufen:

Code: Alles auswählen

class digitalGauge(Canvas):
    def __init__(self, window, row, column, color, value, maxVal, name, gaugeScale):
        Canvas.__init__(self, window, bg="black", height=100, width=100)
        xval = 20
        yval = 10
        self.maxVal = maxVal
        #coord = xval + 5, yval + 5, (xval + 100) * gaugeScale, (yval + 100) * gaugeScale
 
        gaugeValue = maxVal / float(value)  # calculate the GaugeValue
 
        self.hand = self.create_arc(xval, yval, (xval + 100 * gaugeScale),
                                      (yval + 100 * gaugeScale), start=0,
                                      extent=-(220 / gaugeValue), fill=color)  # Draw hand
 
        self.outline = self.create_arc(xval - 3, yval - 3, (xval + 100 * gaugeScale + 3),
                                         (yval + 100 * gaugeScale + 3), start=0, extent=-220, style="arc",
                                         outline="white", width=2)  # draw outline
 
        self.valueBox = self.create_rectangle((xval + 50 * gaugeScale), yval + 20 * gaugeScale,
                                                xval + 100 * gaugeScale + 3, yval + 50 * gaugeScale,
                                                outline='white',
                                                width=2)  # draw Value Box
 
        self.value1 = self.create_text(xval + 54 * gaugeScale, yval + 22 * gaugeScale, anchor="nw",
                                         text=value,
                                         fill="white", font=(textFont, int(round(15 * gaugeScale))))
 
        self.value2 = self.create_text(xval, yval - 8, anchor="nw", text=name, fill="white",
                                         font=(textFont, int(round(19 * gaugeScale))))
 
 
    def update(self, valueUpdated):
        self.itemconfig(self.value1, text=valueUpdated)
        gaugeValue = self.maxVal / float(valueUpdated)
        self.itemconfig(self.hand, extent=-(220 / gaugeValue))
Dieses gaugeScale ist etwas seltsam. Normalerweise will man ja ein width und height vorgeben und das Gauge passt sich dann automatisch in dieses Rechteck ein.

"for i in range(len(self.gauge))" ist ein Anti-Pattern, da man in Python direkt über die Elemente iterieren kann:

Code: Alles auswählen

    def update(self):
        for gauge, value in zip(self.gauges, self.measuredItemsValue):
            gauge.update(value)
@Alfons Mittelmeyer: nach einem Doppelpunkt sollte immer eine neue Zeile anfangen.
BlackJack

@tsaG: Du musst die `__init__()` von der `Canvas`-Klasse am Anfang der `__init__()` aufrufen: ``Canvas.__init__(self, window, bg="black", height=100, width=100)`` und dann aus allen ``self.G`` ein ``self`` machen denn das Objekt *selbst* ist dann ja ein `Canvas`-Exemplar. Berechnen und anpassen des Zeigers und des Wertes als Text sollte nur einmal im Code stehen. Also den Text leer lassen und den Zeiger auf der 0-Position zeichnen und dann von der `__init__()` aus die Aktualisierungsmethode aufrufen.

Wenn man die einzelnen Seiten in einen `Frame` steckt, also zum Beispiel die Klasse für die jeweilige Seite von `Frame` erben lässt, dann braucht man nicht alle Einzelteile einzeln ”verstecken” und ”anzeigen” sondern kann das mit der kompletten Seite machen. Und das dann auch wieder vom Aufrufer aus und nicht im Widget selber.

@Alfons Mittelmeyer: Die Aufteilung mit dem `__init__()` und dem `__call__()` macht keinen Sinn. Eine Liste mit Platzhalterwerten ist „unpythonisch”. Man würde da entweder `None` zuweisen oder eine leere Liste wenn `update()` auch ohne vorheriges aufrufen des Objekts nicht auf die Nase fallen soll.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack ich wsollte nur darauf hinweisen, dass man es nicht nummeriert, sondern über Index machen soll. Am Besten man macht eine leere Liste und danach in der init gleich mit append.
BlackJack

@Alfons Mittelmeyer: Eher: man erstellt eine Liste wo die Elemente gleich drin sind.
Antworten