maplotlib.widgets 与 FuncAnimation

maplotlib.widgets with FuncAnimation

我正在尝试使用 animation.FuncAnimation 实时绘制串行数据,并且我希望能够为控制器设置一个目标值。所以我想在图形上有一个可编辑的文本框,它由标记为 target 的行实时反映,但我不知道如何将文本框与 FuncAnimation 合并。当我将文本框代码放在循环函数之外时,它什么都不做(但绘图有效)但是当它在循环内时,文本框出现在图形的中间并且绘图不再有效。非常感谢任何帮助。

import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
import matplotlib.animation as animation
import time
import random


target = float(0.0)
def changeTarget(text):
    try:
        target = float(text)
    except ValueError:
        print(text + " is not a number")

def loop(i):
    t.append(time.time() - tic)
    data.append(random.random())
    target_data.append(target)

    ax.clear()
    ax.plot(t, data, label='data')
    ax.plot(t, target_data, label='target')
    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Data')
    ax.set_xlim([t[-1]-15, t[-1]+5])    
    ax.set_ylim([min(min(data), min(target_data))-5, max(max(data), max(target_data))+5])
    ax.legend()

    # This part causes problems
    text_box = TextBox(ax, 'Target:', initial="0")
    text_box.on_submit(changeTarget)
    plt.draw()

t = []
data = []
target_data = []

fig, ax = plt.subplots()
# text_box = TextBox(ax, 'Target:', initial="0")
# text_box.on_submit(changeTarget)
# plt.draw()

tic = time.time()
ani = animation.FuncAnimation(fig, loop, interval = 100)
plt.show()

编辑:删除随机数据上的 10 倍乘数表明绘图确实有效,但看起来 window 没有按预期更新,并且小部件仍然位于轴的顶部并且不可编辑。

我想出了一个使用 tkinter 来做到这一点的方法:

import tkinter as tk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import matplotlib.animation as animation
import time
import random

window = tk.Tk()

# Target entry box
frm_target = tk.Frame(master=window)
ent_humidity = tk.Entry(master=frm_target, width=3)
lbl_hum = tk.Label(master=frm_target, text="Target: ")
lbl_hum.grid(row=0, column=0, sticky="e")
ent_humidity.grid(row=0, column=1, sticky="w")
frm_target.pack()

# Set Target when user hits Enter
target = float(0.0)
def onEnterPressed(event):
    global target
    text = ent_humidity.get()
    try:
        target = float(text)
    except ValueError:
        print(text + " is not a number")    
window.bind('<Return>', onEnterPressed)

# Figure for plotting
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)

# "Data" collection logic
t = []
data = []
target_data = []
def loop(i):
    t.append(time.time() - tic)
    data.append(random.random() * 10)
    target_data.append(target)

    ax.clear()
    ax.plot(t, data, label='data')
    ax.plot(t, target_data, label='target')
    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Data')    
    ax.set_xlim([t[-1]-15, t[-1]+5])
    ax.set_ylim([min(min(data), min(target_data))-5, max(max(data), max(target_data))+5])
    ax.legend()


# GUI stuff for figure & toolbar
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

# Quit button
def _quit():
    window.quit()
    window.destroy()
button = tk.Button(master=window, text="Quit", command=_quit)
button.pack(side=tk.BOTTOM)

# Run app
tic = time.time()
ani = animation.FuncAnimation(fig, loop, interval=100) # interval in ms
tk.mainloop()