我想在Tkinter窗口上显示一个每秒更新的图(以及其他内容)。我只需要从数据矩阵中获得一条线并将其绘制,然后转到下一行,依此类推。
由于我需要一个“开始/停止”按钮,因此我正在使用threading
。
为此,我遵循了this post,基本上可以满足我的需求。
但是,一段时间后Python崩溃,Spyder显示此错误:
An error occurred while starting the kernel
Tcl_AsyncDelete: async handler deleted by the wrong thread
我尝试阅读有关此内容的信息,但是我并没有真正找到解决方案或解释。
这是示例代码:
import tkinter as tk
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
continuePlotting = False
line = 0
data = np.random.rand(100, 500)
def change_state():
global continuePlotting
if continuePlotting == True:
continuePlotting = False
else:
continuePlotting = True
def data_points():
global line
global data
l = line % len(data) - 1
r = data[l]
line = line+1
return r
def app():
root = tk.Tk()
root.configure(background='white')
# First Plot
top = tk.Frame(root)
top.pack(fill='both')
fig = Figure()
ax = fig.add_subplot(111)
graph = FigureCanvasTkAgg(fig, master=top)
graph.get_tk_widget().pack(fill='both')
def plotter():
while continuePlotting:
ax.cla()
dpts = data_points()
y = dpts[0:-1]
x = np.linspace(0,len(y),len(y))
ax.plot(x, y)
ax.grid(True)
graph.draw()
time.sleep(1)
def gui_handler():
change_state()
threading.Thread(target=plotter).start()
b = tk.Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
b.pack()
root.mainloop()
if __name__ == '__main__':
app()
任何帮助将不胜感激
答案 0 :(得分:1)
基本问题是您正在从非GUI线程调用Tk函数。不要那样做Tk并非设计为从随机线程调用。通用解决方案在此站点上被描述为对a question on tkinter thread communication的答案。简而言之,将计算出的数据推送到队列中并引发Tk事件,以使UI线程知道还有更多数据准备就绪。然后,事件处理程序可以从队列中获取新值,并使用它来做UI事情。
已附加是使用此机制的脚本的修改版本。
import tkinter as tk
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
from queue import Queue
DATA_READY_EVENT = '<<DataReadyEvent>>'
continuePlotting = False
line = 0
data = np.random.rand(100, 500)
def change_state():
global continuePlotting
if continuePlotting == True:
continuePlotting = False
else:
continuePlotting = True
def data_points():
global line
global data
l = line % len(data) - 1
r = data[l]
line = line+1
return r
def app():
root = tk.Tk()
root.configure(background='white')
queue = Queue()
# First Plot
top = tk.Frame(root)
top.pack(fill='both')
fig = Figure()
ax = fig.add_subplot(111)
graph = FigureCanvasTkAgg(fig, master=top)
graph.get_tk_widget().pack(fill='both')
def plot(ev):
x,y = queue.get()
ax.plot(x, y)
ax.grid(True)
graph.draw()
def plotter():
global continuePlotting
while continuePlotting:
ax.cla()
dpts = data_points()
y = dpts[0:-1]
x = np.linspace(0,len(y),len(y))
queue.put((x,y))
graph.get_tk_widget().event_generate(DATA_READY_EVENT)
time.sleep(1)
def gui_handler():
change_state()
threading.Thread(target=plotter).start()
graph.get_tk_widget().bind(DATA_READY_EVENT, plot)
b = tk.Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
b.pack()
root.mainloop()
if __name__ == '__main__':
app()