我正在尝试为我玩的旧游戏创建助手。这将显示您使用进度条施放的咒语所剩余的时间长度(与宏绑定。即,我拥有的D宏的咒语长度为3.7秒,如下所示)。我对编程有点陌生,所以我分阶段进行了此操作。我已经完成了该程序,以便它搜索我的宏并将有关Key事件的正确计时器发送给该函数。但是问题是咒语只是简单地排队而不是在再次按下新的宏时打断。
我试图在较短的实验脚本中解决此问题,因为我发现在学习时更容易做点位,然后再将它们组合起来。如您所见,我试图在单独的线程上运行计时器,以便可以在中途取消计时器(在完整程序中,这是在宏完成之前按下宏的时间)。
[从我在该站点上找到的示例中,我以为线程中的该全局变量可以工作,但是在取消func(x)并打印它已暂停之前,它仍然只是完成了func(x)的运行。对不起,如果我说的不够好-或说的太多。我第一次在这里发帖
下面的代码-预先感谢!
import time
import sys
from tkinter import *
from tkinter.ttk import *
root = Tk()
progress = Progressbar(root, orient = HORIZONTAL, length = 500, mode = 'determinate')
progress.pack()
# on key press check spells
global casting
def func(x):
global casting
while casting == True and progress['value']<100:
increments = 100/x
progress['value'] += increments
root.update_idletasks()
time.sleep(0.01)
else:
progress['value'] = 0
root.update_idletasks()
return
#def cancel():
# time.sleep(2)
## global casting
# casting = False
# print("Halted casting")
casting = True
t1 = threading.Thread(target=func(370))
t1.start()
time.sleep(0.9)
casting = False
print("Halted")
t1.join
###### New code updated
from tkinter import *
from tkinter.ttk import *
global casting
myMacros = {
"my_keys": {
"d": ["Flamestrike", 370],
"x": ["Poison", 150],
"q": ["Cure", 100],
"a": ["Lightning", 250],
"w": ["Greater Heal", 300],
"z": ["Magic Arrow", 100],
"e": ["Magic Reflection", 350]
}
}
def update_progressbar(x):
if casting is True and progress['value'] < 100:
increments = 100/x
progress['value'] += increments
# schedule this function to run again in 10ms
root.after(10, update_progressbar, x)
else:
progress['value'] = 0
def cancel():
global casting
casting = False
def cast(x):
global casting
casting = True
update_progressbar(x)
def key(event):
# Adding dynamic spellcasts grabbed from dictionary
if(event.char in myMacros["my_keys"]):
cancel()
# print(myMacros["my_keys"][event.char][0])
# print(myMacros["my_keys"][event.char][1])
cast(myMacros["my_keys"][event.char][1])
root = Tk()
progress = Progressbar(root, orient = HORIZONTAL, length = 500, mode = 'determinate')
#start_button = Button(root, text="Cast", command=cast)
#cancel_button = Button(root, text="Cancel", command=cancel)
progress.pack(side="top", fill="x")
#start_button.pack(side="left")
#cancel_button.pack(side="left")
root.bind("<Key>",key)
root.mainloop()
对于这种类型的问题,您不需要线程,也不应使用循环。 Tkinter已经运行了一个循环:mainloop
。 Tkinter不是线程安全的,线程是具有许多陷阱的高级主题。在这种情况下,线程添加的问题多于解决的问题。
相反,编写一个可以定期调用以更新进度条并使用after
安排该功能的功能。当您想在循环中运行一小段代码时,这是正确的技术,尽管它仅在要运行的代码花费的时间少于一秒钟的情况下才有效。就是这种情况-更新进度条只需要几毫秒。
该函数看起来像这样。请注意,它是如何进行一些工作的,然后计划自己在将来再次运行。 casting
变量设置为false或进度达到100时,循环将停止。
def update_progressbar(x):
global after_id
if casting and progress['value'] < 100:
increments = 100/x
progress['value'] += increments
# schedule this function to run again in 10ms
after_id = root.after(10, update_progressbar, x)
else:
progress['value'] = 0
然后您需要通过调用update_progressbar
来开始此操作:
update_progressbar(370)
这里是一个基于原始代码的完整工作示例。我添加了两个按钮来启动和取消进度条。
from tkinter import *s
from tkinter.ttk import *
global casting
global after_id
after_id = None
def update_progressbar(x):
global after_id
if casting and progress['value'] < 100:
increments = 100/x
progress['value'] += increments
# schedule this function to run again in 10ms
after_id = root.after(10, update_progressbar, x)
else:
progress['value'] = 0
def cancel():
global casting
casting = False
if after_id is not None:
root.after_cancel(after_id)
def cast():
global casting
casting = True
update_progressbar(370)
root = Tk()
progress = Progressbar(root, orient = HORIZONTAL, length = 500, mode = 'determinate')
start_button = Button(root, text="Cast", command=cast)
cancel_button = Button(root, text="Cancel", command=cancel)
progress.pack(side="top", fill="x")
start_button.pack(side="left")
cancel_button.pack(side="left")
root.mainloop()