PythonTkinter GUI从多个线程更新——它是线程安全的吗?一些示例代码
我一直在研究TKinter的一个问题,这是基于其他线程的数据更新GUI。正如许多人在网上建议的那样,我使用self.root.after()方法使用队列轮询策略 这工作得很好,但我有一个问题,我需要更新TKinter GUI中嵌入的matplotlib绘图,这样做会“停用”//将焦点从GUI的其他方面移开/可能会阻止GUI的其他方面。具体地说,如果我在TKinter条目中键入某些内容,并且matplotlib图形更新,则光标不再位于条目中,并且我的键入过程已中断 为了解决这个问题,我想我应该尝试在一个单独的线程中更新TKinter。现在,我在网上看到很多人声称TKinter不是线程安全的,但我也看到其他人说TKinter是线程安全的。我继续做了一个示例程序,它运行得很好:即使绘图更新,光标也可以保留在条目中。但我想知道的是,这个程序安全吗?或者它容易受到随机或可预测的失败的影响?我能做些什么来拥有线程安全的GUI 下面是我的示例代码:PythonTkinter GUI从多个线程更新——它是线程安全的吗?一些示例代码,python,multithreading,matplotlib,tkinter,thread-safety,Python,Multithreading,Matplotlib,Tkinter,Thread Safety,我一直在研究TKinter的一个问题,这是基于其他线程的数据更新GUI。正如许多人在网上建议的那样,我使用self.root.after()方法使用队列轮询策略 这工作得很好,但我有一个问题,我需要更新TKinter GUI中嵌入的matplotlib绘图,这样做会“停用”//将焦点从GUI的其他方面移开/可能会阻止GUI的其他方面。具体地说,如果我在TKinter条目中键入某些内容,并且matplotlib图形更新,则光标不再位于条目中,并且我的键入过程已中断 为了解决这个问题,我想我应该尝试
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()
将Tkinter作为tk导入
导入线程
导入队列
导入日期时间
输入数学
导入matplotlib
matplotlib.use(“TkAgg”)
从matplotlib.backends.backend_tkagg导入图CAVASTKAGG,导航工具栏2TKAGG
从matplotlib.figure导入图形
将matplotlib.gridspec导入为gridspec
gui_queue=queue.queue()
GUI_主题_颜色='浅蓝色'
##################################################
类MainGUI:
定义初始化(自,根):
self.root=根
self.root.title('带有线程更新的TKinter GUI')
self.root.minsize(宽度=800,高度=300)
self.root.config(background=GUI\u THEME\u 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\u框架,text='Button 1',command=self.button1\u命令)
self.button1.grid(行=0,列=0)
self.entry1=tk.Entry(master=self.big\u框架)
self.entry1.bind(“”,self.entry1\u事件)
self.entry1.grid(行=1,列=0)
def入口1_事件(自身、事件):
self.button1.config(text=str(self.entry1.get())
self.entry1.delete(0,tk.END)
def按钮1_命令(自身):
打印“单击按钮1”
类GUI操纵器(threading.Thread):
定义初始化(自):
threading.Thread.\uuuuu init\uuuuuu(自)
self.app=无
self.start\u time=datetime.datetime.utcnow()
self.sec_checker=1
self.num_循环=0
self.x_list=[]
self.y_list=[]
def运行(自):
打印“起始线程…”
while gui_queue.empty():#在这里等待,直到我们收到MainGUI实例
通过
self.app=gui_queue.get()
尽管如此:
diff_time=(datetime.datetime.utcnow()-self.start_time)。总秒数()
楼层差异时间=数学楼层(差异时间)
如果floor_diff_time>=self.sec_checker:#每秒配置按钮1文本
self.app.button1.config(text=str(楼层差异时间))
self.sec_checker+=1
self.plot_图()
def plot_图(自身):
self.num_循环+=1
self.x_list.append(self.num_循环)
self.y_list.append(math.sin(self.num_循环/5.0))
如果self.num_循环==1:
self.fig1=Figure(figsize=(12,6),facecolor=GUI\u THEME\u COLOR)
self.fig1.suptitle('图1',
fontsize=14,
fontwweight='bold')
self.gs=gridspec.gridspec(2,2)
self.plot1=self.fig1.add_子图(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(真)
如果self.num_循环>1:
self.plot1.cla()
self.plot1.plot(self.x\u列表、self.y\u列表)
如果self.num_循环==1:
self.canvas=FigureCanvasTkAgg(self.fig1,master=self.app.big_frame)
self.canvas.draw()
self.canvas.get_tk_widget().grid(行=2,列=0)
其他:
self.canvas.draw()
def main():
root=tk.tk()
app=MainGUI(根目录)
gui_queue.put(应用程序)
gui\u操纵器\u线程=gui操纵器()
gui\u操纵器\u thread.daemon=True
gui_操纵器_线程.start()
root.mainloop()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()
提前谢谢 可能重复的