如何在 ipywidgets 输出中嵌入 LiveGraph?

How to embed LiveGraph in ipywidgets Output?

通过使用 this answer to produce a LiveGraph and this answer 将变量更新到线程,我能够生成一个图表,该图表每秒更新一次,其幅度由滑块(下面的代码)确定。这两个答案都非常有帮助!

%matplotlib notebook
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from threading import Thread, Lock
import time
import ipywidgets as widgets
from IPython.display import display
import numpy as np
'''#################### Live Graph ####################''' 
# Create a new class which is a live graph
class LiveGraph(object):
    def __init__(self, baseline):
        self.x_data, self.y_data = [], []
        self.figure = plt.figure()
        self.line, = plt.plot(self.x_data, self.y_data)
        self.animation = FuncAnimation(self.figure, self.update, interval=1200)
        # define variable to be updated as a list
        self.baseline = [baseline]
        self.lock = Lock()
        self.th = Thread(target=self.thread_f, args = (self.baseline,), daemon=True)
        # start thread
        self.th.start()
    
    def update_baseline(self,baseline):
        # updating a list updates the thread argument
        with self.lock:
            self.baseline[0] = baseline
    
    # Updates animation
    def update(self, frame):
        self.line.set_data(self.x_data, self.y_data)
        self.figure.gca().relim()
        self.figure.gca().autoscale_view()
        return self.line,
    
    def show(self):
        plt.show()
    
    # Function called by thread that updates variables
    def thread_f(self, base):
        x = 0
        while True:
            self.x_data.append(x)
            x += 1
            self.y_data.append(base[0])    
            time.sleep(1)  
            
'''#################### Slider ####################'''            
# Function that updates baseline to slider value
def update_baseline(v):
    global g
    new_value = v['new']
    g.update_baseline(new_value)
    
slider = widgets.IntSlider(
    value=10,
    min=0,
    max=200,
    step=1,
    description='value:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='d'
)
slider.observe(update_baseline, names = 'value')

'''#################### Display ####################''' 

display(slider)
g = LiveGraph(slider.value)

不过,我还是想将图形放在具有其他小部件的更大界面中。看来我应该把LiveGraph放在Output widget里面,但是当我用下面的代码替换我代码的'Display section'时,没有显示数字。

out = widgets.Output(layout={'border': '1px solid black'})
with out:
    g = LiveGraph(slider.value)

vbox = widgets.VBox([slider,out], align_self='stretch',justify_content='center')       
vbox

有没有办法将此 LiveGraph 嵌入到输出小部件或 box widget 中?

我找到了一个解决方案,即完全避免使用 FuncAnimation 和 Output 小部件,同时将我的后端保持为内联。 从 matplotlib 更改为 bqplot 也很重要!

代码如下所示(要小心,因为它会不断增加列表)。

我尝试过的细节:

我在使用输出小部件时没有成功通过线程更新图形(尝试使用 [=12= 清除轴],重新绘制整个图 - 因为它是静态后端 - 并且还使用 clear_output() command)。 此外,ipywidgets 不允许将 matplotlib 图形直接放置在容器内,但如果它是 bqplot 图形则可以!

我希望这个答案能帮助任何试图将 ipywidgets 与在充满其他小部件的界面中不断更新自身的情节集成的人。

%matplotlib inline
import bqplot.pyplot as plt
from threading import Thread, Lock
import time
import ipywidgets as widgets
from IPython.display import display
import numpy as np

fig = plt.figure()
t, value = [], []
lines = plt.plot(x=t, y=value)

# Function that updates baseline to slider value
def update_baseline(v):
    global base, lock
    with lock:
        new_value = v['new']
        base = new_value
    
slider = widgets.IntSlider(
    value=10,
    min=0,
    max=200,
    step=1,
    description='value:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='d'
)
base = slider.value
slider.observe(update_baseline, names = 'value')

def thread_f():
    global t, value, base, lines
    x = 0
    while True:
        t.append(x)
        x += 1
        value.append(base)
        with lines.hold_sync():
            lines.x = t
            lines.y = value
        
        time.sleep(0.1)
        
lock = Lock()
th = Thread(target=thread_f, daemon=True)
# start thread
th.start()

vbox = widgets.VBox([slider,fig], align_self='stretch',justify_content='center')       
vbox

P.S。我是线程的新手,所以要小心,因为使用此代码可能无法正确停止线程。

bqplot==0.12.29
ipywidgets==7.6.3
numpy==1.20.2