Tkinter Display clearen

Fragen zu Tkinter.
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()
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Alfons Mittelmeyer hat geschrieben:Deswegen soll man aber auch __del__() implementieren für den Fall, dass man vergisst destroy auftzurufen
Ich bin mir ziemlich sicher, dass man einfach von einer vorhandenen Klasse erbt und sich dann um so einen Schabernack gar nicht kümmern muss ;)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:Ich bin mir ziemlich sicher, dass man einfach von einer vorhandenen Klasse erbt und sich dann um so einen Schabernack gar nicht kümmern muss ;)
Ja so macht man es meistens, von tk.Canvas ableiten, obwohl man gar keine Methoden von tk.Canvas überschreibt und damit gar keine Ableitung bräuchte.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Alfons Mittelmeyer hat geschrieben:Ja so macht man es meistens, von tk.Canvas ableiten, obwohl man gar keine Methoden von tk.Canvas überschreibt und damit gar keine Ableitung bräuchte.
Doch. Man braucht sich keine unter anderem keine __del__-Methoden zusammen zu frickeln und kann sich darauf verlassen, dass alle benötigten Methoden vorhanden sind, und bei Bedarf überschrieben werden können.
Das ist ja das tolle an Vererbung in der OOP :wink:
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:Doch. Man braucht sich keine unter anderem keine __del__-Methoden zusammen zu frickeln und kann sich darauf verlassen, dass alle benötigten Methoden vorhanden sind, und bei Bedarf überschrieben werden können. Das ist ja das tolle an Vererbung in der OOP :wink:
Ja, aber nur solange Du nicht selber etwas dazu machst, was der Aufhebung bedarf - da bitte es nicht vergessen. Bei nur Widgets mit GUI Callbacks brauchst Du in der Regel nichts mit __del__() tun.

Und bei Widgets, ist das __del__() auch nicht wichtig. Wenn Du vergessen hast, sie zu löschen, dann siehst Du schon, dass sie noch da sind. Was hier das Problem ist. Aber es gibt auch Vieles andere, das man nicht sieht, und was dann zum Crash führen kann, mitunter verbunden mit aufwendiger Fehlersuche,
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Am besten fängt man mit einer einfachen Klasse an, ohne global, ohne Sternchenimporten und ohne ausführbaren Code auf Modulebene.
Also, das mit der Klasse habe ich nicht richtig verstanden. Ich fange immer ganz sauber an, und mache alles lokal, etwa:

Code: Alles auswählen

def main(parent):
    tk.Label(parent,text='Hello',command = (lambda: print('Hello'))).pack()

main(root)
Ja man kann das auch mit einer Klasse machen:

Code: Alles auswählen

def main(parent):

    class MyClass:
        
        def __init__(self,parent):
            tk.Label(parent,text='Hello',command = (lambda: print('Hello'))).pack()

    MyClass(parent)

main(root)
Aber wozu man das macht, verstehe ich einfach nicht.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: man macht eine Klasse, damit man globale Variablen wie activeWindow nicht braucht. Die Klassendefinition sollte aus der Funktion main raus.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi tsaG

Kannst du noch verstehen und mitverfolgen was hier in deinem Thread abläuft? Ich nicht. :K

Gruss wuf :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: man macht eine Klasse, damit man globale Variablen wie activeWindow nicht braucht. Die Klassendefinition sollte aus der Funktion main raus.
Dann braucht man also doch keine Klasse, denn mit einer Funktion hat man ja auch keine globalen Variablen. Allerdings ist dann der Zugriff nicht schön, weil man dann eine Liste odef etwas Ähnliches nehmen muss. Wer keine Liste nehmen will, der definiert sich eben eine Klasse. Die könnte aber auch innerhalb der Funktion und nur für die Variablen sein.

@tsaG: ich habe es jetzt mit einer Funktion gemacht und nicht mehr verschachtelt. Vielleicht gefällt Dir das ja besser
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Alfons Mittelmeyer hat geschrieben:@tsaG: ich habe es jetzt mit einer Funktion gemacht und nicht mehr verschachtelt. Vielleicht gefällt Dir das ja besser
Hoffentlich nicht, denn das ist großer Unsinn.
Lieber Alfons, bitte bedenke, dass durchaus unerfahrene Python-Anwender deine Posts lesen. Und wenn man versucht globale Variablen dadurch zu vermeiden, dass man alles eine Ebene einrückt - also alles in main schreibt - dann sind sie ja trotzdem global, nur weil man das ganze einen Ebene verschiebt ändert sich ja das Problem nicht.
Das hilft dem Anwender also gar nicht und zeugt von falschem Verständnis.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@sparrow Wieso ist das dann global? Hast Du schon überlegt, wie schnell die Funktion main vorbei ist?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@Alfons: Weißt du warum man globale Variablen vermeiden soll?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:@Alfons: Weißt du warum man globale Variablen vermeiden soll?
Das brauchst Du mir bestimmt nicht sagen. Aber stimmt, das war schlecht programmiert, denn wir haben eine globale Variable 'root':

Code: Alles auswählen

root = tk.Tk()

def main(parent):
    xxx

main(root)
root.mainloop()
Das können wir natürlich besser machen:

Code: Alles auswählen

def main(parent):
    xxx
    return parent

main(tk.Tk()).mainloop()
Jetzt endlich zufrieden???

Die Funktion main wäre allerdings noch da. Man könnte zwar main mit del löschen, aber dann müssten wir bereits vorher root definieren. Ganz ohne etwas geht es eben leider nicht. Zumindest komme ich nicht drauf.

Man bräuchte da eine Libraryfunktion, welcher man eine Funktion zur Ausführung übergibt und die dabei die Funktion löscht. Sollte aber nicht schwer zu schreiben sein.
BlackJack

@Alfons Mittelmeyer: Du zeigst eine Lösung „ohne verschachtelte Funktionen“ die aus einer Funktion besteht in der lauter Funktionen definiert werden. Womit das verschachtelte Funktionen sind. Dachtest Du das merkt keiner oder merkst Du das selber nicht? ;-)

Und dann bist Du anscheinend nicht mal in der Lage das globale `root` einfach *in* die `main()`-Funktion zu verschieben und fängst schon wieder mit Funktionen löschen wollen an. Dünnes Eis mein Freund…
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Hör jetzt auf mit dem Unsinn. Das ist in diesem Fall dasselbe, wie wenn Du eine Klasse mit verschiedenen Methoden machst. Sind das dann bei Dir auch verschachtelte Methoden? Zumindest trifft das zu für Funktionen zu, die nur für einmaligen Gebrauch bestimmt sind, wie so eine main Funktion. Da fehlt nur das self gegenüber einer Klassendefinition.

Bei der Klasse ist aber etwas besser. Methoden können auch andere Methoden aufrufen, die nachher definiert sind. Bei der Funktion kann nur aufgerufen werden, was vorher definiert wurde - zumindest ist es so in Python
BlackJack

@Alfons Mittelmeyer: Das ist nicht dasselbe. Man hat Funktionen bei denen nicht an der Schnittstelle erkennbar ist das die innen etwas verwenden was eben nicht über die Schnittstelle hinein gekommen ist. Das ist unübersichtlich und untestbar. Also genau der Grund warum das ohne die zusätzliche Ebene in der `main()`-Funktion nicht gut ist. Neue Werte zuweisen kann man dem inneren Zustand so auch nicht, wie man das bei einer Klasse könnte. Und selbst wenn es dasselbe wäre, dann sollte es einen offensichtlichen Weg geben und der ist in Python nun mal in der Regel Klassen statt Closures zu verwenden. Nicht zuletzt weil Python's Closures kein vollwertiger Ersatz für Klassen sind, da sich die Namen von inneren Funktionen nicht ohne weiteres neu binden lassen.
Antworten