Пример #1
0
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": []
                        }})
Пример #2
0
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" {}>'
Пример #3
0
    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)
Пример #4
0
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__)
Пример #5
0
    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__())