Countdown in einem Label
- __blackjack__
- User
- Beiträge: 14084
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Sam123: Du hast da einfach Glück das beide `Canvas`-Objekte die gleichen IDs für die Elemente liefern. Da sollte man ich aber nicht drauf verlassen. Ich würde das über Tags lösen, oder eine eigene Klasse für die Würfeldarstellung wo dann gleiche Attributnamen nicht zwingend gleiche Werte haben müssen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
@__blackjack__: Vielen Dank für deine Tips! Ich würde gern beide Methoden ausprobieren, mit Tags und eigener Klasse. Aber auch nach dem googeln usw. hab ich momentan gar keinen Plan, wie du das hier mit Tags meinst. Könntest du evtl. ein kleines Beispiel geben?
Hast du auch mal probiert das zu googeln? Das ist der erste Treffer: https://tkdocs.com/tutorial/canvas.html Mit einer eigenen Sektion zu tags: https://tkdocs.com/tutorial/canvas.html#tags
Hallo zusammen,
ich hab folgenden einfachen Code, der mir einfach das Würfeln simulieren soll und nach Zufallsprinzip eins von sechs Würfelbildern mit den entsprechenden Augenzahlen in einem Label anzeigen soll. Und das als eine Endlosschleife. Wenn ich auf den Button drücke, wechselt das Bild nur einmal, die Funktionl läuft 988 mal durch, danach kommen dutzend Fehlermeldungen und als letztes wird angezeigt: RecursionError: maximum recursion depth exceeded while calling a Python object. Leider komme ich selber nicht weiter und wäre für Hilfe sehr dankbar.
ich hab folgenden einfachen Code, der mir einfach das Würfeln simulieren soll und nach Zufallsprinzip eins von sechs Würfelbildern mit den entsprechenden Augenzahlen in einem Label anzeigen soll. Und das als eine Endlosschleife. Wenn ich auf den Button drücke, wechselt das Bild nur einmal, die Funktionl läuft 988 mal durch, danach kommen dutzend Fehlermeldungen und als letztes wird angezeigt: RecursionError: maximum recursion depth exceeded while calling a Python object. Leider komme ich selber nicht weiter und wäre für Hilfe sehr dankbar.
Code: Alles auswählen
import tkinter
from functools import partial
import random
IMAGES = [
"wuerfel_ein_auge.gif",
"wuerfel_zwei_augen.gif",
"wuerfel_drei_augen.gif",
"wuerfel_vier_augen.gif",
"wuerfel_fuenf_augen.gif",
"wuerfel_sechs_augen.gif",
]
def next_wuerfel(hauptfenster, label, images):
label["image"] = random.choice(images)
hauptfenster.after(1000, next_wuerfel(hauptfenster, label, images))
def main():
hauptfenster = tkinter.Tk()
images = [tkinter.PhotoImage(file=image) for image in IMAGES]
label_bild = tkinter.Label(hauptfenster, image=random.choice(images))
label_bild.pack()
tkinter.Button(hauptfenster, text = "Next", command = partial(next_wuerfel, hauptfenster, label_bild, images)).pack()
hauptfenster.mainloop()
if __name__ == '__main__':
main()
Du darfst ja auch keine Rekursion starten, sondern next_wuerfel nochmal schedulen. Wie schon im Button-Command einfach mit partial wieder eine Funktion mit all ihren Argumenten erzeugen, die dann ohne Argumente einfach von after aufgerufen wird. Genauso wie der Button.
Das untere Beispiel ist doch absolut gleichwertig, oder etwa nicht. Nur, dass ich dem Label statt Bilder Zahlen zuweise. Und da funtkioniert das doch.
Also irgendwie verstehe ich das nicht. Ich möchte einfach, dass wenn ich auf den Button klicke, mir die Bilder nach dem Zufallsprinzip ausgegeben werden, bis ich das Finster irgendwann schließe.
Also irgendwie verstehe ich das nicht. Ich möchte einfach, dass wenn ich auf den Button klicke, mir die Bilder nach dem Zufallsprinzip ausgegeben werden, bis ich das Finster irgendwann schließe.
Code: Alles auswählen
import tkinter as tk
from functools import partial
def countdown(root, counter, label):
counter -= 1
label["text"] = counter
root.after(1000, countdown, root, counter, label)
def main():
root = tk.Tk()
counter = 10
textlabel = tk.Label(root, text = "10", font=("Arial", 36))
textlabel.pack()
tk.Button(root, text="Countdown", command=partial(countdown, root, counter, textlabel)).pack()
root.mainloop()
if __name__ == '__main__':
main()
Hallo zusammen,
ich hab das kleine Programm unten geschrieben. Und es funktioniert auch.
Nun wollte ich das Programm mit einer main()-Funktion versehen, s.u. Es läuft auch, aber irgendwie nicht richtig. Denn beim Stop-Drücken würfelt er trotzdem weiter. Weiss jemand, was der Fehler ist? (Ich weiss, globale Variablen usw sind böse. Alles nur zu Übungszwecken.).
ich hab das kleine Programm unten geschrieben. Und es funktioniert auch.
Code: Alles auswählen
import tkinter
import random
def start_wuerfeln():
global running
running = True
wuerfeln()
def wuerfeln():
global running
if (running == True):
zufallszahl_wuerfel_1 = random.randint(1, 6)
label_wuerfel_1["text"] = " " + str(zufallszahl_wuerfel_1) + " "
hauptfenster.after(500, wuerfeln)
def stop_wuerfeln():
global running
running = False
running = False
hauptfenster = tkinter.Tk()
label_wuerfel_1 = tkinter.Label(hauptfenster, text=" 0 ", bg="yellow", font="Arial 44", borderwidth=5, relief="groove")
label_wuerfel_1.pack()
tkinter.Button(hauptfenster, text="Start", command=start_wuerfeln).pack()
tkinter.Button(hauptfenster, text="Stop", command=stop_wuerfeln).pack()
hauptfenster.mainloop()
Nun wollte ich das Programm mit einer main()-Funktion versehen, s.u. Es läuft auch, aber irgendwie nicht richtig. Denn beim Stop-Drücken würfelt er trotzdem weiter. Weiss jemand, was der Fehler ist? (Ich weiss, globale Variablen usw sind böse. Alles nur zu Übungszwecken.).
Code: Alles auswählen
import tkinter
import random
from functools import partial
def start_wuerfeln(hauptfenster, label_wuerfel_1):
running = True
wuerfeln(running, hauptfenster, label_wuerfel_1)
def wuerfeln(running, hauptfenster, label_wuerfel_1):
print(running)
if (running == True):
zufallszahl_wuerfel_1 = random.randint(1, 6)
label_wuerfel_1["text"] = " " + str(zufallszahl_wuerfel_1) + " "
hauptfenster.after(500, wuerfeln, running, hauptfenster, label_wuerfel_1)
def stop_wuerfeln(hauptfenster, label_wuerfel_1):
running = False
wuerfeln(running, hauptfenster, label_wuerfel_1)
def main():
hauptfenster = tkinter.Tk()
label_wuerfel_1 = tkinter.Label(hauptfenster, text=" 0 ", bg="yellow", font="Arial 44", borderwidth=5, relief="groove")
label_wuerfel_1.pack()
tkinter.Button(hauptfenster, text="Start", command=partial(start_wuerfeln, hauptfenster, label_wuerfel_1)).pack()
tkinter.Button(hauptfenster, text="Stop", command=partial(stop_wuerfeln, hauptfenster, label_wuerfel_1)).pack()
hauptfenster.mainloop()
if __name__ == '__main__':
main()
- __blackjack__
- User
- Beiträge: 14084
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
In `stop_wuerfeln()` rufst Du `wuerfeln()` auf was einfach nichts macht, weil `running` den Wert `False` hat. Dieser Aufruf hat keinen Einfluss auf die anderen Aufrufe, denn das `running` ist ein anderes. Das ist eine lokaler Name der nichts mit den anderen Aufrufen zu tun hat.
Du brauchst eine Klasse um Dir den Zustand zu merken. Auch nicht unbedingt als Flag, sondern besser als `None` oder die ID des nächsten Ereignisses das mit `after()` geplant wurde. Denn damit und `after_cancel()` kann man das abbrechen/wiederrufen.
Namen sollte man nicht nummerieren. Ist hier auch total überflüssig eine 1 an die Namen zu hängen. Es gibt ja nicht mal eine andere Zahl.
Du brauchst eine Klasse um Dir den Zustand zu merken. Auch nicht unbedingt als Flag, sondern besser als `None` oder die ID des nächsten Ereignisses das mit `after()` geplant wurde. Denn damit und `after_cancel()` kann man das abbrechen/wiederrufen.
Namen sollte man nicht nummerieren. Ist hier auch total überflüssig eine 1 an die Namen zu hängen. Es gibt ja nicht mal eine andere Zahl.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Dafür brauchst Du eine Klasse:
Code: Alles auswählen
import tkinter
import random
class Hauptfenster(tkinter.Tk):
def __init__(self):
tkinter.Tk.__init__(self)
self.wuerfel_id = None
self.wuerfel_wert = tkinter.IntVar(self, 0)
tkinter.Label(self, textvariable=self.wuerfel_wert,
bg="yellow", font="Arial 44", borderwidth=5,
relief="groove").pack()
tkinter.Button(self, text="Start", command=self.start_wuerfeln).pack()
tkinter.Button(self, text="Stop", command=self.stop_wuerfeln).pack()
def start_wuerfeln(self):
self.wuerfel_id = self.after(0, self.wuerfeln)
def stop_wuerfeln(self):
if self.wuerfel_id is not None:
self.after_cancel(self.wuerfel_id)
self.wuerfel_id = None
def wuerfeln(self):
self.wuerfel_wert.set(random.randint(1, 6))
self.wuerfel_id = self.after(500, self.wuerfeln)
def main():
hauptfenster = Hauptfenster()
hauptfenster.mainloop()
if __name__ == '__main__':
main()
Vielen Dank für euere Antworten!
Ich bin jedoch erstmal eine Stufe runtergegangen und hab versucht, das einfache Beispiel für die Konsole zu schreiben s.u.
In der GUI kann ich eine Funktion immer wieder aufrufen während sie läuft. Hier funktioniert das nicht, die Funktion start_wuerfeln() blockiert dann das Programm und die weitere Ausführung. Gibt es eine Möglichkeit die Funktion zu unterbrechen, wenn ich z.B. eine betimmte Taste drücke und dann weiter laufen lassen mit einer anderen Taste?
Ich bin jedoch erstmal eine Stufe runtergegangen und hab versucht, das einfache Beispiel für die Konsole zu schreiben s.u.
In der GUI kann ich eine Funktion immer wieder aufrufen während sie läuft. Hier funktioniert das nicht, die Funktion start_wuerfeln() blockiert dann das Programm und die weitere Ausführung. Gibt es eine Möglichkeit die Funktion zu unterbrechen, wenn ich z.B. eine betimmte Taste drücke und dann weiter laufen lassen mit einer anderen Taste?
Code: Alles auswählen
import random
from time import sleep
def start_wuerfeln(running):
running = True
wuerfeln(running)
def wuerfeln(running):
if (running == True):
zufallszahl = random.randint(1,6)
print(zufallszahl)
sleep(0.5)
wuerfeln(running)
def stop_wuerfeln():
running = False
wuerfeln(running)
def main():
running = False
start_wuerfeln(running)
sleep(5)
stop_wuerfeln()
if __name__ == '__main__':
main()
@Sam123: Vollzieh mal nach, was dein Programm tut.
In start_wuerfeln bindest du True an den Namen running. running übergibst du an wuefeln. In wuerfeln prruefst du, ob running wahr ist (ist es, wie wir gerade festgetellt haben), dann gibst du eine Zufallszahl aus, legst das Programm eine halbe Sekunde schlafen und rufst dann wieder wuerfeln auf.
Das erneute Aufrufen von wuerfeln wäre auch in einer GUI ein falscher rekursiver Aufruf. Warum sollte sich wuerfeln selbst aufrufen?
Und da auch bei dem rekursiven Aufruf die Umstände nicht geändert haben, kommst du wieder zu der Bedingung, ob running wahr ist ... und da das wahr ist geht es wieder den selben Gang.
Vielleicht kannst du kurz beschreiben, was genau du erreichen willst, dann kann man dir auch einen möglichen aufbau an die Hand geben. Dein Versuch lässt da viele Interpretationsmöglichkeiten.
In start_wuerfeln bindest du True an den Namen running. running übergibst du an wuefeln. In wuerfeln prruefst du, ob running wahr ist (ist es, wie wir gerade festgetellt haben), dann gibst du eine Zufallszahl aus, legst das Programm eine halbe Sekunde schlafen und rufst dann wieder wuerfeln auf.
Das erneute Aufrufen von wuerfeln wäre auch in einer GUI ein falscher rekursiver Aufruf. Warum sollte sich wuerfeln selbst aufrufen?
Und da auch bei dem rekursiven Aufruf die Umstände nicht geändert haben, kommst du wieder zu der Bedingung, ob running wahr ist ... und da das wahr ist geht es wieder den selben Gang.
Vielleicht kannst du kurz beschreiben, was genau du erreichen willst, dann kann man dir auch einen möglichen aufbau an die Hand geben. Dein Versuch lässt da viele Interpretationsmöglichkeiten.
ich denke, auf dem Level ist mir schon klar, was das Programm macht. Warum eigentlich falscher rekursiver Ausdruck? In den Beispielen davor (z.B. vom 3. Februar, s.u.) hat sich die Funktion doch auch immer wieder selbst aufgerufen, mit after. Ist es keine Rekursion?
Wenn ich das mit einer while-Schleife mache, dann habe ich keine Rekursion. Aber kann ich die Funktion wuerfeln() irgendwie stoppen, wenn sie erstmal läuft, running = False übergeben? Und dann evtl. wieder starten, wie bei der GUI mit den Buttons "Start" und "Stop".
Ich will später ein umfangreicheres Würfelspiel programmieren. Aber jetzt versuche ich erstmal, Python ein bischen besser zu vestehen
Code: Alles auswählen
def wuerfeln(self):
self.wuerfel_wert.set(random.randint(1, 6))
self.wuerfel_id = self.after(500, self.wuerfeln)
Wenn ich das mit einer while-Schleife mache, dann habe ich keine Rekursion. Aber kann ich die Funktion wuerfeln() irgendwie stoppen, wenn sie erstmal läuft, running = False übergeben? Und dann evtl. wieder starten, wie bei der GUI mit den Buttons "Start" und "Stop".
Ich will später ein umfangreicheres Würfelspiel programmieren. Aber jetzt versuche ich erstmal, Python ein bischen besser zu vestehen

Code: Alles auswählen
import random
from time import sleep
def start_wuerfeln(running):
running = True
wuerfeln(running)
def wuerfeln(running):
while(running == True):
zufallszahl = random.randint(1,6)
print("*****")
print("* " + str(zufallszahl) +" *")
print("*****\n")
sleep(0.5)
def stop_wuerfeln():
running = False
wuerfeln(running)
def main():
running = False
start_wuerfeln(running)
stop_wuerfeln()
if __name__ == '__main__':
main()
Nein, das ist keine Rekursion, die Ausführung wird immer wieder an den Mainloop zurückgegeben.
Ein Konsolenprogramm sieht normalerweise keine solche Interaktion vor. Da braucht man dann wieder Zusatzpakete für Text-UIs und dann bist Du bei einer GUI besser aufgehoben.
Ein Konsolenprogramm sieht normalerweise keine solche Interaktion vor. Da braucht man dann wieder Zusatzpakete für Text-UIs und dann bist Du bei einer GUI besser aufgehoben.