我正在制作一个反应时间游戏。描述:Tkinter GUI,随机时间后会出现一个具有随机颜色、大小、字体、位置的框。如果玩家愿意,openpyxl 可以保存游戏数据,线程用于计算所用的秒数,DateTime 可以放入游戏数据的 Excel 表中,随机选择位置、大小、字体、颜色、框中的文本。
预期的游戏是,你会在一段时间后出现一个框,你尽可能快地单击该框,它会循环 3 次,当 3 次完成时,你会得到一个统计 GUI,其中包含每次的时间3次,平均时间,保存btn,再玩btn。
错误:错误是游戏计数错误,如果你尝试游戏你会看到当统计GUI出现时一直会增加0.01,例如: 时间1:1.15 时间2:1.16 时间3:1.17
如果有人能指出错误,那就太棒了。
代码:
from tkinter import *
from tkinter import messagebox
from time import sleep
import openpyxl
import datetime
import random
import threading
def run_game():
root = Tk()
root.title("Reaction Time Game")
root.iconbitmap("C:/Users/Axelr/PycharmProjects/PC01/main/Self built/Reaction Time Game/icon.ico")
root.geometry("1400x1000")
root.configure(bg="orange")
workbook = openpyxl.load_workbook(filename="time_data.xlsx")
sheet = workbook.active
time_used_list = []
first_run = True
def clicked3():
global keep_counting
keep_counting = False
root.destroy()
root2 = Tk()
root2.title("Reaction Time Game Completed")
root2.iconbitmap("C:/Users/Axelr/PycharmProjects/PC01/main/Self built/Reaction Time Game/icon.ico")
root2.geometry("700x570")
root2.configure(bg="lightblue")
def save():
current_datetime = datetime.datetime.now()
current_datetime = current_datetime.strftime("%Y-%m-%d | %H:%M")
place = sheet.max_row + 1
sheet[f"A{place}"] = round(time_used_list[0], 2)
sheet[f"B{place}"] = round(time_used_list[1], 2)
sheet[f"C{place}"] = round(time_used_list[2], 2)
average = sum(time_used_list) / len(time_used_list)
sheet[f"D{place}"] = round(average, 2)
sheet[f"E{place}"] = current_datetime
workbook.save(filename="time_data.xlsx")
print("Saved time data")
def run_another():
root2.destroy()
run_game()
print("list of times:")
for time in time_used_list:
print(time)
title_label = Label(root2, text="Results", font=("Arial", 20, "bold"), bg="orange")
title_label.pack(padx=20, pady=20)
time1_label = Label(root2, text=f"Time used 1: {round(time_used_list[0], 2)} seconds", font=("Arial", 20), bg="orange")
time1_label.pack(padx=20, pady=20)
time2_label = Label(root2, text=f"Time used 2: {round(time_used_list[1], 2)} seconds", font=("Arial", 20), bg="orange")
time2_label.pack(padx=20, pady=20)
time3_label = Label(root2, text=f"Time used 3: {round(time_used_list[2], 2)} seconds", font=("Arial", 20), bg="orange")
time3_label.pack(padx=20, pady=20)
average_time = sum(time_used_list) / len(time_used_list)
average_label = Label(root2, text=f"Average Time Used: {round(average_time, 2)} seconds", font=("Arial", 20), bg="orange")
average_label.pack(padx=20, pady=20)
save_btn = Button(root2, text="Save", font=("Arial", 20), command=save, bg="orange")
save_btn.pack(padx=20, pady=20)
play_btn = Button(root2, text="Play Again", font=("Arial", 20), bg="orange", command=run_another)
play_btn.pack(padx=20, pady=20)
root2.mainloop()
def clicked():
global keep_counting, times_clicked, first_run
keep_counting = False
if times_clicked == 2:
clicked3()
else:
times_clicked += 1
btn.destroy()
print(first_run)
print(times_clicked)
first_run = False
start(first_run)
def time_thread():
global time_used
time_used = 0.00
while keep_counting:
sleep(0.01)
time_used += 0.01
time_used_list.append(time_used)
def start(first):
global keep_counting, times_clicked, first_run
if first:
times_clicked = 0
first_run = False
color_list = ["red", "green", "yellow", "cyan", "white", "blue", "magenta"]
random_color = random.choice(color_list)
random_x = random.randint(100, 600)
print(f"random_x = {random_x}")
random_y = random.randint(100, 600)
print(f"random_y = {random_y}")
random_width = random.randint(5, 15)
random_height = random.randint(5, 20)
print(f"random_width = {random_width}, random_height = {random_height}")
font_list = ["Helvetica", "Garamond", "Frutiger", "Bodoni", "Times", "Futura"]
random_font = random.choice(font_list)
random_font_size = random.randint(10, 18)
print(f"Font = {random_font}, font_size = {random_font_size}")
text_list = ["!", "?", "/", "+", "=", "<", ">", "%", "&", "(", ")", "-", "|", ";", ":", "[", "]", "{", "}", "^",
"@", "#"]
random_text = random.choice(text_list)
print(f"Random text = {random_text}")
random_time = random.randint(3, 5)
print(f"Random time = {random_time}")
thread = threading.Thread(target=time_thread)
keep_counting = True
def btn_create():
global btn
btn = Button(root, text=random_text, font=(random_font, random_font_size), bg=random_color, width=random_width, command=clicked)
btn.place(x=random_x, y=random_y)
thread.start()
time_wait = random_time * 1000
root.after(time_wait, btn_create)
start(first_run)
root.mainloop()
run_game()
请帮助我。
您的
time_thread
假设0.01秒的睡眠总是需要0.01秒。这显然是错误的。睡眠仅保证您将等待至少 0.01 秒。例如,在 Windows 上,调度程序间隔约为 0.016 秒,因此您的睡眠时间永远不能少于该时间。您可以通过更改计时线程以仅拍摄之前和之后的时间来解决此问题。
def time_thread():
start = time.time()
while keep_counting:
time.sleep(0.01)
time_used_list.append(time.time()-start)
然而,即使这样也有一个巨大的缺陷。这里的问题是,由于 Python 的解释器锁,在
keep_counting
有机会运行之前,很可能将 time_thread
设置为 False,然后再次设置为 True,在这种情况下,它将继续运行。您最终会同时计算所有三个 time_threads。
你不需要那个循环。只需在显示按钮时捕获
starttime
,并在单击按钮时捕获结束时间。完全消除 keep_counting
和时间线。这根本不需要导入threading
。
所以:
def run_game():
root = Tk()
root.title("Reaction Time Game")
root.geometry("1400x1000")
root.configure(bg="orange")
starttime = 0
time_used_list = []
first_run = True
def clicked3():
root.destroy()
root2 = Tk()
...
def clicked():
global times_clicked, first_run
time_used_list.append(time.time()-starttime)
if times_clicked == 2:
clicked3()
...
random_time = random.randint(3, 5)
print(f"Random time = {random_time}")
def btn_create():
global btn
btn = Button(root, text=random_text, font=(random_font, random_font_size), bg=random_color, width=random_width, command=clicked)
btn.place(x=random_x, y=random_y)
starttime = time.time()
time_wait = random_time * 1000
root.after(time_wait, btn_create)
通常,不要使用线程来计算经过的时间。只需获取开始时间和结束时间并减去即可。
在 Python 中使用 time.sleep() 函数的睡眠时间不可能少于 0.01 秒,而在 Windows 上则为 0.016 秒。