Python Tkinter: 通过回调事件更改窗口标题时应用程序卡顿
我正在用Tkinter创建一个简单的Python界面,想用self.title
来改变窗口的标题,当某个事件发生时。
如果我把这个事件绑定到一个按钮上,或者直接在Tk线程中调用事件处理函数,窗口标题就会按预期改变。但是,我想让这个事件由一个单独的线程来触发,结果发现,在事件处理函数中使用title
会导致应用程序卡住。
我在事件处理函数中做的其他事情(比如更新标签)都没问题,所以我猜这个事件是正常触发的。我试过用wm_title
代替title
,但没有看到什么不同。我查了一下title
的用法,发现没有什么奇怪的地方,只要用一个字符串来设置标题就可以了。
这里有一个简化的示例,能重现这个问题(我在WinXP上运行v2.7.1,供参考);这个应用程序运行正常10秒(可以移动窗口、调整大小等),然后Timer
生成事件后,应用程序就会冻结。
import Tkinter
import threading
class Gui(Tkinter.Tk):
def __init__(self, parent=None):
Tkinter.Tk.__init__(self, parent)
self.title('Original Title')
self.label = Tkinter.Label(self, text='Just a Label.',
width=30, anchor='center')
self.label.grid()
self.bind('<<change_title>>', self.change_title)
timer = threading.Timer(10, self.event_generate, ['<<change_title>>'])
timer.start()
def change_title(self, event=None):
self.title('New Title')
G = Gui(None)
G.mainloop()
3 个回答
0
我也试过在2.6.2版本(Windows系统)上,但标题没有变化。不过没有出现运行错误。
0
嗯,我觉得你的代码运行得很好。
不过,如果在十秒之前中断它,就会出现“RuntimeError: main thread is not in main loop”的错误提示。
我是在ubuntu 10.10上使用python 2.6.6。
希望这些信息对你有帮助。
2
我遇到了同样的问题,就是当从主线程以外的线程调用self.title()时,界面会卡住。Tkinter要求所有的界面操作都必须在同一个线程中进行,也就是主线程。
我的解决办法是让那个单独的线程把函数放到一个队列里。主线程会定期处理这个队列,利用Tkinter提供的after(ms)函数。下面是一个用你代码的例子:
import Tkinter
import threading
from Queue import Queue, Empty
class Gui(Tkinter.Tk):
def __init__(self, parent=None):
Tkinter.Tk.__init__(self, parent)
self.ui_queue = Queue()
self._handle_ui_request()
self.title('Original Title')
self.label = Tkinter.Label(self, text='Just a Label.',
width=30, anchor='center')
self.label.grid()
self.bind('<<change_title>>', self.change_title)
timer = threading.Timer(1, self.event_generate, ['<<change_title>>'])
timer.start()
def change_title(self, event=None):
# Separate the function name, it's args and keyword args,
# and put it in the queue as a tuple.
ui_function = (self.title, ('New Title',), {})
self.ui_queue.put(ui_function)
def _handle_ui_request(self):
'''
Periodically services the UI queue to handles UI requests in the main thread.
'''
try:
while True:
f, a, k = self.ui_queue.get_nowait()
f(*a, **k)
except Empty:
pass
self.after(200, self._handle_ui_request)
G = Gui(None)
G.mainloop()