添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Hello I would like to create a countdown timer within a subroutine which is then displayed on the canvas. I'm not entirely sure of where to begin I've done some research on to it and was able to make one with the time.sleep(x) function but that method freezes the entire program which isn't what I'm after. I also looked up the other questions on here about a timer and tried to incorporate them into my program but I wasn't able to have any success yet.

TLDR; I want to create a countdown timer that counts down from 60 seconds and is displayed on a canvas and then have it do something when the timer reaches 0.

Is anyone able to point me in the right direction?

Thanks in advance.

EDIT: With the suggestions provided I tried to put them into the program without much luck.

Not sure if there is a major error in this code or if it's just a simple mistake. The error I get when I run it is below the code.

This is the part of the code that I want the timer in:

def main(): #First thing that loads when the program is executed.
   global window
   global tkinter
   global canvas
   global cdtimer
   window = Tk()
   cdtimer = 60
   window.title("JailBreak Bob")
   canvas = Canvas(width = 960, height = 540, bg = "white")
   photo = PhotoImage(file="main.gif")
   canvas.bind("<Button-1>", buttonclick_mainscreen)
   canvas.pack(expand = YES, fill = BOTH)
   canvas.create_image(1, 1, image = photo, anchor = NW)
   window.mainloop()
def buttonclick_mainscreen(event):
   pressed = ""
   if event.x >18 and event.x <365 and event.y > 359 and event.y < 417 : pressed = 1 
   if event.x >18 and event.x <365 and event.y > 421 and event.y < 473 : pressed = 2 
   if event.x >18 and event.x <365 and event.y > 477 and event.y < 517 : pressed = 3 
   if pressed == 1 :
     gamescreen()
   if pressed == 2 :
     helpscreen()
   if pressed == 3 :
     window.destroy()
def gamescreen():
  photo = PhotoImage(file="gamescreen.gif")
  canvas.bind("<Button-1>", buttonclick_gamescreen)
  canvas.pack(expand = YES, fill = BOTH)
  canvas.create_image(1, 1, image = photo, anchor = NW)
  game1 = PhotoImage(file="1.gif")
  canvas.create_image(30, 65, image = game1, anchor = NW)
  e1 = Entry(canvas, width = 11)
  e2 = Entry(canvas, width = 11) 
  canvas.create_window(390, 501, window=e1, anchor = NW)
  canvas.create_window(551, 501, window=e2, anchor = NW)
  canvas.after(1, gamescreen)
  window.mainloop()
def cdtimer():
  canvas.delete(ALL)
  global cdtimer
  cdtimer -= 1
  canvas.create_text(510, 6, text=cdtimer, font="Ubuntu 29 bold", anchor = NW) 
  if cdtimer == 0:
    scorescreen()
  else:
    canvas.after(1000, gamescreen)
main()

Error MSG:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.2/tkinter/__init__.py", line 1402, in __call__
    return self.func(*args)
  File "/usr/lib/python3.2/tkinter/__init__.py", line 490, in callit
    func(*args)
  File "/home/ppppwn3d/workspace/Python/JailBreakBob/JailBreakBob.py", line 50, in     gamescreen
    e1 = Entry(canvas, width = 11)
  File "/usr/lib/python3.2/tkinter/__init__.py", line 2372, in __init__
    Widget.__init__(self, master, 'entry', cnf, kw)
  File "/usr/lib/python3.2/tkinter/__init__.py", line 1952, in __init__
    cnf = _cnfmerge((cnf, kw))
  File "/usr/lib/python3.2/tkinter/__init__.py", line 71, in _cnfmerge
    if isinstance(cnfs, dict):
RuntimeError: maximum recursion depth exceeded while calling a Python object
                You should never call mainloop more than once. Also, calling gamescreen every millisecond means you're creating 2000 images and 2000 entry widgets every second.
– Bryan Oakley
                Jul 10, 2013 at 15:47
                I've fixed it now, I was calling the incorrect subroutine to start the CD timer    canvas.after(1, gamescreen) Should have been   canvas.after(1, cdtimer) and      canvas.after(1000, gamescreen) should have been     canvas.after(1000, cdtimer)
– ThatsNotMyName
                Jul 11, 2013 at 6:35

This is an expansion of Oakley's answer. It demonstrates how to display the time in a canvas as well as kick off the whole thing:

from tkinter import *
root = Tk()
canvas = Canvas(root)
canvas.pack()
time = 60
def tick():
    # You have to clear the canvas each time the clock updates 
    # (otherwise it writes on top of the old time).  Since the
    # time is the only thing in the canvas, delete(ALL) works
    # perfectly (if it wasn't however, you can delete the id
    # that goes with the clock).
    canvas.delete(ALL)
    # I have to declare time as a global because I'm not using
    # a class (otherwise, I could do something like self.time -= 1)
    global time
    time -= 1
    # You can place the time wherever in the canvas
    # (I chose 10,10 for the example)
    canvas.create_text(10, 10, text=time)
    if time == 0:
        do_something()
    else:
        canvas.after(1000, tick)
canvas.after(1, tick)
root.mainloop()

The script counts down from 60 seconds (displaying the remaining time as it goes) and, when it hits 0, it calls do_something.

I was able to get the code to run without errors in the program but I'm suffering from stack overflows now haha. I should be able to fix that after I rearrange some lines. – ThatsNotMyName Jul 9, 2013 at 10:09 You don't have to clear the canvas for every tick. You can create the text item once and use itemconfigure to change the text value on each tick. Arguably, if all you're doing is displaying the time, a label widget is a little easier to use than a canvas. – Bryan Oakley Jul 9, 2013 at 10:50 I took a look at the example code again and realised I messed up a part of it. The format of the program I've written is as follows: import x def main(): define the canvas the name etc etc and calls the other subroutines def screenwithcountdown(): the canvas has an image and entry widget the countdown timer lines main() Where would I place the "canvas.after(1, screenwithcountdown)"? It seems vital to the program but I tried placing it in various areas without success. And sorry if what I typed makes no sense. – ThatsNotMyName Jul 9, 2013 at 12:53 @ThatsNotMyName: "canvas.after(1, screenwidthcountdown)" is what kicks off the timer loop. It is basically saying "after one millisecond (or when the script is loaded), begin counting. The exact location of where to put it is up to you (as long as it is after you define "canvas" and "screenwidthcountdown". – user2555451 Jul 9, 2013 at 14:39

You want to use the after method. The logic goes something like this:

def update_clock(self):
    self.counter -= 1
    if self.counter == 0 :
        do_something()
    else:
        self.after(1000, self.update_clock)

The above will subtract one from the counter. If the counter is zero it does something special. Otherwise, it schedules itself to run again in one second.

Threading? For a simple counter? That's adding way too much complexity for such a simple problem. Plus, the thread can't update the Tkinter GUI directly so you have to add more complexity to communicate between threads with a queue and some sort of polling mechanism. – Bryan Oakley Jul 10, 2013 at 15:44

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.