the*_*010 5 python multithreading tkinter matplotlib thread-safety
我一直在研究 TKinter 遇到的这一问题,即根据其他线程的数据更新 GUI。正如许多人在网上建议的那样,我诉诸于使用队列轮询策略和self.root.after()方法。
这工作得很好,但我有一个问题,我需要更新嵌入在 TKinter GUI 中的 matplotlib 图,这样做会“停用”/将焦点从 GUI 的其他方面移开/可能会阻止GUI 的其他方面。具体来说,如果我在 TKinter 条目中输入内容并且 matplotlib 图更新,则光标不再位于条目中,并且我的输入过程已被中断。
为了解决这个问题,我想我应该尝试在一个单独的线程中更新 TKinter。现在,我在网上看到很多人声称TKinter不是线程安全的,但我也看到其他人说它是线程安全的。我继续制作了一个示例程序,它似乎运行得很好:即使绘图更新,光标也可以保留在条目中。但我想知道的是,这个程序安全吗?或者它是否容易出现随机或可预测的故障?我该怎么做才能拥有线程安全的 GUI?
这是我的示例代码:
import Tkinter as tk
import threading
import Queue
import datetime
import math
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.gridspec as gridspec
gui_queue = Queue.Queue()
GUI_THEME_COLOR = 'lightblue'
##################################################
class MainGUI:
def __init__(self, root):
self.root = root
self.root.title('TKinter GUI with Threaded Updates')
self.root.minsize(width=800, height=300)
self.root.config(background=GUI_THEME_COLOR)
self.big_frame = tk.Frame(master=root)
self.big_frame.pack(fill=tk.BOTH, expand=tk.YES, padx=10, pady=10)
self.button1 = tk.Button(master=self.big_frame, text='Button 1', command=self.button1_command)
self.button1.grid(row=0, column=0)
self.entry1 = tk.Entry(master=self.big_frame)
self.entry1.bind('<Return>', self.entry1_event)
self.entry1.grid(row=1, column=0)
def entry1_event(self, event):
self.button1.config(text=str(self.entry1.get()))
self.entry1.delete(0, tk.END)
def button1_command(self):
print 'Button 1 clicked'
class GUIManipulator(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.app = None
self.start_time = datetime.datetime.utcnow()
self.sec_checker = 1
self.num_loops = 0
self.x_list = []
self.y_list = []
def run(self):
print 'Starting GUIManipulator thread...'
while gui_queue.empty(): # Wait here until we receive the MainGUI instance
pass
self.app = gui_queue.get()
while True:
diff_time = (datetime.datetime.utcnow() - self.start_time).total_seconds()
floor_diff_time = math.floor(diff_time)
if floor_diff_time >= self.sec_checker: # Configure button1 text every second
self.app.button1.config(text=str(floor_diff_time))
self.sec_checker += 1
self.plot_figure()
def plot_figure(self):
self.num_loops += 1
self.x_list.append(self.num_loops)
self.y_list.append(math.sin(self.num_loops/5.0))
if self.num_loops == 1:
self.fig1 = Figure(figsize=(12, 6), facecolor=GUI_THEME_COLOR)
self.fig1.suptitle('Figure 1',
fontsize=14,
fontweight='bold')
self.gs = gridspec.GridSpec(2, 2)
self.plot1 = self.fig1.add_subplot(self.gs[:, 0])
self.plot1.set_title('Plot 1')
self.plot1.set_xlabel('x')
self.plot1.set_ylabel('sin(x)', labelpad=-10)
self.plot1.grid(True)
if self.num_loops > 1:
self.plot1.cla()
self.plot1.plot(self.x_list, self.y_list)
if self.num_loops == 1:
self.canvas = FigureCanvasTkAgg(self.fig1, master=self.app.big_frame)
self.canvas.draw()
self.canvas.get_tk_widget().grid(row=2, column=0)
else:
self.canvas.draw()
def main():
root = tk.Tk()
app = MainGUI(root)
gui_queue.put(app)
gui_manipulator_thread = GUIManipulator()
gui_manipulator_thread.daemon = True
gui_manipulator_thread.start()
root.mainloop()
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1622 次 |
| 最近记录: |