【问题标题】:Spinning wheel function does not always exit纺车功能并不总是退出
【发布时间】:2017-02-10 20:38:30
【问题描述】:

我有一个循环旋转轮 gif 以显示正在运行的进程的函数。当进程完成时,该函数会再次以不同的值(开/关)调用,Label 更改为 ✓。如果我只有来自threadspawner() 的几个线程启动,那么它们就可以很好地完成并将label 更改为✓。如果我同时启动很多线程(比如 12 个),那么线程完成得很好,但是一个或两个标签的纺车有时不会变成 ✓。

请注意,在下面的示例中,我无法让加载程序停止并更改为 ✓。也许这是我搞砸的一部分,但在我的代码中它切换得很好。

我应该以不同的方式表示负载轮还是以不同的方式调用 loader('off')?

我尝试为每个线程使用一个全局变量,并在线程启动时将其设置为 True,然后在线程结束时将其设置为关闭。这仅在我更新加载器循环中的变量以获取新值时才有效,这意味着我必须为每个线程创建一个不同的加载器函数,这是很多额外的代码。我尝试使用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()

【问题讨论】:

  • 这里似乎没有任何东西可以保证您在打开微调器之后关闭它。
  • 在线程启动时使用变量“on”,最后使用变量“off”,然后在加载程序循环中更新变量,这是唯一可靠的方法吗?
  • 你为什么要从不同的线程关闭和打开它?为什么要在等待其他线程完成之前关闭微调器
  • 您不能从多个线程安全地访问 Tkinter 函数。使用线程处理冗长的任务是一个合理的解决方案,但是您必须以某种方式安排代表它们的任何 GUI 更新被传递到主线程(也许通过队列),并在那里执行。
  • @user2357112 我想在用于启动该线程的 Checkbutton 旁边表示正在运行的线程。有一个纺车是我找到的最好的方式。为简单起见,我尝试使用加载器功能控制每个线程的纺车。您如何建议我在 GUI 中向用户显示线程正在运行/已完成?

标签: python python-3.x text tkinter


【解决方案1】:

你所做的问题是switch是一个局部变量,所以当loader函数启动时,微调器启动,但是当它停止时,switch参数被传递给加载器函数的无尽链仍然是'on',因此即使标签设置为'✓',文本也会立即重置以继续加载动画;当使用参数'off' 调用加载器函数时,switch 变量在该函数调用的本地范围内仅具有值'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 调用,因此虽然这不是您询问的原始问题,但我使用 rootafter 方法代替线程和 time.sleep()秒。

这是我推荐的解决方案:

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

注意:我还对代码做了一些其他的小改动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    • 2020-12-02
    • 1970-01-01
    相关资源
    最近更新 更多