Python 纺车功能并不总是退出
我有一个函数,它循环一个旋转的gif来显示一个正在运行的进程。当进程完成时,将使用不同的值(开/关)再次调用该函数,Python 纺车功能并不总是退出,python,python-3.x,text,tkinter,Python,Python 3.x,Text,Tkinter,我有一个函数,它循环一个旋转的gif来显示一个正在运行的进程。当进程完成时,将使用不同的值(开/关)再次调用该函数,标签变为✓. 如果我只有来自threadspawner()kick off的两个线程,那么它们会很好地完成,并将标签更改为✓. 如果我同时开始大量的线程(比如12个),那么线程就可以很好地结束了,但是一个或两个标签的旋转轮有时不会更改为一个✓. 请注意,在下面的示例中,我无法让加载程序停止并更改为✓. 也许这是我搞砸的一部分,但在我的代码中,它切换得很好 我应该以不同的方式表示装载
标签变为✓. 如果我只有来自threadspawner()
kick off的两个线程,那么它们会很好地完成,并将标签更改为✓. 如果我同时开始大量的线程(比如12个),那么线程就可以很好地结束了,但是一个或两个标签的旋转轮有时不会更改为一个✓.
请注意,在下面的示例中,我无法让加载程序停止并更改为✓. 也许这是我搞砸的一部分,但在我的代码中,它切换得很好
我应该以不同的方式表示装载轮还是以不同的方式调用装载机(“关闭”)
我尝试为每个线程使用一个全局变量,并在线程开始时将其设置为True,然后在线程结束时将其设置为off。这只有在我更新loader循环中的变量以获得新值时才起作用,这意味着我必须为每个线程创建一个不同的loader函数,这是大量额外的代码。我尝试使用while
循环而不是if
循环,但没有任何改变
我想对每个线程运行的纺车使用相同的加载器功能。如果这不是最好的方法,请告诉我正确的方向
from tkinter import *
import time
import threading
main = Tk()
def threadspawner():
global var1
global var2
print(var1)
if var1.get() == 2:
thread1 = threading.Thread(target=lambda: loader(lbl1, 'on')) #<-- starts the spinning wheel to show that something is happening
thread1.start()
time.sleep(1) #start doing stuff here
loader(lbl1, 'off') #<-- Turns off the spinning wheel
thread1.join()
if var2.get() == 2:
thread2 = threading.Thread(target=lambda: loader(lbl1, 'on')) #<-- starts the spinning wheel to show that something is happening
thread2.start()
time.sleep(2)
loader(lbl2, 'off')#<-- Turns off the spinning wheel
thread2.join()
var1 = IntVar()
chk1 = Checkbutton(main, text='process1', onvalue=2, offvalue=0, variable=var1)
chk1.grid(row=1)
lbl1 = Label(main, text='')
lbl1.grid(row=1, column=1)
var2 = IntVar()
chk2 = Checkbutton(main, text='process2', variable=var2)
chk2.grid(row=2)
lbl2 = Label(main, text='')
lbl2.grid(row=2, column=1)
strt = Button(main, text='Start', command=lambda: threading.Thread(target=lambda: threadspawner()).start())
strt.grid(columnspan=2)
def loader(label, switch):
global lbl1
global lbl2
#im using images for my program but you do not have them so I use keyboard entries below to represent
#img = PhotoImage(file='icons\\wheel.gif', format="gif -index 0")
#img1 = PhotoImage(file='icons\\wheel.gif', format="gif -index 1")
#img2 = PhotoImage(file='icons\\wheel.gif', format="gif -index 2")
if switch == 'off':
label['text'] = '✓'
return
if switch == 'on':
print('starting loop')
if switch == 'on':
label['text'] = '/'
time.sleep(.1)
if switch == 'on':
label['text'] = '-'
time.sleep(.1)
if switch == 'on':
label['text'] = '\\'
time.sleep(.1)
if switch == 'on':
loader(label, 'on')
main.mainloop()
从tkinter导入*
导入时间
导入线程
main=Tk()
def threadspawner():
全球var1
全球var2
打印(var1)
如果var1.get()==2:
thread1=threading.Thread(target=lambda:loader(lbl1,'on'))#您所做的操作的问题是switch
是一个局部变量,因此当loader
函数启动时,微调器启动,但当它停止时,传递给无限加载函数链的开关
参数仍然是'on'
,因此即使标签设置为'✓'代码>,立即重置文本以继续加载动画;当使用参数'off'
调用加载器函数时,开关
变量仅在该函数调用的本地范围内具有值'off'
一个快速而肮脏的修复方法是创建一个新的全局变量,该变量可用于永远停止加载程序函数自身调用。这有很多问题,例如一次只允许一个微调器
global_switch = 'off'
def loader(label, switch):
global lbl1, lbl2, global_switch
if not switch==None:
global_switch = switch
if global_switch == 'off':
....
if global_switch == 'on':
....
loader(label, None)
main.mainloop()
然而,我推荐使用类的更整洁的方法。(关于将其转换为使用图像而不是文本,有一些未经测试的评论建议)。另外,您不能在主线程之外安全地进行tkinter调用,因此,尽管这不是您所问的最初问题,但我使用了根的after
方法来代替线程和time.sleep()
s
以下是我建议的解决方案:
from tkinter import *
import time
import threading
main = Tk()
def threadspawner():
global var1
global var2
print(var1.get(),var2.get())
if var1.get() != 0:
lbl1.start() #<-- starts the spinning wheel to show that something is happening
time.sleep(1) #start doing stuff here
lbl1.stop() #<-- Turns off the spinning wheel
if var2.get() != 0:
lbl2.start() #<-- starts the spinning wheel to show that something is happening
time.sleep(2)
lbl2.stop() #<-- Turns off the spinning wheel
class Loader(Label):
positions_text = '/-\\'
#positions_imgs = [PhotoImage(file='icons\\wheel.gif', format="gif -index "+str(i)) for i in range(3)]
def __init__(self, parent, *args, **kwargs):
kwargs['text']=''
Label.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.runnng = False
self.pos = 0
def start(self):
self.running = True
self.parent.after(0,self.update)
def update(self):
self.config(text = self.positions_text[self.pos])
#self.config(image = self.positions_imgs[self.pos])
self.pos = (self.pos+1)%len(self.positions_text)
#self.pos = (self.pos+1)%len(self.positions_imgs)
if self.running:
self.parent.after(100,self.update)
else:
self.config(text = '✓')
def stop(self):
self.running = False
var1 = IntVar()
chk1 = Checkbutton(main, text='process1', onvalue=2, offvalue=0, variable=var1)
chk1.grid(row=1)
lbl1 = Loader(main) # create an instance of the Loader class
lbl1.grid(row=1, column=1)
var2 = IntVar()
chk2 = Checkbutton(main, text='process2', variable=var2)
chk2.grid(row=2)
lbl2 = Loader(main) # create an instance of the Loader class
lbl2.grid(row=2, column=1)
strt = Button(main, text='Start', command=lambda: threading.Thread(target=threadspawner).start())
strt.grid(columnspan=2)
main.mainloop()
从tkinter导入*
导入时间
导入线程
main=Tk()
def threadspawner():
全球var1
全球var2
打印(var1.get(),var2.get())
如果var1.get()!=0:
lbl1.start()#这里似乎没有任何东西可以保证您在打开微调器后将其关闭。唯一可靠的方法是在线程启动时使用变量“开”,在线程结束时使用变量“关”,然后在加载程序循环中更新变量吗?为什么要从单独的线程关闭和打开它?为什么要在等待另一个线程完成之前关闭微调器?不能从多个线程安全地访问Tkinter函数。使用线程执行冗长的任务是一个合理的解决方案,但您必须以某种方式安排代表它们的任何GUI更新发送到主线程(可能通过队列),并在那里执行。@user2357112我想在用于启动该线程的选中按钮旁边表示正在运行的线程。有一个纺车是我找到的最好的方法。为了简单起见,我尝试用loader函数控制每个线程的旋转轮。您建议我如何向GUI中的用户显示线程正在运行/完成?