Python tKinter:如何暂停倒数计时器
Python tKinter: How to pause countdown timer
我制作了一个倒数计时器,每当我按下键盘上的 space 键时它就会启动,但问题是在计时器结束之前我无法对程序执行任何操作。我想让它在第二次按下 space 键时暂停。
我制作的倒数计时器在一个 while 循环中工作,当计时器达到 0 时该循环结束,因此程序会等到循环结束后再做任何其他事情,即使我想停止计时器我也做不到它是 运行.
这是代码
from tkinter import *
from tkinter import ttk
import tkinter as tk
from PIL import ImageTk, Image
def StartTimer():
if (root.turn % 2) == 0: #Turn to white
root.number = '1'
root.color = 'white'
else: #Turn to black
root.number = '2'
root.color = 'black'
doTimer()
def doTimer():
root.time = root.minute *60 + root.second
root.turn = root.turn+1
number = root.number
root.rndsquare1.configure(image=root.green)
root.timer1.configure(bg='#1C953D')
root.white.configure(bg='#1C953D')
r=0
while r < root.time:
root.update_idletasks()
root.after(1000)
root.second = root.second - 1
if root.second == -1:
root.minute = root.minute -1
root.second = 59
root.time1 = ''
if len(str(root.minute)) == 1:
root.time1 = '0' + str(root.minute)
else:
root.time1 = str(root.minute)
if len(str(root.second)) == 1:
root.time1 = root.time1 + ':' + '0' + str(root.second)
else:
root.time1 = root.time1 + ':' + str(root.second)
root.timer1.configure(text=root.time1)
r=r+1
root.timer1.configure(bg='#454545')
root.white.configure(bg='#454545')
root.rndsquare1.configure(image=root.grey)
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.title("Chess Clock")
self.minsize(1539,600)
self.windowBG = '#313131'
self.state('zoomed')
self.configure(bg=self.windowBG)
self.CreateWindow()
def CreateWindow(self):
self.grey = ImageTk.PhotoImage(Image.open(r"D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square grey.png"))
self.green = ImageTk.PhotoImage(Image.open(r"D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square green.png"))
self.turn=0
self.rndsquare1 = Label(self, image=self.grey, borderwidth=0)
self.rndsquare1.place(x=65, y=120)
self.rndsquare2 = Label(self, image=self.grey, borderwidth=0)
self.rndsquare2.place(x=809, y=120)
self.bind('<space>',lambda event:StartTimer())
self.createTimers()
def createTimers(self):
self.minute = 1
self.second = 5
self.time1 = ''
if len(str(self.minute)) == 1:
self.time1 = '0' + str(self.minute)
else:
self.time1 = str(self.minute)
if len(str(self.second)) == 1:
self.time1 = self.time1 + ':' + '0' + str(self.second)
else:
self.time1 = self.time1 + ':' + str(self.second)
self.time2 = ''
if len(str(self.minute)) == 1:
self.time2 = '0' + str(self.minute)
else:
self.time2 = str(self.minute)
if len(str(self.second)) == 1:
self.time2 = self.time2 + ':' + '0' + str(self.second)
else:
self.time2 = self.time2 + ':' + str(self.second)
self.timer1 = Label(self, text=self.time1, bg='#454545', fg='white', font ="Gadugi 40 bold")
self.timer1.place(x=330, y=420)
self.timer2 = Label(self, text=self.time2, bg='#454545', fg='white', font ="Gadugi 40 bold")
self.timer2.place(x=1080, y=420)
self.white = Label(self, text='White', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white.place(x=325, y=160)
self.black = Label(self, text='Black', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black.place(x=1075, y=160)
root=root()
root.mainloop()
D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square grey.png
D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square green.png
您可以通过大量重构代码来解决这个问题。您可以向您的小部件添加 2 个时钟,每个时钟跟踪在其自身上花费了多少。空格键侦听器只是在当前使用的时钟之间切换。通过每隔 200 毫秒左右定时 do_clock_logic
,它会检查是否设置了当前时钟,如果是,如果时间到了,如果是这样,则切换到另一个时钟。在任何情况下,它都会触发时钟 tick() 方法来更新其内部状态,该状态也处理 ui 更新。
这样就没有“阻塞”的 while 循环,所有计时的东西都由 tk 处理:
from tkinter import Tk, Label
import tkinter as tk
from PIL import ImageTk, Image
from datetime import datetime, timedelta
class clock():
"""A single clock that handles updating/timekeeping itself. It uses
the both class-level memebrs as active/inactive image and has
references provided to place the image and the timing text."""
active_img = None
deactive_img = None
@staticmethod
def format_time(delta, ms = False):
"""Returns a formatted strng for a timedelta instance,
optionally with milliseconds"""
return f"{delta.seconds//60:02}:{delta.seconds%60:02}" + (
f".{(delta.microseconds // 1000):04}" if ms else "")
def __init__(self, minutes, seconds, bg_lbl, text_lbl):
"""Set the clocks max duration providing 'minutes' and 'seconds'.
Provide tk-labels with a background image 'bg_lbl' and
'text_lbl' for the time display."""
self.max_duration = timedelta(seconds=seconds+minutes*60)
# UI
self.bg_lbl = bg_lbl,
self.text_lbl = text_lbl
# reset to inactive image and no text
self.bg_lbl[0].config(image = clock.deactive_img)
self.text_lbl.config(text = "")
# internal time keeping of total spent time1
self.total = timedelta() # 0 delta at start
def update_lbl(self, spent):
# update the image if needed
self.bg_lbl[0].config(image = clock.active_img if self.started is not None else clock.deactive_img)
# update labels - if not active - show with milliseconds
if self.started is not None:
self.text_lbl.config( text = clock.format_time(self.max_duration - spent))
else:
self.text_lbl.config(text = f"Total:\n{clock.format_time(self.total, True)}")
def start_clock(self):
# starts the clock
self.started = datetime.now()
self.update_lbl(timedelta())
def tick(self):
# ticks the clock - stops it if time has run out
if self.started is not None:
spent = datetime.now() - self.started
if spent > self.max_duration:
self._stop_clock(spent)
return False
self.update_lbl(spent)
return True
return None
def stop_clock(self):
# stop clock from the outside if <space> is hit
if self.started is not None:
spent = datetime.now() - self.started
self._stop_clock(spent)
def _stop_clock(self, spent):
# internal method that stops the clock, adds total & updates
spent = min(spent, self.max_duration) # fix it
self.total += spent
self.started = None
self.update_lbl(None)
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.title("Chess Clock")
self.minsize(1539,600)
self.windowBG = '#313131'
self.state('zoomed')
self.configure(bg=self.windowBG)
self.CreateWindow()
def CreateWindow(self):
self.grey = ImageTk.PhotoImage(Image.open(r"grey.png"))
self.green = ImageTk.PhotoImage(Image.open(r"green.png"))
# used to determine player
self.turn = 0
# give the clock class the two images to switch
# if changing between active/inactive state
clock.deactive_img = self.grey
clock.active_img = self.green
# one clocks UI
self.white_bg = Label(self, image=self.grey, borderwidth=0)
self.white_bg.place(relx=.3, rely=.55, anchor="center")
self.white = Label(self, text='White', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white.place(relx=.3, rely=.2, anchor="center")
self.white_timer = Label(self.white_bg, text="", bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white_timer.place(relx=.5, rely=.5, anchor="center")
# seconds clock UI
self.black_bg = Label(self, image=self.grey, borderwidth=0)
self.black_bg.place(relx=.7, rely=.55, anchor="center")
self.black = Label(self, text='Black', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black.place(relx=.7, rely=.2, anchor="center")
self.black_timer = Label(self.black_bg, text="", bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black_timer.place(relx=.5, rely=.5, anchor="center")
# provide the background-label and the text label
# for time and create two clocks for the players
self.clock1 = clock(1, 5, self.white_bg, self.white_timer)
self.clock2 = clock(1,5, self.black_bg, self.black_timer)
# which clock is currently in use?
self.who_is_it = None
# handles switching to next players clock
self.bind('<space>', lambda _: self.next_player())
self.bind('<Control-Key-q>', lambda _: self.stop())
# check every 200ms if clocks need to be switched over
self.after(200, self.do_clock_logic)
def do_clock_logic(self):
# do nothing if no clock startet
# check if clock has run out, then switch to next players clock
if self.who_is_it is not None:
# tick() returns False if the player spent all his time
# tick() returns True if the player still has time
# tick() returns None if clock is not yet started
if self.who_is_it.tick() == False:
self.next_player()
# recheck clocks in 200ms
self.after(200, self.do_clock_logic)
def stop(self):
"""First Ctrl+q will stop clocks, second will quit."""
if self.who_is_it is not None:
self.who_is_it.stop_clock()
self.who_is_it = None
self.do_clock_logic = lambda _: None is None
else:
self.destroy()
def next_player(self):
if self.who_is_it is not None:
self.who_is_it.stop_clock()
self.turn += 1
# player 1 on "odd turns", player 2 on "even turns"
self.who_is_it = self.clock1 if self.turn % 2 else self.clock2
self.who_is_it.start_clock()
root=root()
root.mainloop()
得到
在第一次 CTRL+q
之后你会得到结果 - 第二次 CTRL+q
关闭你的 window:
关于 UI/logic 内容可以更好地构建 - 但它可以作为概念证明。
我制作了一个倒数计时器,每当我按下键盘上的 space 键时它就会启动,但问题是在计时器结束之前我无法对程序执行任何操作。我想让它在第二次按下 space 键时暂停。
我制作的倒数计时器在一个 while 循环中工作,当计时器达到 0 时该循环结束,因此程序会等到循环结束后再做任何其他事情,即使我想停止计时器我也做不到它是 运行.
这是代码
from tkinter import *
from tkinter import ttk
import tkinter as tk
from PIL import ImageTk, Image
def StartTimer():
if (root.turn % 2) == 0: #Turn to white
root.number = '1'
root.color = 'white'
else: #Turn to black
root.number = '2'
root.color = 'black'
doTimer()
def doTimer():
root.time = root.minute *60 + root.second
root.turn = root.turn+1
number = root.number
root.rndsquare1.configure(image=root.green)
root.timer1.configure(bg='#1C953D')
root.white.configure(bg='#1C953D')
r=0
while r < root.time:
root.update_idletasks()
root.after(1000)
root.second = root.second - 1
if root.second == -1:
root.minute = root.minute -1
root.second = 59
root.time1 = ''
if len(str(root.minute)) == 1:
root.time1 = '0' + str(root.minute)
else:
root.time1 = str(root.minute)
if len(str(root.second)) == 1:
root.time1 = root.time1 + ':' + '0' + str(root.second)
else:
root.time1 = root.time1 + ':' + str(root.second)
root.timer1.configure(text=root.time1)
r=r+1
root.timer1.configure(bg='#454545')
root.white.configure(bg='#454545')
root.rndsquare1.configure(image=root.grey)
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.title("Chess Clock")
self.minsize(1539,600)
self.windowBG = '#313131'
self.state('zoomed')
self.configure(bg=self.windowBG)
self.CreateWindow()
def CreateWindow(self):
self.grey = ImageTk.PhotoImage(Image.open(r"D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square grey.png"))
self.green = ImageTk.PhotoImage(Image.open(r"D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square green.png"))
self.turn=0
self.rndsquare1 = Label(self, image=self.grey, borderwidth=0)
self.rndsquare1.place(x=65, y=120)
self.rndsquare2 = Label(self, image=self.grey, borderwidth=0)
self.rndsquare2.place(x=809, y=120)
self.bind('<space>',lambda event:StartTimer())
self.createTimers()
def createTimers(self):
self.minute = 1
self.second = 5
self.time1 = ''
if len(str(self.minute)) == 1:
self.time1 = '0' + str(self.minute)
else:
self.time1 = str(self.minute)
if len(str(self.second)) == 1:
self.time1 = self.time1 + ':' + '0' + str(self.second)
else:
self.time1 = self.time1 + ':' + str(self.second)
self.time2 = ''
if len(str(self.minute)) == 1:
self.time2 = '0' + str(self.minute)
else:
self.time2 = str(self.minute)
if len(str(self.second)) == 1:
self.time2 = self.time2 + ':' + '0' + str(self.second)
else:
self.time2 = self.time2 + ':' + str(self.second)
self.timer1 = Label(self, text=self.time1, bg='#454545', fg='white', font ="Gadugi 40 bold")
self.timer1.place(x=330, y=420)
self.timer2 = Label(self, text=self.time2, bg='#454545', fg='white', font ="Gadugi 40 bold")
self.timer2.place(x=1080, y=420)
self.white = Label(self, text='White', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white.place(x=325, y=160)
self.black = Label(self, text='Black', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black.place(x=1075, y=160)
root=root()
root.mainloop()
D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square grey.png
D:\Users\Jean Paul\OneDrive\Programming\Programs\Prog 6 - Chess Clock\bg square green.png
您可以通过大量重构代码来解决这个问题。您可以向您的小部件添加 2 个时钟,每个时钟跟踪在其自身上花费了多少。空格键侦听器只是在当前使用的时钟之间切换。通过每隔 200 毫秒左右定时 do_clock_logic
,它会检查是否设置了当前时钟,如果是,如果时间到了,如果是这样,则切换到另一个时钟。在任何情况下,它都会触发时钟 tick() 方法来更新其内部状态,该状态也处理 ui 更新。
这样就没有“阻塞”的 while 循环,所有计时的东西都由 tk 处理:
from tkinter import Tk, Label
import tkinter as tk
from PIL import ImageTk, Image
from datetime import datetime, timedelta
class clock():
"""A single clock that handles updating/timekeeping itself. It uses
the both class-level memebrs as active/inactive image and has
references provided to place the image and the timing text."""
active_img = None
deactive_img = None
@staticmethod
def format_time(delta, ms = False):
"""Returns a formatted strng for a timedelta instance,
optionally with milliseconds"""
return f"{delta.seconds//60:02}:{delta.seconds%60:02}" + (
f".{(delta.microseconds // 1000):04}" if ms else "")
def __init__(self, minutes, seconds, bg_lbl, text_lbl):
"""Set the clocks max duration providing 'minutes' and 'seconds'.
Provide tk-labels with a background image 'bg_lbl' and
'text_lbl' for the time display."""
self.max_duration = timedelta(seconds=seconds+minutes*60)
# UI
self.bg_lbl = bg_lbl,
self.text_lbl = text_lbl
# reset to inactive image and no text
self.bg_lbl[0].config(image = clock.deactive_img)
self.text_lbl.config(text = "")
# internal time keeping of total spent time1
self.total = timedelta() # 0 delta at start
def update_lbl(self, spent):
# update the image if needed
self.bg_lbl[0].config(image = clock.active_img if self.started is not None else clock.deactive_img)
# update labels - if not active - show with milliseconds
if self.started is not None:
self.text_lbl.config( text = clock.format_time(self.max_duration - spent))
else:
self.text_lbl.config(text = f"Total:\n{clock.format_time(self.total, True)}")
def start_clock(self):
# starts the clock
self.started = datetime.now()
self.update_lbl(timedelta())
def tick(self):
# ticks the clock - stops it if time has run out
if self.started is not None:
spent = datetime.now() - self.started
if spent > self.max_duration:
self._stop_clock(spent)
return False
self.update_lbl(spent)
return True
return None
def stop_clock(self):
# stop clock from the outside if <space> is hit
if self.started is not None:
spent = datetime.now() - self.started
self._stop_clock(spent)
def _stop_clock(self, spent):
# internal method that stops the clock, adds total & updates
spent = min(spent, self.max_duration) # fix it
self.total += spent
self.started = None
self.update_lbl(None)
class root(Tk):
def __init__(self):
super(root, self).__init__()
self.title("Chess Clock")
self.minsize(1539,600)
self.windowBG = '#313131'
self.state('zoomed')
self.configure(bg=self.windowBG)
self.CreateWindow()
def CreateWindow(self):
self.grey = ImageTk.PhotoImage(Image.open(r"grey.png"))
self.green = ImageTk.PhotoImage(Image.open(r"green.png"))
# used to determine player
self.turn = 0
# give the clock class the two images to switch
# if changing between active/inactive state
clock.deactive_img = self.grey
clock.active_img = self.green
# one clocks UI
self.white_bg = Label(self, image=self.grey, borderwidth=0)
self.white_bg.place(relx=.3, rely=.55, anchor="center")
self.white = Label(self, text='White', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white.place(relx=.3, rely=.2, anchor="center")
self.white_timer = Label(self.white_bg, text="", bg='#454545', fg='white', font ="Gadugi 40 bold")
self.white_timer.place(relx=.5, rely=.5, anchor="center")
# seconds clock UI
self.black_bg = Label(self, image=self.grey, borderwidth=0)
self.black_bg.place(relx=.7, rely=.55, anchor="center")
self.black = Label(self, text='Black', bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black.place(relx=.7, rely=.2, anchor="center")
self.black_timer = Label(self.black_bg, text="", bg='#454545', fg='white', font ="Gadugi 40 bold")
self.black_timer.place(relx=.5, rely=.5, anchor="center")
# provide the background-label and the text label
# for time and create two clocks for the players
self.clock1 = clock(1, 5, self.white_bg, self.white_timer)
self.clock2 = clock(1,5, self.black_bg, self.black_timer)
# which clock is currently in use?
self.who_is_it = None
# handles switching to next players clock
self.bind('<space>', lambda _: self.next_player())
self.bind('<Control-Key-q>', lambda _: self.stop())
# check every 200ms if clocks need to be switched over
self.after(200, self.do_clock_logic)
def do_clock_logic(self):
# do nothing if no clock startet
# check if clock has run out, then switch to next players clock
if self.who_is_it is not None:
# tick() returns False if the player spent all his time
# tick() returns True if the player still has time
# tick() returns None if clock is not yet started
if self.who_is_it.tick() == False:
self.next_player()
# recheck clocks in 200ms
self.after(200, self.do_clock_logic)
def stop(self):
"""First Ctrl+q will stop clocks, second will quit."""
if self.who_is_it is not None:
self.who_is_it.stop_clock()
self.who_is_it = None
self.do_clock_logic = lambda _: None is None
else:
self.destroy()
def next_player(self):
if self.who_is_it is not None:
self.who_is_it.stop_clock()
self.turn += 1
# player 1 on "odd turns", player 2 on "even turns"
self.who_is_it = self.clock1 if self.turn % 2 else self.clock2
self.who_is_it.start_clock()
root=root()
root.mainloop()
得到
在第一次 CTRL+q
之后你会得到结果 - 第二次 CTRL+q
关闭你的 window:
关于 UI/logic 内容可以更好地构建 - 但它可以作为概念证明。