Tkinter Display clearen

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

Hallo!

Ich möchte mir eine GUI bauen, diese hat mehrere Oberfläche durch die man durch schalten kann.

Nun habe ich das mit dem wechseln von Oberflächen nicht so ganz verstanden, vielleicht ist mein Ansatz auch falsch.

Ich habe eine Oberfläche, diese möchte ich löschen und daraufhin dann die neue laden. Ist das richtig so?

Hier mein Code

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter
from Tkinter import *

textFont = "Helvetica"
top = Tkinter.Tk()

activeWindow = "BOOT"
C = Tkinter.Canvas(top, bg="black", height=480, width=320)

# overview.overrideredirect(1)
# overview.focus_set()
# top.config(cursor="none")

# Declare Variables
measuredItems = ["RPM", "Water", "EGT", "Fuel Cap.", "Fuel Flow", "Oil Temp", ]
measuredItemsColor = ["green", "green", "green", "green", "green", "green"]


def drawDigitalGaugeTK(xval, yval, color, value, maxVal, name, gaugeScale):
    coord = xval + 5, yval + 5, (xval + 100) * gaugeScale, (yval + 100) * gaugeScale
    gaugeValue = maxVal / float(value)
    gauge = C.create_arc(xval, yval, (xval + 100 * gaugeScale), (yval + 100 * gaugeScale), start=0,
                         extent=-(220 / gaugeValue), fill=color)  # Draw hand
    outline = C.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

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

    value = Label(top, text=value, bg="black", fg="turquoise", font=(textFont, int(round(14 * gaugeScale)))).place(
        x=xval + 52 * gaugeScale, y=yval + 20 * gaugeScale)  # Write value into the box

    value = Label(top, text=name, bg="black", fg="white", font=(textFont, int(round(20 * gaugeScale)))).place(x=xval,
                                                                                                              y=yval - 15)  # Write value into the box


def drawBandGraph():
    pass


def drawButtons():
    # left
    Button(top, text="MAIN", wraplength=1, command=lambda: setActiveWindow("Main"), pady=6, font=(textFont, 10)).place(
        x=0, y=0)
    Button(top, text="ENG", wraplength=1, command=lambda: setActiveWindow("ENG"), pady=6, font=(textFont, 10)).place(
        x=0, y=120)
    Button(top, text="NAV", wraplength=1, command=lambda: setActiveWindow("NAV"), pady=6, font=(textFont, 10)).place(
        x=0, y=240)
    Button(top, text="STAT", wraplength=1, command=lambda: setActiveWindow("STAT"), pady=6, font=(textFont, 10)).place(
        x=0, y=360)
    # right
    Button(top, text="PERF.", wraplength=1, command=lambda: setActiveWindow("PERFORMANCE"), pady=6,
           font=(textFont, 10)).place(x=290, y=0)
    Button(top, text="MEDIA", wraplength=1, command=lambda: setActiveWindow("MEDIA"), pady=6,
           font=(textFont, 10)).place(x=290, y=120)
    Button(top, text="NET", wraplength=1, command=lambda: setActiveWindow("NETWORK"), pady=6,
           font=(textFont, 10)).place(x=290, y=240)
    Button(top, text="SETTINGS", wraplength=1, command=lambda: setActiveWindow("BOOT"), pady=6,
           font=(textFont, 10)).place(x=290, y=360)


def errorWindow():  # width 120px ; height 200px
    xval = 190  # Xval the Error Box is drawn
    yval = 0  # Yval the Error Box is drawn
    itemlength = len(measuredItemsColor)
    itemfound = 0
    for i in range(0, itemlength):
        if (measuredItemsColor[i] == "yellow" or measuredItemsColor[i] == "red"):
            itemfound += 1
            value = Label(top, text=measuredItems[i], bg="black", fg=measuredItemsColor[i], font=(textFont, 20)).place(
                x=xval + 3, y=yval + 3 + (25 * (itemfound - 1)))  # Write value into the box


def bootScreen():
    drawDigitalGaugeTK(70, 50, measuredItemsColor[0], 2000, 6000, measuredItems[0], 0.8)


def mainWindow():
    drawDigitalGaugeTK(50, 20, measuredItemsColor[0], 2000, 6000, measuredItems[0], 0.8)
    drawDigitalGaugeTK(50, 120, measuredItemsColor[1], 2000, 6000, measuredItems[1], 0.8)
    drawDigitalGaugeTK(50, 220, measuredItemsColor[2], 2000, 6000, measuredItems[2], 0.8)


def setActiveWindow(windowName):
    global oldWindow
    oldWindow = ""
    global activeWindow
    activeWindow = windowName


def loadActiveWindow():
    if (activeWindow != oldWindow):
        C.delete("all")
        if (activeWindow == "MAIN"):
            mainWindow()

        if (activeWindow == "BOOT"):
            bootScreen()

        activeWindowOld = activeWindow


errorWindow()
C.pack()
# top.mainloop()
setActiveWindow("MAIN")
drawButtons()  # Draws Main Buttons
while True:
    loadActiveWindow()
    top.update()
    #       time.sleep(0.2)
loadActiveWindow() lädt dabei das Aktive Menü und versucht dabei vorher, falls ein anderes Fenster geladen wurde, die Oberfläche aufzuräumen.

Leider löscht es nur basics wie Arcs oder Rechtecke auf dem Canvas. Dinge wie Labels oder Butons bleiben jedoch erhalten.

Wie ist hier die richtige vorgehensweise?
BlackJack

@tsaG: Wenn man die Elemente auf einem `Canvas` löscht werden erstaunlicherweise auch tatsächlich nur die Elemente gelöscht die man auch tatsächlich auf diesem `Canvas`-Exemplar erzeugt hat. Also musst Du die anderen Widgets entweder auch auf dem `Canvas`-Exemplar erstellen oder Dir die Objekte merken und per `destroy()`-Aufruf zerstören, oder alles in ein `Frame`-Objekt stecken und *das* mit so einem Aufruf zerstören.

Auf jeden Fall sollte man das ohne ``global`` und dafür mit Klassen machen wenn es sauber und nachvollziehbar werden soll.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

tsaG hat geschrieben:loadActiveWindow() lädt dabei das Aktive Menü und versucht dabei vorher, falls ein anderes Fenster geladen wurde, die Oberfläche aufzuräumen.

Leider löscht es nur basics wie Arcs oder Rechtecke auf dem Canvas. Dinge wie Labels oder Butons bleiben jedoch erhalten.

Wie ist hier die richtige vorgehensweise?
Das ist doch ganz einfach. Lösch eben den Canvas. Wenn Du C.destroy() machst, ist er weg. Allerdings statt pack solltest Du dann ein place Layout machen, sonst schnurrt Dein Fenster zusammen. Oder Du setzt die geometry von Deinem main window, dann bleibt es auch in seiner Größe erhalten.

Ach so, die Buttons möchtest Du auch weg haben, Dann setz die in den Canvas, also mit dem Canvas als parent.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@tsaG: Wenn man die Elemente auf einem `Canvas` löscht werden erstaunlicherweise auch tatsächlich nur die Elemente gelöscht die man auch tatsächlich auf diesem `Canvas`-Exemplar erzeugt hat.
Das war nicht erstaunlicherweise. Das war ein Canvas mit pack layout und die Buttons waren im Mainwindow mit einem place layout. Konnten also gar nicht dadurch gelöscht werden. Also, wie gesagt, Buttons in den Canvas und dann den Canvas löschen. Und danach wieder neu anlegen.
BlackJack

@Alfons Mittelmeyer: Es ist auch nicht erstaunlich das Du keinen funktionierenden Ironiedetektor hast… :roll:
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

Hi,

vielen Dank für die Antworten, leider funktioniert es bei mir nich so ganz (oder ich mache etwas falsch).

Hier

Code: Alles auswählen

value1 = Label(top, text=value, bg="black", fg="turquoise", font=(textFont, int(round(14 * gaugeScale)))).place(
        x=xval + 52 * gaugeScale, y=yval + 20 * gaugeScale)  # Write value into the box
Habe ich nun anstatt top nur C verwendet um nachher mit C.delete("all") alles zu löschen. Jedoch bleiben die Labels und Buttons trotzdem stehen. :-/
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

tsaG hat geschrieben:Habe ich nun anstatt top nur C verwendet um nachher mit C.delete("all") alles zu löschen. Jedoch bleiben die Labels und Buttons trotzdem stehen. :-/
Ich hatte geschrieben:

Code: Alles auswählen

C.destroy()
Und vergiss danach das neu anlegen nicht.
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

Hi,

Danke für den Tip. Jedoch weiss ich nicht wie ich das ganze realisieren soll. Ich definiere ganz am Anfang C.

Wenn ich dann in einer Funktion C lösche und dann neu definiere bekomme ich den Fehler

UnboundLocalError: local variable 'C' referenced before assignment

packe ich das C.destroy() in eine andere Funktion, kann ich es zwar löschen, da C jedoch lokal ist, kann ich es nicht neu erstellen (ist lokal)...

Hast Du vielleicht ein Beispiel für mich? Bis jetzt mag ich noch das .delete("all") lieber, da es mir nicht mein Canvas löscht.

So sieht mein aktuelles Programm aus, jedoch funktioniert es nicht...

Gibt es auch eine Funktion mit ala Label.destroy() ?

nutze ich self.value1.destroy() in der digitalGauge Klasse mit clearMain() bekomme ich folgenden Fehler:

Code: Alles auswählen

AttributeError: 'NoneType' object has no attribute 'destroy'

Code: Alles auswählen

#!/usr/bin/env python

import Tkinter
from Tkinter import *

textFont = "Helvetica"

activeWindow = "BOOT"
top = Tkinter.Tk()
C = Tkinter.Canvas(top, bg="black", height=480, width=320)
#A = Tkinter.Canvas(top, bg="black", height=480, width=320)


# overview.overrideredirect(1)
# overview.focus_set()
# top.config(cursor="none")

# Declare Variables
measuredItems = ["RPM", "Water", "EGT", "Fuel Cap.", "Fuel Flow", "Oil Temp", ]
measuredItemsColor = ["green", "green", "green", "green", "green", "green"]

class digitalGauge:
    def __init__(self, window, xval, yval, color, value, maxVal, name, gaugeScale):
        self.coord = xval + 5, yval + 5, (xval + 100) * gaugeScale, (yval + 100) * gaugeScale
        self.gaugeValue = maxVal / float(value)
        self.gauge = window.create_arc(xval, yval, (xval + 100 * gaugeScale), (yval + 100 * gaugeScale), start=0,
                             extent=-(220 / self.gaugeValue), fill=color)  # Draw hand
        self.outline = window.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 = window.create_rectangle((xval + 50 * gaugeScale), yval + 20 * gaugeScale, xval + 100 * gaugeScale + 3,
                                      yval + 50 * gaugeScale, outline='white', width=2)  # draw Value Box
    
        self.value1 = Label(window, text=value, bg="black", fg="turquoise", font=(textFont, int(round(14 * gaugeScale)))).place(
            x=xval + 52 * gaugeScale, y=yval + 20 * gaugeScale)  # Write value into the box
    
        self.value2 = Label(window, text=name, bg="black", fg="white", font=(textFont, int(round(20 * gaugeScale)))).place(x=xval,
                                                                                                              y=yval - 15)  # Write value into the box 
    def delete(self):
        self.value1.destroy()
        self.value2.destroy()
        #self.window.delete("all")
    
def drawButtons():
    # left
    Button(C, text="MAIN", wraplength=1, command=lambda: setActiveWindow("Main"), pady=6, font=(textFont, 10), bg="light slate grey").place(
        x=0, y=0)
    Button(C, text="ENG", wraplength=1, command=lambda: setActiveWindow("ENG"), pady=6, font=(textFont, 10), bg="light slate grey").place(
        x=0, y=120)
    Button(C, text="NAV", wraplength=1, command=lambda: setActiveWindow("NAV"), pady=6, font=(textFont, 10), bg="light slate grey").place(
        x=0, y=240)
    Button(C, text="STAT", wraplength=1, command=lambda: setActiveWindow("STAT"), pady=6, font=(textFont, 10), bg="light slate grey").place(
        x=0, y=360)
    # right
    Button(C, text="PERF.", wraplength=1, command=lambda: setActiveWindow("PERFORMANCE"), pady=6,
           font=(textFont, 10), bg="light slate grey").place(x=290, y=0)
    Button(C, text="MEDIA", wraplength=1, command=lambda: setActiveWindow("MEDIA"), pady=6,
           font=(textFont, 10), bg="light slate grey").place(x=290, y=120)
    Button(C, text="NET", wraplength=1, command=lambda: setActiveWindow("NETWORK"), pady=6,
           font=(textFont, 10), bg="light slate grey").place(x=290, y=240)
    Button(C, text="SETTINGS", wraplength=1, command=lambda: setActiveWindow("BOOT"), pady=6,
           font=(textFont, 10), bg="light slate grey").place(x=290, y=360)


def errorWindow():  # width 120px ; height 200px
    xval = 190  # Xval the Error Box is drawn
    yval = 0  # Yval the Error Box is drawn
    itemlength = len(measuredItemsColor)
    itemfound = 0
    for i in range(0, itemlength):
        if (measuredItemsColor[i] == "yellow" or measuredItemsColor[i] == "red"):
            itemfound += 1
            value = Label(top, text=measuredItems[i], bg="black", fg=measuredItemsColor[i], font=(textFont, 20)).place(
                x=xval + 3, y=yval + 3 + (25 * (itemfound - 1)))  # Write value into the box


def bootScreen():
    test = digitalGauge(C, 70, 50, measuredItemsColor[0], 2000, 6000, measuredItems[0], 0.8)

def mainWindow():
    global rpm
    rpm = digitalGauge(C, 50, 20, measuredItemsColor[0], 2000, 6000, measuredItems[0], 0.8)
    global water
    water = digitalGauge(C, 50, 120, measuredItemsColor[1], 2000, 6000, measuredItems[1], 0.8)
    global egt
    egt = digitalGauge(C, 50, 220, measuredItemsColor[2], 2000, 6000, measuredItems[2], 0.8)

def clearMain():
    rpm.delete()
    water.delete()
    egt.delete()

def setActiveWindow(windowName):
    global oldWindow
    oldWindow = ""
    global activeWindow
    activeWindow = windowName

def loadActiveWindow():
    if (activeWindow != oldWindow):
        if (activeWindow == "MAIN"):
            mainWindow()
        if (activeWindow == "BOOT"):
            clearMain() # Wechsel auf den bootScreen, löschen des mainWindows
            bootScreen()
        activeWindowOld = activeWindow

errorWindow()
C.pack()
# top.mainloop()
setActiveWindow("MAIN")
drawButtons()  # Draws Main Buttons
while True:
    loadActiveWindow()
    top.update()
BlackJack

@tsaG: Wie gesagt: Erstelle die Widgets auf dem `Canvas` wie Du auch die anderen Grafikobjekte erstellst. Da gibt's eine `create*()`-Methode mit der man auch beliebige Wigets auf dem `Canvas` platzieren kann und die dann auch dem Tag 'all' zugeordnet werden und damit gelöscht werden können.

Und dann werd dringend ``global`` und den ganzen Kram auf Modulebene los der da als Programm abläuft. Auf Modulebene gehören nur Definitionen von Konstanten, Funktionen, und Klassen. Sonst wird das ganze schnell unübersichtlich, schwer nachzuvollziehen und unwart- und testbar.

Sternchenimporte sind auch keine gute Idee. Damit holst Du Dir im Falle von `Tkinter` ca. 190 Namen ins Modul von denen Du nur einen ganz kleinen Bruchteil verwendest. Das wird auch schnell unübersichtlich und man kann sich auch leicht Namenskollisionen einhandeln.

`C` ist ein schlechter Name und entspricht in der Schreibweise auch nicht dem Style Guide for Python Code, wie auch viele der anderen Namen nicht.

Kommentare wie ``# Declare variables`` sind überflüssig wenn man deutlich sieht was im Code danach gemacht wird. Wobei das hier auch noch inhaltlich nicht korrekt ist weil in Python keine Variablen deklariert werden. So wie das dann verwendet wird sollte es auch *eine* Liste mit einem eigenen Datentyp sein weil jeweils ein Wert aus der einen Liste zu einem Wert aus der anderen gehört. Das führt dann zu komplexerem Code als eigentlich nötig ist, und es besteht die Gefahr das man beim Ändern oder Erweitern Fehler macht und die Listen nicht mehr ”synchron” sind. Der Code der die beiden Listen zusammenführt ist „unpythonisch“ weil da eine Schleife über eine unnötige Index-Laufvariable gemacht wird, statt die Listen mit `zip()` oder `itertools.izip()` und ohne unnötigen Index zusammenzuführen.

Die Klammern um ``if``-Bedingungen gehören da nicht hin.

Wenn man `itemfound` *nach* dem Zeichnen erhöht, braucht man beim zeichnen nicht 1 von dem Wert abziehen. Wobei man da auch eher eine Schleife über die gefilterten Werte machen würde und die dann mit `enumerate()` nummerieren würde, damit man das nicht alles manuell machen muss.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@tsaG: Hab es mir mal angeschaut und damit ich mehr Überblick habe, ein wenig umgeordnet, das siehst Du hier. Weiss aber nicht, warum es bei Dir nicht geht.

Das mit dem error_window musst Du aber noch anders lösen. Damit es sichtbar ist, muss man zuerst den Canvas machen und dann das Error window. Wenn man aber dann den Canvas löscht und wieder neu anlegt, überdeckt der das Error Window. Wenn man es dann nochmals neu anlegt, liegt zwar eines drüber, aber auch noch eines drunter. Und das wird dann jedesmal mehr. Solltest also bevor Du ein Neues machst auch das alte löschen. Oder am Besten legst Du es auch in den Canvas. Dann löscht Du es mit dem Cancas mit und brauchst die Labels nicht einzeln zu löschen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi tsaG

Wie heisst dein Projekt? (Name für den Titel des Hauptfensters)

Gruss wuf :wink:
Take it easy Mates!
tsaG
User
Beiträge: 14
Registriert: Samstag 22. August 2015, 00:20

Wow, sehr schön! Vielen Dank dass Du es auch etwas strukturiert hast. Meine Programmierkenntnisse beschränkten sich bisher etwas auf C, schön zu sehen wie man es hier in Python "richtig" macht.

@wuf, bisher hat mein Programm noch keinen Namen :D Es wird später auf einem Embedded Rechner als Vollbild laufen, daher gibt es keine Titelleiste (steige gerade von PyGame um).


Grüße
BlackJack

@tsaG: So macht man es in Python nicht ”richtig”, das ist eher ”falsch”. Da wurden mehrere Ebenen von Funktionen verschachtelt statt, wie es in Python eigentlich gemacht würde, mit Klassen zu arbeiten. Alfons' Quelltext solltest Du *nicht* als Massstab für idiomatisches Python nehmen. Er hat da deutlich abweichende Vorstellungen davon wie ein GUI-Programm strukturiert und aufgebaut werden sollte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sollte auch keine Musterlösung werden, sondern sich an der Lösung von tsaG orientieren. Nur Endlosschleife mit root.update und immer noch irgendwelche Buttons löschen, obwohl man den Canvas löscht, das ist ein bisschen zuviel Durcheinander. Besser man startet mit etwas das funktioniert und baut es dann aus. Und wenn man dann will, kann man es immer noch stilistisch verschönern.

Mit einem Programm starten, das nicht läuft und dann immer noch mehr dazumachen, das wird einfach nichts. Also tsaG, eine Musterlösung war das nicht. Bei einer Musterlösung kann ja BlackJack Dir helfen. Mir war nur wichtig, dass Du ein wenig mehr Überblick über Dein Programm bekommst und dass es läuft.

Und in Deinem Falle ist es ja auch nicht so, dass Du eine Klasse, wie BlackJack schreibst, brauchen würdest. Mit einer Klasse macht man nämlich etwas, während der Canvas da ist. Und hier gibt es keine Funktionalität. Hier macht man den Canvas nur kapputt. Aber da mit einer Klasse arbeiten, würde ich in diesem Forum vermeiden, denn die Methode __del__() verwenden, wird hier schon nicht gern gesehen, Und Probleme bekommst Du dann mit BlackJack und andern hier, wenn Du zum Löschen der augenblicklichen Instanz auch noch del nimmst.

Aber mit einer Klasse wäre es doch übersichtlicher. Eigentlich brauchst Du nur __init__ und __del__. Aber wenn Du da die anderen Funktionen als Methoden aufnimmst, die von __init__ und untereinander aufgerufen werden, wird es nicht verschachtelt.

@tsaG: und vergiss nicht, das mit dem Error Window richtig zu machen.
Zuletzt geändert von Alfons Mittelmeyer am Montag 7. September 2015, 11:15, insgesamt 1-mal geändert.
BlackJack

@Alfons Mittelmeyer: Ich wüsste nicht wo man hier `__del__()` implementieren müsste, ausser man macht beim OOP-Entwurf komische Sachen die man in Python so nicht machen würde.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Ich wüsste nicht wo man hier `__del__()` implementieren müsste, ausser man macht beim OOP-Entwurf komische Sachen die man in Python so nicht machen würde.
Also wenn man eine eigene Klasse MyScreen von tk.Canvas ableitet, braucht man __del__() nicht. Denn da kann man nach wie vor destroy() nehmen. Wenn man das aber mit der eigenen Klasse genauso macht, wie mit der augenblicklichen Funktion create_canvas und das etwa MyScreen nennt, dann hat man:

Code: Alles auswählen

# statt ========================
activeWindow ='Main'
create_canvas(root,activeWindow)

def do_loadActiveWindow(windowtype):
    global activeWindow
    if windowtype != activeWindow:
        activeWindow = windowtype
        screen.destroy()
        create_canvas(root,windowtype)

# hat man dann ==============
activeWindow ='Main'
my_screen = MyScreen(root,activeWindow)

def do_loadActiveWindow(windowtype):
    global activeWindow
    global my_screen
    if windowtype != activeWindow:
        activeWindow = windowtype
        my_screen = MyScreen(root,activeWindow)
Jedesmal - außer beim ersten Mal - muss bei "my_screen = MyScreen(root,activeWindow)" der Canvas destroyed werden. Und das macht man normalerweise in __del__()

Ach so, fällt mir ein was man noch verbessern könnte:

- bei der Funktion create_canvas C als returnwert machen statt jetzt in die globale Variable 'screen' zu schreiben, dann wäre es so:

Code: Alles auswählen

activeWindow ='Main'
my_screen = create_canvas(root,activeWindow)

def do_loadActiveWindow(windowtype):
    global activeWindow
    global my_screen
    if windowtype != activeWindow:
        activeWindow = windowtype
        my_screen.destroy()
        my_screen = create_canvas(root,activeWindow)
@tsaG, ich denke, dass das mit der Funktion create_canvas für Dich besser ist, als Dich mit __init_,_ mit __del__ und mit der Diskussion rumzuschlagen, wann __del__() aufgerufen wird.

Wenn Du allerdings von außen auf etwas im Canvas zugreifen willt, etwa ein Update des Error Widows, dann musst Du doch eine Klasse machen, und das create_canvas in die __init__() schreiben, außerdem auch Dein Error Window und dann noch eine update Methode für das Errorwindow definieren. Aber dort bitte keine neuen Label anlegen, sondern nur Inhalte aktualisieren, und bei __del__() destroy auf den Canvas
Zuletzt geändert von Alfons Mittelmeyer am Montag 7. September 2015, 12:34, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: sauber programmiert braucht man da weder __del__ noch global. Wenn man mit einem "funktionierenden" Programm startet, das aber nicht sauber programmiert ist, dann kann man zwar immer noch was dazu bauen, das wird aber nichts. Am besten fängt man mit einer einfachen Klasse an, ohne global, ohne Sternchenimporten und ohne ausführbaren Code auf Modulebene.
BlackJack

@Alfons Mittelmeyer: Man macht normalerweise gar nichts mit `__del__()` und Du hast ja selber schon geschrieben wie man das ganz einfach vermeiden kann, in dem man es eben nicht mehr so macht wie es bisher da steht, sondern objektorientiert, was ja genau mein Vorschlag war und was in Python (und anderen objektorientierten Programmiersprachen) halt der übliche Weg ist GUI-Code zu schreiben. Wenn man anfängt gegen den Strom zu schwimmen wird's halt schwieriger und man ertrinkt in Problemen die man sich selber geschaffen hat.

Selbst wenn man nicht von `Canvas` ableitet, wäre das `Canvas`-Exemplar ja in der Klasse gekapselt und man würde eine Methode zum zerstören/löschen bereit stellen statt sich mit `__del__()` beschäftigen zu müssen.

Wenn man die einzelnen auswechselbaren ”Fenster” als Objekte modelliert braucht man ausserdem die Indirektion über die Zeichenketten nicht mehr und kann als `active_window` direkt das jeweilige Objekt verwenden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3 Ekläre das bitte nicht mir, sondern tsaG. Ich erkläre ihm nur, was er unbedingt braucht. Was stylistisch am Besten ist, kannst ja Du ihm ja erklären. Vielleicht kommt er aber auch selber mit der Zeit drauf, wenn er sich weiter damit beschäftigt - und erkennt, wofür etwas gut ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack, natürlich hast Du, dass man so eine destroy-Medothe implementiert. Deswegen soll man aber auch __del__() implementieren für den Fall, dass man vergisst destroy auftzurufen, also:

Code: Alles auswählen

def __init__(self):
    self.isDestroyed = False

def destroy(self):
    self.isDestroyed = True

def __del__(self):
    if not self.isDestroyed: self.destroy()
Antworten