Ich habe ein Software-Architektur-Problem.
Ich möchte mit tkinter ein Fenster öffnen und darauf den Fortschritt meiner Berechnungen anzeigen, kein einfacher Fortschrittsbalken, sondern ein informatives Canvas. Das Fenster wäre also nicht besonders interaktiv. Wenn ich aber mit mainloop() die Kontrolle ans Fenster übergebe, kriege ich sie nie wieder zurück. Das Fenster soll ja weiterbestehen, während ich meine Rechnungen ausführe (wie jeder normale Fortschrittsbalken auch), ich möchte nur hin und wieder die Berechnungen unterbrechen und aufs Fenster zugreifen können, um es zu verändern. Ich finde bei tkinter kein Befehl "zeichne das Fenster und übergebe ans Hauptprogramm".
Ich könnte (rein theoretischerweise) die Kontrolle einmal und entgültig an tkinter übergeben, und mein Number-Crunching vom Fenster aus ausrufen, aber ich habe ein *sehr* ungutes Gefühl dabei, (1) weil ich die Möglichkeit offenhalten will, verschiedene und je nach Bedarf verschiedenartige Fortschrittsfenster zu öffnen, und (2) weil dann bei Tkinter vieles Unnötige im Hintergrund läuft, schließlich wird am Fenster während meiner (sehr aufwändigen) Berechnungen nichts verändert.
Wie macht man so etwas richtig?
Fortschrittsfenster
Hallo Goswin,
ansich musst du in Tkinter nicht zwingend den Mainloop aufrufen.
Man könnte dessen Funktionalität auch "manuell" mit der update()-Methode erreichen allerdings verarbeitet Tkinter dann entsprechende Events (klicks im Fenster, etc.) auch nur zum Zeitpunkt des Aufrufs von update().
Das funktioniert im ersten Wurf auch mit 2 Rootwindows. Aber ob das so sauber ist...
Ich weiss es nicht mehr genau aber entweder darf es keine 2 Roots oder nur keine 2 mainloops in einer Applikation geben sonst crashts...
Gruß,
dahaze
[wahrscheinlich werde ich wegen diesem Post jetzt gleich von den Admins gesteinigt. ]
ansich musst du in Tkinter nicht zwingend den Mainloop aufrufen.
Man könnte dessen Funktionalität auch "manuell" mit der update()-Methode erreichen allerdings verarbeitet Tkinter dann entsprechende Events (klicks im Fenster, etc.) auch nur zum Zeitpunkt des Aufrufs von update().
Das funktioniert im ersten Wurf auch mit 2 Rootwindows. Aber ob das so sauber ist...
Ich weiss es nicht mehr genau aber entweder darf es keine 2 Roots oder nur keine 2 mainloops in einer Applikation geben sonst crashts...
Code: Alles auswählen
>>> import time
>>> import Tkinter
>>> root = Tkinter.Tk()
>>> label = Tkinter.Label(root, text=0)
>>> label.grid()
>>> root2 = Tkinter.Tk()
>>> label2 = Tkinter.Label(root2, text=0)
>>> label2.grid()
>>> for i in xrange(100):
... label.configure(text=i)
... root.update()
... time.sleep(0.5)
... label2.configure(text=i*2)
... root2.update()
... time.sleep(0.5)
...
>>> root.destroy()
>>> root2.destroy()
dahaze
[wahrscheinlich werde ich wegen diesem Post jetzt gleich von den Admins gesteinigt. ]
- Goswin
- User
- Beiträge: 363
- Registriert: Freitag 8. Dezember 2006, 11:47
- Wohnort: Ulm-Böfingen
- Kontaktdaten:
Prima es funktioniert, vielen Dank!
Vom Namen her ist es natürlich schon irgendwie seltsam, dass man etwas "updaten" kann, das noch nicht existiert; deshalb wäre ich auch nie auf die Idee gekommen, "update()" zu benutzen.
Ich habe übrigens auch zwei Fenster erstellt und bei mir ist nichts abgestürzt. Mein Testprogramm war natürlich extrem einfach, aber bisher läuft alles bestens.
Vom Namen her ist es natürlich schon irgendwie seltsam, dass man etwas "updaten" kann, das noch nicht existiert; deshalb wäre ich auch nie auf die Idee gekommen, "update()" zu benutzen.
Ich habe übrigens auch zwei Fenster erstellt und bei mir ist nichts abgestürzt. Mein Testprogramm war natürlich extrem einfach, aber bisher läuft alles bestens.
@dahaze: Es darf nur ein `Tk`-Objekt geben. Da hängt der Tk-Interpreter dran und dessen interner Zustand und wenn es zwei davon gibt, dann ist Chaos möglich.
@Goswin: Das das läuft ist egal, man darf das auf keinen Fall machen. Das ist wie der Typ der aus dem Hochhausfenster springt und bei jedem Stockwerk meint, bis jetzt ging's gut.
Das mit `update()` kann man machen, man hebelt damit allerdings die normale Vorgehensweise bei GUI-Programmierung aus. Ausserdem steht im `Tkinter Book` von effbot dieser Satz: „This method should be used with care, since it may lead to really nasty race conditions if called from the wrong place (…).”
@Goswin: Das das läuft ist egal, man darf das auf keinen Fall machen. Das ist wie der Typ der aus dem Hochhausfenster springt und bei jedem Stockwerk meint, bis jetzt ging's gut.
Das mit `update()` kann man machen, man hebelt damit allerdings die normale Vorgehensweise bei GUI-Programmierung aus. Ausserdem steht im `Tkinter Book` von effbot dieser Satz: „This method should be used with care, since it may lead to really nasty race conditions if called from the wrong place (…).”
- Goswin
- User
- Beiträge: 363
- Registriert: Freitag 8. Dezember 2006, 11:47
- Wohnort: Ulm-Böfingen
- Kontaktdaten:
Was bei mir läuft, ist sehr ähnlich, aber nicht genau dasselbe wie bei dahaze, nämlich:BlackJack hat geschrieben:Es darf nur ein `Tk`-Objekt geben. Da hängt der Tk-Interpreter dran und dessen interner Zustand und wenn es zwei davon gibt, dann ist Chaos möglich.
Das das läuft ist egal, man darf das auf keinen Fall machen.
Code: Alles auswählen
class Fortschritt(object):
def __init__(self):
self._fenster = t.Tk()
self._cv = t.Canvas(self._fenster,width=400,height=300)
self._cv.pack()
self._fenster.update()
def zeige(self,farbe):
self._cv.config(bg=farbe)
self._fenster.update()
fs1 = Fortschritt()
fs2 = Fortschritt()
sleep(2)
#
print("Programm gestartet")
fs1.zeige(farbe='red')
sleep(2)
#
print("Programm halb durchgelaufen")
fs1.zeige(farbe='green')
fs2.zeige(farbe='cyan')
sleep(2)
#
print("Programm durchgelaufen")
fs2._fenster.destroy()
fs1._fenster.mainloop()
Darf man das auch nicht? Und wenn nicht, was ist die empfohlene Alternative, für das und für update()? Es muss ja irgendeine "normale" Möglichkeit geben, mehrere Fortschrittsbalken zu zeichnen.
Zuletzt geändert von Anonymous am Donnerstag 17. Januar 2013, 15:51, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
Grund: Quelltext in Python-Code-Tags gesetzt.
@Goswin: Das sollte so gehen, allerdings friert zwischen den `update()`-Aufrufen die GUI ein. Das ist zum Beispiel unschön wenn man ein anderes Fenster „davor vorbeischiebt” und dann der Fensterinhalt zerstört wird und nicht neugezeichnet wird bis wieder ein `update()` aufgerufen wird.
Der übliche Weg wäre, dass man den länger laufenden Code in einen Thread auslagert, der dann mittels `Queue.Queue` und der `after()`-Methode auf Widgets mit den Fortschrittsbalken kommuniziert.
Edit: Argh, ich hatte nur die Anzahl der im Quelltext vorkommenden `Tk`-Aufrufe gezählt. Aber der Code davon wird ja zweimal ausgeführt und das darf man natürlich *nicht* machen. Das `Tk`-Objekt ist *das* *Haupt*fenster. Davon darf es nur eines geben. Wenn man mehr will, dann ist `Toplevel` die Klasse dafür. Man kann auch mehrere `Toplevel` erstellen und das Hauptfenster mit der `withdraw()`-Methode „verstecken” wenn man es nicht braucht.
Der übliche Weg wäre, dass man den länger laufenden Code in einen Thread auslagert, der dann mittels `Queue.Queue` und der `after()`-Methode auf Widgets mit den Fortschrittsbalken kommuniziert.
Edit: Argh, ich hatte nur die Anzahl der im Quelltext vorkommenden `Tk`-Aufrufe gezählt. Aber der Code davon wird ja zweimal ausgeführt und das darf man natürlich *nicht* machen. Das `Tk`-Objekt ist *das* *Haupt*fenster. Davon darf es nur eines geben. Wenn man mehr will, dann ist `Toplevel` die Klasse dafür. Man kann auch mehrere `Toplevel` erstellen und das Hauptfenster mit der `withdraw()`-Methode „verstecken” wenn man es nicht braucht.
- Goswin
- User
- Beiträge: 363
- Registriert: Freitag 8. Dezember 2006, 11:47
- Wohnort: Ulm-Böfingen
- Kontaktdaten:
@BlackJack:
Danke. Ich teste jetzt einmal folgenden Konstruktor
Habe ich zu verstehen, dass bei jedem Aufruf des Konstruktors mit "t.Tk().withdraw()" dasselbe Tk immer wieder neu versteckt wird? (Wenn es nur eines davon gibt, dann kann ich mir nichts anderes vorstellen)
Danke. Ich teste jetzt einmal folgenden Konstruktor
Code: Alles auswählen
def __init__(self):
t.Tk().withdraw()
#
self._fenster = t.Toplevel()
self._cv = t.Canvas(self._fenster,width=400,height=300)
self._cv.pack()
self._fenster.update()
Zuletzt geändert von Goswin am Donnerstag 17. Januar 2013, 20:13, insgesamt 2-mal geändert.
Nein, du erstellst mittels ``t.Tk()`` immer wieder ein neues Tk-Objekt und versteckst dieses. Du darfst Tk aber nur einmal in deinem Programm aufrufen. Der Tk-Aufruf hat in der init-Methode nichts zu suchen, den musst du hinter das ``if __name__ == "__main__":`` packen. Wenn du mehrere Fenster brauchst, dann musst du, wie BlackJack es schon geschrieben hat, Toplevel-Objekte erzeugen.
Für Python-Code gibt es übrigens Python-Codeblöcke. Die solltest du verwenden und nicht Zitate, bei denen auch noch die Einrückung verloren geht
Für Python-Code gibt es übrigens Python-Codeblöcke. Die solltest du verwenden und nicht Zitate, bei denen auch noch die Einrückung verloren geht
Das Leben ist wie ein Tennisball.
- Goswin
- User
- Beiträge: 363
- Registriert: Freitag 8. Dezember 2006, 11:47
- Wohnort: Ulm-Böfingen
- Kontaktdaten:
Alles klar, inzwischen berichtigt!EyDu hat geschrieben:Für Python-Code gibt es übrigens Python-Codeblöcke. Die solltest du verwenden und nicht Zitate, bei denen auch noch die Einrückung verloren geht
Zuletzt geändert von Goswin am Donnerstag 17. Januar 2013, 20:15, insgesamt 2-mal geändert.
Schau noch einmal genauer auf die Buttons: Python-CodetagsGoswin hat geschrieben:Tippfehler, bereits berichtigt!EyDu hat geschrieben:Für Python-Code gibt es übrigens Python-Codeblöcke. Die solltest du verwenden und nicht Zitate, bei denen auch noch die Einrückung verloren geht
Das Leben ist wie ein Tennisball.