繁体   English   中英

Python:Tkinter中的“更改应用程序”图标挂起

[英]Python: Change Application icon in Tkinter hangs

我使用线程和Tkinter编写了一个GUI程序。我使用了线程,因为它一直在检查端口27上的Arduino输入。

 def main():
    t = Test()
    t.go()
    try:
        join_threads(t.threads)
    except KeyboardInterrupt:
        print "\nKeyboardInterrupt catched."
        print "Terminate main thread."
        print "If only daemonic threads are left, terminate whole program."

class Test(object):

    def __init__(self):
        self.running = True
        self.threads = []
        self.root=Tk()
        self.Rval = IntVar()
        self.Rval.set(2)
        self.root.title("RFID EM LOCK CONTROLLER")
        self.variable=StringVar()
        self.variable2=StringVar()
        self.var2=StringVar()
        self.var3=StringVar()
        self.i=0
        self.root.resizable(0,0)
        self.your_label=Label(self.root,textvariable=self.variable,width=40,height=5,bg="Black",fg="Green")
        self.lframe = Frame(self.root,width=300,height=200,padx=0)
        self.lframe.pack()
        self.root.wm_iconbitmap(bitmap = "icon.ico")

    def foo(self):
        ser=serial.Serial("COM27",9600)
        while(self.running):
            self.var2= ser.readline()
            v = self.var2[0:8];
            print v
            if self.Isexist(v):
                ser.write('A')
                self.var2="Valid Card\n"+"Card Number: "+v; 
            else:
                ser.write('B')
                self.var2="InValid Card\n"+"Card Number: "+v;
    def grid(self):
        self.your_label.pack()

    def update_label(self):
        self.i=self.i+1
        self.variable.set(str(self.var2))
        self.variable2.set(str(self.var2))
        self.root.after(20,self.update_label)                       
    def get_user_input(self):
        self.grid()
        self.root.after(20,self.update_label)
        self.root.mainloop()

    def go(self):
        t1 = threading.Thread(target=self.foo)
        t2 = threading.Thread(target=self.get_user_input)
        # Make threads daemonic, i.e. terminate them when main thread
        # terminates. From: http://stackoverflow.com/a/3788243/145400
        t1.daemon = True
        t2.daemon = True
        t1.start()
        t2.start()
        self.threads.append(t1)
        self.threads.append(t2)


def join_threads(threads):
    """
    Join threads in interruptable fashion.
    From http://stackoverflow.com/a/9790882/145400
    """
    for t in threads:
        while t.isAlive():
            t.join(5)

if __name__ == "__main__":
    main()

上面的代码的问题是,当我在Windows 8.1 prox64上使用self.root.wm_iconbitmap(bitmap = "icon.ico")设置应用程序图标时,它会挂起。 我正在tkinter中使用python 2.7。 没有应用图标的作品。

如何解决这个问题?

tkinter 喜欢在除主之外的任何线程来运行。 您正在启动两个后台线程。 self.foo看起来OK-doesn't作任何tkinter电话。 self.get_user_input调用tkinter方式packaftermainloop ,这意味着你可以期望两个结果:第一,它会自动召唤布莱恩·奥克利 ,谁就会出现在这里一团烟雾,并告诉你,你不能做到这一点; 其次,它将导致程序的不确定行为,包括偶发性或非偶发性的挂起和崩溃。 wm_iconbitmap知道这可能与wm_iconbitmap交互的方式,但是我知道未定义的行为可以wm_iconbitmap所有原因,并且任何包含tkinter调用的后台线程充其量都是定时炸弹。

您已经使用过的after方法实际上是避免tkinter应用程序中使用后台线程的一种好方法。 after的实际调用会立即返回,因此很容易从您的主线程使用它,然后该任务将以安全的tkinter管理方式安排在后台执行。

这是使GUI的主更新循环在后台发生的一种方法,可以替代调用self.root.mainloop() 只需定义此方法,然后在主线程中调用一次即可,它将立即返回(几乎),然后继续在后台运行:

def background_updateloop( self ):
    self.root.update()
    self.afterID_updateloop = self.root.after( 100, self.background_updateloop )

要停止它,请调用self.root.after_cancel( self.afterID_updateloop)

如前所述,从单独的线程调用任何种类的tkinter方法只会导致问题,但是可以使用.after进行排序。

.after不会直接导致tkinter更改其显示的内容,但会将事件排队在它可以处理的主线程上。 通过以较小的时间范围(例如100毫秒)调用.after并将其定向到包含要进行的tkinter调用的方法,您可以从主线程外部安全地进行tkinter调用,该调用几乎可以在主线程上立即执行线。

如果我没有清楚说明我的意思,我将以您的图标更改行为例,将代码放在下面。

def change_icon(self):
    self.root.wm_iconbitmap(bitmap = "icon.ico")

然后将您最初调用该图标的位置更改为:

self.root.after(100, self.change_icon)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM