在Jupyter中使用@interact装饰器时,实现“重置”按钮 通过另一个图书馆

问题描述

我试图做一个简单的按钮将小部件“重置”为某些认值。我在Jupyter Lab环境中使用[assembly: ExportRenderer(typeof(App1.Scanner),typeof(App1.iOS.ScannerRenderer))] namespace App1.iOS { public class ScannerRenderer : VieWrenderer<Scanner,UIView> { private VNDocumentCameraviewController scannerController; protected override void OnElementChanged(ElementChangedEventArgs<Scanner> e) { base.OnElementChanged(e); if (this.Control == null) { this.scannerController = new VNDocumentCameraviewController(); this.SetNativeControl(this.scannerController.View); } } } } 装饰器。问题在于,窗口小部件标识符的值已复制到函数内部用作浮点变量的相同标识符,因此,在此新范围内,我无法再访问它们。这是一个简短的示例(不起作用):

    <form action="prova.PHP">
<input type="submit" class="button" name="indietro" value="<"/>
<input type="submit" class="button" name="avanti" value=">"/></form>


 <?PHP
$id = 0;
$db_host     = "localhost";
$db_username = "root";
$db_password = "";
$connection = MysqLi_connect($db_host,$db_username,$db_password);
$db = MysqLi_select_db($connection,"cherubini");
$sql = MysqLi_query($connection,"SELECT * FROM cherubini WHERE ID = $id+1 LIMIT 1;");
$w = array(200000);
while($row = MysqLi_fetch_array($sql)) {
  $w = $row['Lemma'];
}
  if ($_GET) {
       if (isset($_GET['indietro'])) {
       } elseif (isset($_GET['avanti'])) {
         $sql = MysqLi_query($connection,"SELECT * FROM cherubini WHERE ID = $id = $id + 1;");
         $w = array(200000);
         while($row = MysqLi_fetch_array($sql)) {
           $w = $row['Lemma'];
         }
        echo ($w); 
       }
   }

?>

有人知道如何向原始函数对象的引用发送到装饰函数的范围吗?我还将接受实现按钮以重置这些滑块的简单方法

:-D

编辑:更正了文字

解决方法

要实现此目的,您必须使用更多手动的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,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,则可以按照this example notebook中的示例进行操作。

通过另一个图书馆

我编写了一个库mpl-interactions,以使使用ipywidgets滑块控制matplotlib图更加容易。它提供了类似于ipywidgets.interact的功能,因为它可以为您创建小部件,但是它的优点是着眼于matplotlib,因此您只需提供数据即可。有关与ipywidgets here

的区别的更多信息
%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)
,

我可以使用@interact装饰器(现在可以正常使用)了,而无需 来解决这个问题,但是最终结果让我感到不满意。因此,我仍然愿意为可能对此做出清晰/轻松的pythonic版本的人提供正确的答案状态

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

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,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)

编辑:此点的新方法 ============================ p >

我选择@Ianhi答案是正确的,因为他指出了我遇到问题时要考虑的问题。谢谢!

无论如何,我在这里发布了我正在使用的最终方案,该方案足够满足我的需求,我可以在所有交互中重用我的重置按钮:

# Preamble ----
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact,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,step=np.pi*0.1)
slider2 = FloatSlider(min=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])
,

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

按照上述答案重置按钮:

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,value=1000,)
def run(a,b):
  print(a + b)