为了账号安全,请及时绑定邮箱和手机立即绑定

在 Jupyter 中使用 @interact 装饰器时实现“重置”按钮

在 Jupyter 中使用 @interact 装饰器时实现“重置”按钮

喵喔喔 2023-08-15 17:06:12
我正在尝试做一个简单的按钮来将小部件“重置”为某些默认值。我@interact在 Jupyter Lab 环境中使用装饰器。问题是小部件标识符的值被复制到函数内用作浮点变量的相同标识符,因此我无法在这个新范围内再访问它们。这是一个简短的示例(不起作用):import numpy as npimport matplotlib.pyplot as pltfrom ipywidgets import interact, Button@interact(starts_at=(0, np.pi*0.9, np.pi*0.1), ends_at=(np.pi, 2*np.pi, np.pi*0.1))def plot_graph(starts_at=0, ends_at=2*np.pi):        def on_button_clicked(_):        # instructions when clicking the button (this cannot work)        starts_at = 0        ends_at = 2*np.pi            button = Button(description="Reset")    button.on_click(on_button_clicked)    display(button)        f = lambda x : sum(1/a*np.sin(a*x + np.pi/a) for a in range(1,6))    x = np.linspace(0, 2*np.pi, 1000)    plt.plot(x, f(x))    plt.xlim([starts_at, ends_at])有谁知道如何将对原始小部件对象的引用发送到装饰函数的范围?我还将接受实现重置这些滑块的按钮的简单方法。:-D编辑:更正文本流
查看完整描述

3 回答

?
月关宝盒

TA贡献1772条经验 获得超5个赞

要完成此操作,您必须使用更多手动interactive_output功能。该函数允许您预先创建小部件,然后将它们传入:


import ipywidgets as widgets

import numpy as np

import matplotlib.pyplot as plt


start_slider = widgets.FloatSlider(

                            val = 0,

                            min = 0,

                            max = np.pi*0.9,

                            step = np.pi*0.1,

                            description = 'Starts at'

                            )

end_slider = widgets.FloatSlider(

                            val = np.pi,

                            min = np.pi,

                            max = 2*np.pi,

                            step = np.pi*0.1,

                            description = 'Ends at'

                            )

def on_button_clicked(_):

    start_slider.value = 0

    end_slider.value = 2*np.pi


button = Button(description="Reset")

button.on_click(on_button_clicked)

def plot_graph(starts_at=0, ends_at=2*np.pi):

    f = lambda x : sum(1/a*np.sin(a*x + np.pi/a) for a in range(1,6))

    x = np.linspace(0, 2*np.pi, 1000)

    plt.plot(x, f(x))

    plt.xlim([starts_at, ends_at])


display(widgets.VBox([start_slider, end_slider, button]))

widgets.interactive_output(plot_graph, {'starts_at': start_slider, 'ends_at':end_slider})

然而,这将在您每次更新时完全重新生成情节,这可能会导致不稳定的体验。因此,您还可以重写它以使用 matplotlib 方法,就像.set_data在笔记本中使用交互式 matplotlib 后端一样。因此,如果您要使用ipympl,您可以按照此示例笔记本中的示例进行操作。

通过另一个图书馆

我编写了一个mpl-interactions库,以便更轻松地使用 ipywidgets 滑块控制 matplotlib 绘图。ipywidgets.interact它提供了一个类似于为您创建小部件的功能,但它的优点是专注于 matplotlib,因此您需要提供的只是数据。%matplotlib ipympl

import mpl_interactions.ipyplot as iplt

import matplotlib.pyplot as plt

import numpy as np

import ipywidgets as widgets


def plot_graph(starts_at=0, ends_at=2*np.pi):

    x = np.linspace(starts_at, ends_at, 1000)

    f = lambda x : sum(1/a*np.sin(a*x + np.pi/a) for a in range(1,6))

    return np.array([x, f(x)]).T



fig, ax = plt.subplots()

button = widgets.Button(description = 'reset')

display(button)

controls = iplt.plot(plot_graph, starts_at = (0, np.pi), ends_at = (np.pi, 2*np.pi), xlim='auto', parametric=True)

def on_click(event):

    for hbox in controls.controls.values():

        slider = hbox.children[0]

        slider.value = slider.min

button.on_click(on_click)


查看完整回答
反对 回复 2023-08-15
?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

我可以在不使用装饰器的情况下弄清楚它@interact(现在它正在工作),但我对最终结果不满意。所以,我仍然愿意为那些可以做出清晰/更简单的Python版本的人提供正确的答案状态。


无论如何,这是工作代码:


import numpy as np

import matplotlib.pyplot as plt

from ipywidgets import interact, Button, FloatSlider


def plot_graph(starts_at, ends_at):

    f = lambda x : sum(1/a*np.sin(a*x + np.pi/a) for a in range(1,6))

    x = np.linspace(0, 2*np.pi, 1000)

    plt.plot(x, f(x))

    plt.xlim([starts_at, ends_at])

    

starts_at = FloatSlider(min=0, max=np.pi*0.9, value=0, step=np.pi*0.1)

ends_at = FloatSlider(min=np.pi, max=2*np.pi, value=2*np.pi, step=np.pi*0.1)


def on_button_clicked(_):

    starts_at.value = 0

    ends_at.value = 2*np.pi


button = Button(description="Reset")

button.on_click(on_button_clicked)

display(button)

_ = interact(plot_graph, starts_at=starts_at, ends_at=ends_at)

编辑:从这一点开始的新方法================================


我选择 @Ianhi 答案作为正确的答案,因为他指出了在我的问题背景下要考虑的问题。谢谢!


不管怎样,我在这里发布了我正在使用的最终方案,该方案对于我的需求来说足够简单,并且我可以在所有交互中重复使用重置按钮:


# Preamble ----

import numpy as np

import matplotlib.pyplot as plt

from ipywidgets import interact, Button, FloatSlider


def reset_button(defaults={}):

    def on_button_clicked(_):

        for k, v in defaults.items(): 

            k.value = v

    button = Button(description='Reset')

    button.on_click(on_button_clicked)

    display(button)


# Code ----

slider1 = FloatSlider(min=0, max=np.pi*0.9, value=0, step=np.pi*0.1)

slider2 = FloatSlider(min=np.pi, max=2*np.pi, value=2*np.pi, step=np.pi*0.1)

reset_button(defaults={slider1: 0, slider2: 2*np.pi})


@interact(starts_at=slider1, ends_at=slider2)

def plot_graph(starts_at, ends_at):

    f = lambda x : sum(1/a*np.sin(a*x + np.pi/a) for a in range(1,6))

    x = np.linspace(0, 2*np.pi, 1000)

    plt.plot(x, f(x))

    plt.xlim([starts_at, ends_at])


查看完整回答
反对 回复 2023-08-15
?
素胚勾勒不出你

TA贡献1827条经验 获得超9个赞

您可以创建一个简单的装饰器,为每次使用添加一个重置按钮:


按照上面的答案重置按钮:


def reset_button(defaults={}):

    def on_button_clicked(_):

        for k, v in defaults.items(): 

            k.value = v

    button = widgets.Button(description='Reset')

    button.on_click(on_button_clicked)

    display(button)

装饰者:


def interact_plus_reset(**_widgets):

  default_vals = {wid:wid._trait_values['value'] for k, wid in _widgets.items()}

  reset_button(defaults=default_vals)

  def wrap(func):

    @wraps(func)

    @widgets.interact(**_widgets)

    def inner(*args, **kwargs):

      return func(*args, **kwargs)

    return inner

  return wrap

然后按如下方式使用它:


@interact_plus_reset(

  a = widgets.FloatSlider(min=1, max=2000, value=35, step=1), 

  b = widgets.FloatSlider(min=1, max=2000, value=1000, step=1), 

)

def run(a, b):

  print(a + b)


查看完整回答
反对 回复 2023-08-15
  • 3 回答
  • 0 关注
  • 137 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信