我使用线程和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")
设置应用程序图标时,它会挂起。我正在使用python 2.7和tkinter。它没有应用程序图标。
如何解决这个问题?
答案 0 :(得分:2)
tkinter
不喜欢在主要线程以外的任何线程中运行。您正在启动两个后台线程。 self.foo
看起来很好 - 没有进行任何tkinter
来电。但self.get_user_input
调用tkinter
方法pack
,after
和mainloop
,这意味着您可以获得两个结果:首先,它会自动召唤Bryan Oakley ,谁会出现在这里,并告诉你,你不能这样做;第二,它会导致程序的未定义行为,包括零星的或不那么零星的挂起和崩溃。我不确切知道这可能与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)
。
答案 1 :(得分:0)
如前所述,从单独的线程调用任何类型的tkinter方法只会导致问题,但是可以使用.after
对其进行排序。
.after
不会直接导致tkinter更改它显示的任何内容,但会将事件排入其可以处理的主线程上。通过以较小的时间尺度调用.after
,例如100毫秒,并将其指向包含您想要进行的tkinter调用的方法,您可以安全地从主线程外部进行tkinter调用,该调用将立即执行在主线上。
如果我没有明确说明我的意思,我会使用您的图标更改行作为示例将代码放在下面。
def change_icon(self):
self.root.wm_iconbitmap(bitmap = "icon.ico")
然后您最初调用以更改图标的位置,将其更改为:
self.root.after(100, self.change_icon)