def test_magic_signature_raises(): """Test that gui_options must have keys that are params in function.""" with pytest.raises(ValueError): magic_signature(_sample_func, gui_options={"not_a_param": { "choices": [] }})
def test_signature_to_container(): """Test that a MagicSignature can make a container.""" sig = magic_signature(_sample_func, gui_options={"a": {"widget_type": "Slider"}}) container = sig.to_container() assert len(container) == 2 assert repr(container) == "<Container (a: int = 0, b: str = 'hi')>" assert repr(container.a) == "Slider(value=0, annotation=<class 'int'>, name='a')" assert repr(sig.parameters["a"]) == '<MagicParameter "a: int" {}>'
def from_callable( cls, obj: Callable, gui_options: Optional[dict] = None, **kwargs ) -> Container: """Create a Container widget from a callable object. In most cases, it will be preferable to create a ``FunctionGui`` instead. """ return magic_signature(obj, gui_options=gui_options).to_container(**kwargs)
def test_none_defaults(): """Make sure that an unannotated parameter with default=None is ok.""" assert widgets.create_widget(value=None).value is None def func(arg=None): return 1 assert magicgui(func)() == 1 assert str(magic_signature(func)) == str(magicgui(func).__signature__)
def __init__( self, function: Callable[..., _R], call_button: bool | str = False, layout: str = "vertical", labels: bool = True, tooltips: bool = True, app: AppRef = None, visible: bool = False, auto_call: bool = False, result_widget: bool = False, param_options: dict[str, dict] | None = None, name: str = None, **kwargs, ): if not callable(function): raise TypeError( "'function' argument to FunctionGui must be callable.") # consume extra Widget keywords extra = set(kwargs) - {"annotation", "gui_only"} if extra: s = "s" if len(extra) > 1 else "" raise TypeError( f"FunctionGui got unexpected keyword argument{s}: {extra}") if param_options is None: param_options = {} elif not isinstance(param_options, dict) or not all( isinstance(x, dict) for x in param_options.values()): raise TypeError("'param_options' must be a dict of dicts") if tooltips: _inject_tooltips_from_docstrings(function.__doc__, param_options) self._function = function self.__wrapped__ = function # it's conceivable that function is not actually an instance of FunctionType # we can still support any generic callable, but we need to be careful not to # access attributes (like `__name__` that only function objects have). # Mypy doesn't seem catch this at this point: # https://github.com/python/mypy/issues/9934 self._callable_name = (getattr(function, "__name__", None) or f"{function.__module__}.{function.__class__}") sig = magic_signature(function, gui_options=param_options) super().__init__( layout=layout, labels=labels, visible=visible, widgets=list(sig.widgets(app).values()), return_annotation=sig.return_annotation, name=name or self._callable_name, ) self._param_options = param_options self.called = EventEmitter(self, type="called") self._result_name = "" self._call_count: int = 0 # a deque of Progressbars to be created by (possibly nested) tqdm_mgui iterators self._tqdm_pbars: Deque[ProgressBar] = deque() # the nesting level of tqdm_mgui iterators in a given __call__ self._tqdm_depth: int = 0 self._call_button: PushButton | None = None if call_button: text = call_button if isinstance(call_button, str) else "Run" self._call_button = PushButton(gui_only=True, text=text, name="call_button") if not auto_call: # (otherwise it already gets called) def _disable_button_and_call(val): # disable the call button until the function has finished self._call_button = cast(PushButton, self._call_button) self._call_button.enabled = False t, self._call_button.text = self._call_button.text, "Running..." try: self.__call__() finally: self._call_button.text = t self._call_button.enabled = True self._call_button.changed.connect(_disable_button_and_call) self.append(self._call_button) self._result_widget: LineEdit | None = None if result_widget: self._result_widget = LineEdit(gui_only=True, name="result") self._result_widget.enabled = False self.append(self._result_widget) self._auto_call = auto_call if auto_call: self.changed.connect(lambda e: self.__call__())