Tkinter Display clearen

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

Wow, vielen Dank für die Zahlreichen Tips!

Ja, ich war die letzten Tage dabei mein Skript etwas umzubauen und habe bisher als Basis den entwurf von Alfons genommen. Ich werde mir jetzt mal alle Sachen heraussuchen und schauen was ich davon brauchen kann :)

Hier habe ich mittlerweil mal etwas weiter gebastelt. Auch habe ich grid verwendet, leider kann ich mich damit nicht wirklich anfreunden da es sich automatisch vergrößert, am liebsten hätte ich ja eine feste größe...

Hier mein Script

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter
from Tkinter import Button, Tk, Canvas, Label

c_height = 476
c_width = 316

root = Tk()
root.config(width=(c_width - 40), height=c_height, bg="black", cursor="none")
root.resizable(width=0, height=0)

textFont = "Helvetica"

# Declare Variables
measuredItems = ["RPM", "Water", "EGT", "Oil Press", "Oil Temp", "Fuel Flow", "Fuel Quant."]
errorBoxItems = ["TEMP", "PRESS", "FUEL", "POWER", "ERROR"]
errorBoxItemsColor = ["red", "green", "green", "yellow", "green"]
measuredItemsColor = ["green", "green", "green", "green", "green", "green", "green"]
measuredItemsValue = [0, 10, 10, 0, 0, 0, 0]
testVal = 1

screen = None
loadActiveWindow = None


def setupGrid(parent):
    for i in range(0, 4):
        parent.rowconfigure(i, minsize=24)
    parent.rowconfigure(5, minsize=120)
    parent.rowconfigure(6, minsize=120)
    parent.rowconfigure(7, minsize=120)

    parent.columnconfigure(0, minsize=20)
    parent.columnconfigure(1, minsize=125)
    parent.columnconfigure(2, minsize=125)
    parent.columnconfigure(3, minsize=20)


def create_canvas(parent):
    global screen
    C = Canvas(parent, bg="black", height=c_height, width=(c_width - 40))
    C.grid(row=0, column=1, rowspan=8, columnspan=2)
    screen = C


def bootScreen(parent):
    pass
    # value1 = Label(parent, text="test").grid(row=1,column=1)  # Write value into the box


def mainWindow(parent):
    errorWindow(root)
    Testline = parent.create_line(140, 0, 140, 480, fill="red")
    Testline2 = parent.create_line(0, 240, 280, 240, fill="red")

    if (measuredItemsValue[0] < 500):

        measuredItemsValue[0] += 1
    else:
        pass

    gauge1 = digitalGauge(root, 0, 1, measuredItemsColor[0], measuredItemsValue[0], 500, measuredItems[0], 1)
    gauge2 = digitalGauge(root, 5, 1, measuredItemsColor[1], measuredItemsValue[1], 500, "Breite", 1)
    gauge3 = digitalGauge(root, 6, 1, measuredItemsColor[2], measuredItemsValue[2], 500, "Hoehe", 1)
    gauge4 = digitalGauge(root, 7, 1, measuredItemsColor[1], 2000, 6000, measuredItems[1], 1)


    # gauge6 = digitalGauge(root, 5, 2, measuredItemsColor[1], 2000, 6000, measuredItems[1], 1)
    # gauge7 = digitalGauge(root,6, 2, measuredItemsColor[2], 2000, 6000, measuredItems[2], 1)
    # gauge8 = digitalGauge(root, 7, 2, measuredItemsColor[1], 2000, 6000, measuredItems[1],1)


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")
        xval = 20
        yval = 10
        self.coord = xval + 5, yval + 5, (xval + 100) * gaugeScale, (yval + 100) * gaugeScale

        self.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 / self.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))))


class buttonSet:
    def __init__(self, window):
        # left
        Button(window, text="MAIN", wraplength=1, command=lambda: do_loadActiveWindow("Main", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=0, row=0, sticky="WENS", rowspan=5)
        Button(window, text="ENG", wraplength=1, command=lambda: do_loadActiveWindow("ENG", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=0, row=5, sticky="WENS")
        Button(window, text="NAV", wraplength=1, command=lambda: do_loadActiveWindow("NAV", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=0, row=6, sticky="WENS")
        Button(window, text="STAT", wraplength=1, command=lambda: do_loadActiveWindow("STAT", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=0, row=7, sticky="WENS")
        # right
        Button(window, text="PERF", wraplength=1, command=lambda: do_loadActiveWindow("PERFORMANCE", 0),
               font=(textFont, 9), bg="light slate grey").grid(column=3, row=0, sticky="WENS", rowspan=5)
        Button(window, text="MEDIA", wraplength=1, command=lambda: do_loadActiveWindow("MEDIA", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=3, row=5, sticky="WENS")
        Button(window, text="NET", wraplength=1, command=lambda: do_loadActiveWindow("NETWORK", 0), font=(textFont, 9),
               bg="light slate grey").grid(column=3, row=6, sticky="WENS")
        Button(window, text="SETTINGS", wraplength=1, command=lambda: do_loadActiveWindow("BOOT", 0),
               font=(textFont, 9), bg="light slate grey").grid(column=3, row=7, sticky="WENS")


def errorWindow(parent):
    itemfound = 0
    for i in range(0, len(errorBoxItemsColor)):
        if (errorBoxItemsColor[i] != "green"):
            itemfound += 1
            Label(parent, text=errorBoxItems[i], bg="black", fg=errorBoxItemsColor[i], font=(textFont, 13)).grid(
                column=2, row=i, sticky="S")


Buttons = buttonSet(root)  # drawing a Set of buttons

activeWindow = 'BOOT'


def do_loadActiveWindow(selection, updateonly):
    global activeWindow
    if updateonly == 1:  # If updateonly Ignore the selection variable
        selection = activeWindow
    # if selection != activeWindow:
    activeWindow = selection
    screen.destroy()
    create_canvas(root)
    if selection == 'Main':
        mainWindow(screen)
    if selection == 'BOOT':
        bootScreen(screen)


create_canvas(root)
setupGrid(root)
do_loadActiveWindow('Main', 0)


def updateScreen():
    do_loadActiveWindow("", 1)
    measuredItemsValue[1] = root.winfo_width()  # for debugging Purpose
    measuredItemsValue[2] = root.winfo_height()  # for debugging Purpose
    root.after(500, updateScreen)

updateScreen()
root.mainloop()

Wie ihr seht lasse ich den Bildschirm alle 500ms aktualisieren. Mein größtes Problem momentan ist jedoch, dass das Frame auf unterschiedlichen System unterschiedlich groß wird.

Die Breite wird durch Gauge 2 angezeigt, höhe durch Gauge 3. Unter Windows hat das Frame eine größe von 320*480 (wie ihr seht musste ich dazu auch oben die Frame größe um 4 Pixel anpassen).

Starte ich jedoch das selbe Script unter Linux (raspberry), habe ich eine Größe von 354x484.

Gibt es keine Möglichkeit dem Grid eine feste Größe zuzuteilen?

Grüße
BlackJack

@tsaG: Genau aus dem Grund mit den verschiedenen Systemen die verschiedene Einstellungen, Abstände usw. haben will man ja eigentlich *keine* festen Grössen verwenden sondern das sich die GUI den tatsächlichen Grössen die *benötigt* werden anpasst.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@tsaG: grid richtet sich nach Anzahl von Buchstaben und Buchstabenbreiten und Fonts. Und wenn die Fonts auf den verschiedenen Systemen etwas anders ausfallen, dann hast unterschiedliche Größe des Frames in Pixel.
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

Danke, werde das Design dann auf nur ein System auslegen. Oder den Font festlegen, mal schauen.

Habe das ganze nun jetzt mal ausprobiert und die refresh Zeit auf 50 gesetzt, aber scheinbar habe ich irgendwie ein Leistungsleck.... Das Programm startet zwar, läuft los aber immer langsamer. Wenn die Gauge1 bei 300 angekommen ist, updated sich der screen nur noch mit knapp 1 Sekunde. Auch die Programmgröße im Ram ist auf knappe 40mb angestiegen, zum schliessen braucht da Programm knappe 5 Sekunden. Getestet mit meinem Arbeitsrechner (Mac mini mit einem i5). Später soll das Programm dauerhaft auf einem Raspberry laufen, da sollte man das Loch schon gefixt haben :D

Meine erste Vermutung war das ich Gauges erstelle, diese aber nicht lösche. Habe dann aus der Gauge Klasse wieder eine Funktion gemacht welche einfach auf den Canvas C malt und beim Update den Canvas C mit destroyed, jedoch änderte sich am verhalten nichts.
Eigentlich würde ich die Klassen auch gerne behalten.. wie löscht man diese Elemente denn sonst wieder?

Woran kann es liegen?

EDIT: Gerade noch einmal etwas ausprobiert. Ich lade mainWindow gar nicht, nur bootScreen. Dieser hat jedoch nur den Inhalt "pass", trotzdem steigt der benötigte Speicher an. Kommentiere ich meine updateScreen Funktion aus, lade also nur die Knöpfe an der Seite, bleibt es bei 13mb und steigt nicht an :K

EDIT2: So, lade ich diesen Code in meiner do_loadActiveWindow Funktion, fängt es an mit dem Memoryleak. Er zerstört doch das vorher erstellt canvas und erstellt es dann neu, eigentlich sollte es dann doch nicht mehr da sein, oder doch?

Code: Alles auswählen

    activeWindow = selection
    screen.destroy()
    create_canvas(root)
BlackJack

@tsaG: Das ist irgendwie die falsche Schlussfolgerung daraus. Man sollte ja eben nicht alles an einem System auslegen sondern diese ganzen absoluten Grössen soweit wie möglich herauslassen und es so programmieren dass das GUI-Toolkit sich nach den Grössen auf dem jeweiligen System richtet. Schriftart festlegen reicht nicht da die gleiche Schriftart in der gleichen Grösse trotzdem leicht unterschiedliche Pixelbreiten/-höhen für einzelne Zeichen zur Folge haben kann, da die ja nicht mehr als Bitmaps gespeichert sind, sondern als Stützpunkte für Kurven und Linien und die tatsächliche Ausprägung als Bitmap von den Algorithmen und Randbedingungen des Systems abhängt welches diese Daten am Ende in Pixel für den Bildschirm umrechnet.

Das Programm selbst ist mir jetzt viel zu unübersichtlich was da mit welcher globalen Variable zusammenhängt — deswegen macht man das ja auch nicht so weil es extrem anstrengend ist sich da so durchzukämpfen.

Der Ansatz ein komplettes GUI-Fenster alle 50 Millisekunden komplett neu aufbauen zu wollen ist schon grundlegend falsch. So eine Messinstrumentenanzeige gehört in eine eigene Klasse mit einer Methode um den Wert neu zu setzen, woraufhin dann der Text mit dem Wert und der Zeiger *geändert* wird. Also auch da *nicht* neu zeichnen sondern sich die IDs merken und dann die Werte entsprechend ändern. Den `Canvas` ist keine Pixelgrafik sondern Vektorgrafik — man kann alle Zeichenprimitiven die man da auf die Leinwand gebracht hat nachträglich verändern in Position und auch Farbe und den anderen Eigenschaften die man auch beim ersten zeichnen angeben kann. Man kann Elementen auch Tags verpassen und danach alle Elemente mit dem entsprechenden Tag auf einmal verändern um Beispielsweise die Farbe von Teilen der Grafik auf einen Schlag zu verändern ohne das man alle Einzelteile separat umfärben muss.

Was das Speicherleck angeht: Irgendwo wirst Du wahrscheinlich Widgets einem Elternwidget zuordnen das nicht zerstört wird, womit die zwar nicht mehr angezeigt, aber halt immer noch referenziert werden. Das lässt sich auch einfacher überblicken wenn man selbst Klassen verwendet und der eigene Objektgraph dann dem Baum der Widget-Hierarchie nahe kommt. Wenn man Beispielweise eine Gauge-Klasse hat und die von `Frame` oder `Canvas` ableitet, dann würde man dort drin nur Widgets erzeugen die unterhalb des eigenen Exemplars in die Widget-Hierarchie eingefügt werden, womit sichergestellt ist das wenn das `Gauge`-Exemplar zerstört wird, auch der komplette Inhalt zerstört wird und man nicht noch ausserhalb irgendwas entfernen muss.

Edit: Du erzeugst mit jedem Erstellen von einem `digitalGauge`-Objekt ein `Canvas` als Kindelement von `root` und die werden nirgends wieder zerstört, also sammeln die sich halt an.

Die ”Klasse” `digitalGauge` ist in der Form auch nicht wirklich sinnvoll weil die nur aus der `__init__()` besteht und Du den Wert nach dem Erstellen auch gar nicht verwendest, das also eigentlich eine etwas umständlich ausgedrückte Funktion ist.
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: 18260
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