【问题标题】:Python - Countdown timer within a tkinter canvasPython - tkinter 画布中的倒数计时器
【发布时间】:2013-07-06 11:02:40
【问题描述】:

您好,我想在子程序中创建一个倒数计时器,然后将其显示在画布上。我不完全确定从哪里开始我已经对其进行了一些研究,并且能够使用 time.sleep(x) 函数制作一个,但该方法冻结了整个程序,这不是我想要的.我还在这里查找了有关计时器的其他问题,并尝试将它们合并到我的程序中,但我还没有取得任何成功。

TLDR;我想创建一个倒计时计时器,它从 60 秒开始倒计时并显示在画布上,然后在计时器到达 0 时让它执行某些操作。

有人能指出我正确的方向吗?

提前致谢。

编辑:根据提供的建议,我尝试将它们放入程序中,但运气不佳。

不确定此代码是否存在重大错误,或者只是一个简单的错误。 我运行它时得到的错误位于代码下方。

这是我想要计时器的代码部分:

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()

错误信息:

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

【问题讨论】:

  • 您不应多次致电mainloop。此外,每毫秒调用 gamescreen 意味着您每秒创建 2000 个图像和 2000 个条目小部件。
  • 我现在已经修复了,我调用了错误的子程序来启动 CD 计时器 canvas.after(1, gamescreen) 应该是 canvas.after(1, cdtimer) 和 canvas.after( 1000, gamescreen) 应该是 canvas.after(1000, cdtimer)

标签: python canvas timer tkinter


【解决方案1】:

您想使用after 方法。逻辑是这样的:

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

上面会从计数器中减一。如果计数器为零,它会做一些特别的事情。否则,它会安排自己在一秒钟内再次运行。

【讨论】:

    【解决方案2】:

    您可能想尝试线程?

    import thread
    import time
    
    def myFunct():
        sec = 60
        n = 1
        for i in range(sec/n):
            updateClock()
            time.sleep(n)
        finalFunct()
    
    thread.start_new_thread(myFunct, ())
    

    只需将sec 更改为初始数量(以秒为单位),将n 更改为您希望它更新的时间间隔(以秒为单位)。

    【讨论】:

    • 线程?对于一个简单的计数器?对于这样一个简单的问题,这增加了太多的复杂性。此外,线程无法直接更新 Tkinter GUI,因此您必须增加更多复杂性以使用队列和某种轮询机制在线程之间进行通信。
    【解决方案3】:

    这是奥克利答案的扩展。它演示了如何在画布中显示时间以及启动整个过程:

    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()
    

    脚本从 60 秒开始倒计时(显示剩余时间),当它到达 0 时,它会调用 do_something。

    【讨论】:

    • 感谢您的详细回复。没有意识到这不需要导入时间。
    • 我能够让代码在程序中正常运行,但我现在正遭受堆栈溢出的困扰,哈哈。重新排列一些行后,我应该能够解决这个问题。
    • 您不必为每个刻度清除画布。您可以创建一次文本项并使用itemconfigure 更改每个刻度上的文本值。可以说,如果您所做的只是显示时间,那么标签小部件比画布更易于使用。
    • 我再次查看了示例代码,意识到我搞砸了其中的一部分。我编写的程序的格式如下: import x def main(): 定义画布名称等并调用其他子例程 def screenwithcountdown(): 画布有一个图像和条目小部件倒数计时器行 main () 我会将“canvas.after(1, screenwithcountdown)”放在哪里?这似乎对程序至关重要,但我尝试将其放置在各个领域但没有成功。对不起,如果我输入的内容没有意义。
    • @ThatsNotMyName: "canvas.after(1, screenwidthcountdown)" 是启动计时器循环的原因。它基本上是说“在一毫秒后(或加载脚本时),开始计数。放置它的确切位置取决于您(只要它在您定义“canvas”和“screenwidthcountdown”之后。
    猜你喜欢
    • 1970-01-01
    • 2012-05-22
    • 2018-06-14
    • 2016-03-05
    • 1970-01-01
    • 2020-05-15
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多