Skip to content

Commit

Permalink
Move FunctionGui into widgets (#115)
Browse files Browse the repository at this point in the history
* improve type hints for magicgui, move function gui

* fix type

* fix docs references

* add deprecation warning
  • Loading branch information
tlambert03 committed Jan 20, 2021
1 parent b455a99 commit 770efe6
Show file tree
Hide file tree
Showing 18 changed files with 180 additions and 144 deletions.
1 change: 0 additions & 1 deletion docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
chapters:
- file: api/magicgui
- file: api/widgets
- file: api/function_gui
- file: api/bases
- file: api/protocols
- file: api/type_map
Expand Down
11 changes: 0 additions & 11 deletions docs/api/function_gui.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/api/magicgui.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The `@magicgui` decorator is one of the main exports from `magicgui`. It
inspects the signature of a callable object, and selects a widget type
appropriate for each parameter in the signature. It then returns a GUI (a
{class}`~magicgui.function_gui.FunctionGui` instance to be precise) wrapping all of the widgets. It can
{class}`~magicgui.widgets.FunctionGui` instance to be precise) wrapping all of the widgets. It can
be used to quickly generate an interactive graphical component that can control
and call the decorated function or method.

Expand Down
2 changes: 1 addition & 1 deletion docs/api/v0_2_0.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def function(x, y):
```python
>>> print(type(function))
<class 'magicgui.function_gui.FunctionGui'>
<class 'magicgui.widgets.FunctionGui'>
>>> print(type(function.native))
<class 'PyQt5.QtWidgets.QWidget'>
```
Expand Down
1 change: 1 addition & 0 deletions docs/api/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Container Widgets
:toctree: ../_autosummary
Container
FunctionGui
```
4 changes: 2 additions & 2 deletions docs/examples/napari/napari_img_math.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def image_arithmetic(layerA: Image, operation: Operation, layerB: Image):
```

That's it! The `image_arithmetic` function is now a
{class}`~magicgui.function_gui.FunctionGui` that can be shown, or incorporated
{class}`~magicgui.widgets.FunctionGui` that can be shown, or incorporated
into other GUIs (such as the napari GUI shown in this example)

```{note}
Expand Down Expand Up @@ -164,7 +164,7 @@ class Operation(enum.Enum):
### add it to napari

When we decorated the `image_arithmetic` function above, it became a
{class}`~magicgui.function_gui.FunctionGui`. Napari recognizes this type,
{class}`~magicgui.widgets.FunctionGui`. Napari recognizes this type,
so we can simply add it to the napari viewer as follows:

```python
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def snells_law(aoi=30.0, n1=Medium.Glass, n2=Medium.Water, degrees=True):

### et voilà

The object returned by the `magicgui` decorator is an instance of {py:class}`~magicgui.function_gui.FunctionGui`. It can still be called like the original function, but it also knows how to present itself as a GUI:
The object returned by the `magicgui` decorator is an instance of {py:class}`~magicgui.widgets.FunctionGui`. It can still be called like the original function, but it also knows how to present itself as a GUI:

```{code-cell} python
snells_law.show()
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ add.show()

```{tip}
The object returned from {func}`magicgui.magicgui` is a
{class}`~magicgui.function_gui.FunctionGui`, which is in turn just
{class}`~magicgui.widgets.FunctionGui`, which is in turn just
a special type of {class}`~magicgui.widgets.Container` widget. A `Container`
acts just like a basic python list. So in the example above, we could
manually add a {class}`~magicgui.widgets.Label` with "`+`" to our widget as
Expand Down
21 changes: 17 additions & 4 deletions magicgui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,29 @@
__email__ = "talley.lambert@gmail.com"


from . import widgets
from ._magicgui import magicgui
from .application import event_loop, use_app
from .function_gui import FunctionGui, magicgui
from .type_map import register_type

__all__ = [
"event_loop",
"FunctionGui",
"magicgui",
"register_type",
"use_app",
"widgets",
]


def __getattr__(name: str):
if name == "FunctionGui":
from warnings import warn

from .widgets import FunctionGui

warn(
"magicgui.FunctionGui is deprecated. "
"Please import at magicgui.widgets.FunctionGui",
DeprecationWarning,
stacklevel=2,
)

return FunctionGui
136 changes: 136 additions & 0 deletions magicgui/_magicgui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Callable, Optional, Union, overload
from warnings import warn

if TYPE_CHECKING:
from magicgui.application import AppRef
from magicgui.widgets import FunctionGui


@overload
def magicgui( # noqa
function: Callable,
*,
layout: str = "horizontal",
labels: bool = True,
tooltips: bool = True,
call_button: Union[bool, str] = False,
auto_call: bool = False,
result_widget: bool = False,
app: AppRef = None,
**param_options: dict,
) -> FunctionGui:
...


@overload
def magicgui( # noqa
function=None,
*,
layout: str = "horizontal",
labels: bool = True,
tooltips: bool = True,
call_button: Union[bool, str] = False,
auto_call: bool = False,
result_widget: bool = False,
app: AppRef = None,
**param_options: dict,
) -> Callable[[Callable], FunctionGui]:
...


def magicgui(
function: Optional[Callable] = None,
*,
layout: str = "horizontal",
labels: bool = True,
tooltips: bool = True,
call_button: Union[bool, str] = False,
auto_call: bool = False,
result_widget: bool = False,
app: AppRef = None,
**param_options: dict,
):
"""Return a :class:`FunctionGui` for ``function``.
Parameters
----------
function : Callable, optional
The function to decorate. Optional to allow bare decorator with optional
arguments. by default ``None``
layout : str, optional
The type of layout to use. Must be one of {'horizontal', 'vertical'}.
by default "horizontal".
labels : bool, optional
Whether labels are shown in the widget. by default True
tooltips : bool, optional
Whether tooltips are shown when hovering over widgets. by default True
call_button : bool or str, optional
If ``True``, create an additional button that calls the original function when
clicked. If a ``str``, set the button text. by default False
auto_call : bool, optional
If ``True``, changing any parameter in either the GUI or the widget attributes
will call the original function with the current settings. by default False
result_widget : bool, optional
Whether to display a LineEdit widget the output of the function when called,
by default False
app : magicgui.Application or str, optional
A backend to use, by default ``None`` (use the default backend.)
**param_options : dict of dict
Any additional keyword arguments will be used as parameter-specific options.
Keywords MUST match the name of one of the arguments in the function
signature, and the value MUST be a dict.
Returns
-------
result : FunctionGui or Callable[[F], FunctionGui]
If ``function`` is not ``None`` (such as when this is used as a bare decorator),
returns a FunctionGui instance, which is a list-like container of autogenerated
widgets corresponding to each parameter in the function.
If ``function`` is ``None`` such as when arguments are provided like
``magicgui(auto_call=True)``, then returns a function that can be used as a
decorator.
Examples
--------
>>> @magicgui
... def my_function(a: int = 1, b: str = 'hello'):
... pass
...
>>> my_function.show()
>>> my_function.a.value == 1 # True
>>> my_function.b.value = 'world'
"""
if "result" in param_options:
warn(
"\n\nThe 'result' option is deprecated and will be removed in the future."
"Please use `result_widget=True` instead.\n",
FutureWarning,
)

param_options.pop("result")
result_widget = True

def inner_func(func: Callable) -> FunctionGui:
from magicgui.widgets import FunctionGui

func_gui = FunctionGui(
function=func,
call_button=call_button,
layout=layout,
labels=labels,
tooltips=tooltips,
param_options=param_options,
auto_call=auto_call,
result_widget=result_widget,
app=app,
)
func_gui.__wrapped__ = func
return func_gui

if function is None:
return inner_func
else:
return inner_func(function)
4 changes: 2 additions & 2 deletions magicgui/type_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from typing_extensions import get_args, get_origin

from magicgui import function_gui, widgets
from magicgui import widgets
from magicgui.types import (
ReturnCallback,
TypeMatcher,
Expand Down Expand Up @@ -125,7 +125,7 @@ def callable_type(value, annotation) -> Optional[WidgetTuple]:
dtype = _normalize_type(value, annotation)

if dtype in (types.FunctionType,):
return function_gui.FunctionGui, {"function": value} # type: ignore
return widgets.FunctionGui, {"function": value} # type: ignore
return None


Expand Down
4 changes: 2 additions & 2 deletions magicgui/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing_extensions import TypedDict

if TYPE_CHECKING:
from magicgui.function_gui import FunctionGui
from magicgui.widgets import FunctionGui
from magicgui.widgets._bases import CategoricalWidget, Widget
from magicgui.widgets._protocols import WidgetProtocol

Expand All @@ -29,7 +29,7 @@
#: The set of all valid types for widget ``choices``.
ChoicesType = Union[EnumMeta, ChoicesIterable, ChoicesCallback, "ChoicesDict"]
#: A callback that may be registered for a given return annotation. When called, it will
#: be provided an instance of a :class:`~magicgui.function_gui.FunctionGui`, the result
#: be provided an instance of a :class:`~magicgui.widgets.FunctionGui`, the result
#: of the function that was called, and the return annotation itself.
ReturnCallback = Callable[["FunctionGui", Any, Type], None]
#: A valid file path type
Expand Down
2 changes: 2 additions & 0 deletions magicgui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
TextEdit,
TimeEdit,
)
from ._function_gui import FunctionGui
from ._table import Table

#: Aliases for compatibility with ipywidgets. (WIP)
Expand Down Expand Up @@ -67,6 +68,7 @@
"FileEdit",
"FloatSlider",
"FloatSpinBox",
"FunctionGui",
"Label",
"LineEdit",
"LiteralEvalLineEdit",
Expand Down
2 changes: 1 addition & 1 deletion magicgui/widgets/_bases/container_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ContainerWidget(Widget, _OrientationMixin, MutableSequence[Widget]):
For a ``ContainerWidget`` sublcass that is tightly coupled to a specific function
signature (as in the "classic" magicgui decorator), see
:class:`~magicgui.function_gui.FunctionGui`.
:class:`~magicgui.widgets.FunctionGui`.
Parameters
----------
Expand Down
Loading

0 comments on commit 770efe6

Please sign in to comment.