From f66c46b51a7e8c5d7feab711eba921f79670cdbb Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Sat, 6 Apr 2024 13:53:32 +1100 Subject: [PATCH 01/11] wip --- docs/developers/architecture/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/developers/architecture/index.md b/docs/developers/architecture/index.md index 402a8fac9..a05c47dce 100644 --- a/docs/developers/architecture/index.md +++ b/docs/developers/architecture/index.md @@ -11,3 +11,4 @@ code base. For advanced napari usage documentation, see [](explanations). connected to Qt classes and Vispy classes. - [](app-model): Explains the napari application model, a declarative schema for keeping track of commands, menus and keybindings of the napari GUI. +- []() From d1891724c0a0235cd1ce31c9cc292dc563bbe8b5 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 16:40:24 +1000 Subject: [PATCH 02/11] add doc on magicgui type reg --- docs/_toc.yml | 1 + docs/developers/architecture/app_model.md | 2 + docs/developers/architecture/index.md | 4 +- .../architecture/magicgui_type_reg.md | 70 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 docs/developers/architecture/magicgui_type_reg.md diff --git a/docs/_toc.yml b/docs/_toc.yml index 99450b506..75e3da677 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -163,6 +163,7 @@ subtrees: - file: developers/architecture/dir_organization - file: developers/architecture/napari_models - file: developers/architecture/app_model + - file: developers/architecture/magicgui_type_reg - file: naps/index subtrees: - maxdepth: 1 diff --git a/docs/developers/architecture/app_model.md b/docs/developers/architecture/app_model.md index 0fc48d9ea..7d8669a6e 100644 --- a/docs/developers/architecture/app_model.md +++ b/docs/developers/architecture/app_model.md @@ -311,6 +311,8 @@ at runtime (see [issue 6600](https://github.com/napari/napari/issues/6600) for more on this). Note that it is likely that this pull request will be split into smaller pull requests for ease of review and better git history. +(app_model_dep_inj_result)= + ## Dependency injection and result processing Dependency injection allows to write functions using parameter type annotations, diff --git a/docs/developers/architecture/index.md b/docs/developers/architecture/index.md index a05c47dce..c3ac417a6 100644 --- a/docs/developers/architecture/index.md +++ b/docs/developers/architecture/index.md @@ -11,4 +11,6 @@ code base. For advanced napari usage documentation, see [](explanations). connected to Qt classes and Vispy classes. - [](app-model): Explains the napari application model, a declarative schema for keeping track of commands, menus and keybindings of the napari GUI. -- []() +- [](magicgui_type_registration): Explains how `magicgui` widgets are + automatically created, inputs updated and outputs added to the `Viewer` for + registered `napari` types. diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md new file mode 100644 index 000000000..8169735ba --- /dev/null +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -0,0 +1,70 @@ +(magicgui_type_registration)= + +# `magicgui` type registration + +[`magicgui`](https://pyapp-kit.github.io/magicgui/) uses +[type hints](https://peps.python.org/pep-0484/) to infer the appropriate widget type +for a given function parameter. It allows third party packages +(like `napari`) to [register](https://pyapp-kit.github.io/magicgui/type_map/#registering-support-for-custom-types) support for their types using +{func}`~magicgui.type_map.register_type`. `napari` registers +a number of types, additionally specifying, where appropriate: + +* widget type for a parameter type +* function for updating inputs for the widget +* return callbacks, for return types + +This enables `magicgui` widgets to be easily created via type hints. + +```{note} +This page provides implementation details on `napari`-specific type registration +in `magicgui` and is aimed at `napari` developers. + +For information about using `magicgui` (for users and plugin developers) see +[](creating-widgets). +``` + +## Registration details + +`napari` types are either registered via the +{func}`@register_type ` decorator when the are +defined or in +[`napari/types.py`](https://github.com/napari/napari/blob/main/napari/types.py). +For the full list of types registered, see [](magicgui-parameter-annotations). + +All 'layer' types provide a `choices` callable when registering. +This means that these types will create an input +{class}`~magicgui.widgets.bases.CategoricalWidget`, which will be updated via the +`choices` callable. The callable is either `get_layers_data` or `get_layers`. +These functions retrieve the closest parent `Viewer` of the native +{class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of +{class}`~napari.layers.Layer` or tuple of format ('layer name', `Data`). +This callable is set to `self.choices` of the +{class}`~magicgui.widgets.bases.CategoricalWidget` in its +{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method. +`napari` {meth}`~napari.qt.Window.add_dock_widget` connects +{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` +to layer events, so the {class}`~magicgui.widgets.bases.CategoricalWidget` is updated +whenever layers change. **Indeed, {meth}`~napari.qt.Window.add_dock_widget` will +connect any existing `reset_choices` attribute to layer events for all widgets, +not just `magicgui` widgets.** + +```{note} +`magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call +`reset_choices` on all subwidgets. +``` + +The 'layer' types also specify a `return_callback` function that adds the layer +to the closest parent `Viewer` of the native widget when a 'layer' type is a return +annotation. + +{class}`~napari.viewer.Viewer` differs from the Layer types. `napari` simply specifies +that the {class}`~napari.viewer.Viewer` be bound to a widget (technically a +hidden {class}`~magicgui.widgets.EmptyWidget`). The user will need to specify +the widget type for this annotation. + +```{important} +`magicgui` type registration allows `napari` to specify useful defaults for creating +widgets for specific types. It does not provide the the type object. +This is done via dependency injection by "providers" (see +[](app_model_dep_inj_result) for details). +``` From 06c1fe3998eed4a43e6f82707126b483513e90bd Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 16:43:27 +1000 Subject: [PATCH 03/11] typo --- docs/developers/architecture/magicgui_type_reg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index 8169735ba..5b79aabac 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -64,7 +64,7 @@ the widget type for this annotation. ```{important} `magicgui` type registration allows `napari` to specify useful defaults for creating -widgets for specific types. It does not provide the the type object. +widgets for specific types. It does not provide the type object. This is done via dependency injection by "providers" (see [](app_model_dep_inj_result) for details). ``` From 1533e8101a627a8fd7ebe8d58364c6e7ecec8cd0 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 16:54:23 +1000 Subject: [PATCH 04/11] wording --- .../architecture/magicgui_type_reg.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index 5b79aabac..3af1824d3 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -41,18 +41,17 @@ These functions retrieve the closest parent `Viewer` of the native This callable is set to `self.choices` of the {class}`~magicgui.widgets.bases.CategoricalWidget` in its {meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method. -`napari` {meth}`~napari.qt.Window.add_dock_widget` connects -{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` -to layer events, so the {class}`~magicgui.widgets.bases.CategoricalWidget` is updated -whenever layers change. **Indeed, {meth}`~napari.qt.Window.add_dock_widget` will +`napari` {meth}`~napari.qt.Window.add_dock_widget` checks if the dock widget has +a `reset_choices` attribute and if so, connects it to layer events. +Note that `magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call +`reset_choices` on all subwidgets. This means that when the dock widget is a +{class}`~magicgui.widgets.bases.ContainerWidget`, any +{class}`~magicgui.widgets.bases.CategoricalWidget` subwidgets are updated +whenever layers change. +**Indeed, {meth}`~napari.qt.Window.add_dock_widget` will connect any existing `reset_choices` attribute to layer events for all widgets, not just `magicgui` widgets.** -```{note} -`magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call -`reset_choices` on all subwidgets. -``` - The 'layer' types also specify a `return_callback` function that adds the layer to the closest parent `Viewer` of the native widget when a 'layer' type is a return annotation. From 26c4225e1c1c8c56ab90171a4e1a86f1342f94fd Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 19:39:33 +1000 Subject: [PATCH 05/11] wording --- .../architecture/magicgui_type_reg.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index 3af1824d3..65a88e756 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -9,9 +9,9 @@ for a given function parameter. It allows third party packages {func}`~magicgui.type_map.register_type`. `napari` registers a number of types, additionally specifying, where appropriate: -* widget type for a parameter type +* widget type for the parameter type * function for updating inputs for the widget -* return callbacks, for return types +* return callback, for return types This enables `magicgui` widgets to be easily created via type hints. @@ -26,19 +26,18 @@ For information about using `magicgui` (for users and plugin developers) see ## Registration details `napari` types are either registered via the -{func}`@register_type ` decorator when the are -defined or in +{func}`@register_type ` decorator when they are defined or in [`napari/types.py`](https://github.com/napari/napari/blob/main/napari/types.py). For the full list of types registered, see [](magicgui-parameter-annotations). All 'layer' types provide a `choices` callable when registering. -This means that these types will create an input -{class}`~magicgui.widgets.bases.CategoricalWidget`, which will be updated via the -`choices` callable. The callable is either `get_layers_data` or `get_layers`. +This means that annotating with these types will result in an input +{class}`~magicgui.widgets.bases.CategoricalWidget`, which will get updated via the +`choices` callable. This callable is either `get_layers_data` or `get_layers`. These functions retrieve the closest parent `Viewer` of the native {class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of {class}`~napari.layers.Layer` or tuple of format ('layer name', `Data`). -This callable is set to `self.choices` of the +This callable is set to the `choices` attribute of the {class}`~magicgui.widgets.bases.CategoricalWidget` in its {meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method. `napari` {meth}`~napari.qt.Window.add_dock_widget` checks if the dock widget has @@ -48,8 +47,8 @@ Note that `magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will cal {class}`~magicgui.widgets.bases.ContainerWidget`, any {class}`~magicgui.widgets.bases.CategoricalWidget` subwidgets are updated whenever layers change. -**Indeed, {meth}`~napari.qt.Window.add_dock_widget` will -connect any existing `reset_choices` attribute to layer events for all widgets, +**Note that {meth}`~napari.qt.Window.add_dock_widget` will +connect any existing `reset_choices` widget attribute to layer events for all widgets, not just `magicgui` widgets.** The 'layer' types also specify a `return_callback` function that adds the layer From 38b9a1fb22b942c1bd0a730086ffdcd944718fbf Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 19:44:58 +1000 Subject: [PATCH 06/11] wording --- docs/developers/architecture/magicgui_type_reg.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index 65a88e756..b4ae8355e 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -39,13 +39,14 @@ These functions retrieve the closest parent `Viewer` of the native {class}`~napari.layers.Layer` or tuple of format ('layer name', `Data`). This callable is set to the `choices` attribute of the {class}`~magicgui.widgets.bases.CategoricalWidget` in its -{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method. +{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method and thus +gets called via {class}`~magicgui.widgets.bases.CategoricalWidget`'s `choices` setter. `napari` {meth}`~napari.qt.Window.add_dock_widget` checks if the dock widget has a `reset_choices` attribute and if so, connects it to layer events. Note that `magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call `reset_choices` on all subwidgets. This means that when the dock widget is a -{class}`~magicgui.widgets.bases.ContainerWidget`, any -{class}`~magicgui.widgets.bases.CategoricalWidget` subwidgets are updated +{class}`~magicgui.widgets.bases.ContainerWidget`, any subwidgets (e.g., +{class}`~magicgui.widgets.bases.CategoricalWidget`s) are updated whenever layers change. **Note that {meth}`~napari.qt.Window.add_dock_widget` will connect any existing `reset_choices` widget attribute to layer events for all widgets, From f8202fa2013d774d28eeec21b3ccb9e8218246a3 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 9 Apr 2024 20:53:05 +1000 Subject: [PATCH 07/11] iter --- docs/developers/architecture/magicgui_type_reg.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index b4ae8355e..fb9977f24 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -62,8 +62,8 @@ hidden {class}`~magicgui.widgets.EmptyWidget`). The user will need to specify the widget type for this annotation. ```{important} -`magicgui` type registration allows `napari` to specify useful defaults for creating -widgets for specific types. It does not provide the type object. +`magicgui` type registration allows `napari` to specify useful defaults when creating +widgets for specific types. It does **not** provide the type object. This is done via dependency injection by "providers" (see [](app_model_dep_inj_result) for details). ``` From 6970f1986ccb641355f8981f9db8ec24add7d2e1 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Wed, 10 Apr 2024 10:16:23 +1000 Subject: [PATCH 08/11] wording --- docs/developers/architecture/magicgui_type_reg.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index fb9977f24..30c92e3bf 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -13,7 +13,7 @@ a number of types, additionally specifying, where appropriate: * function for updating inputs for the widget * return callback, for return types -This enables `magicgui` widgets to be easily created via type hints. +This enables `magicgui` widgets to be easily created via type annotations. ```{note} This page provides implementation details on `napari`-specific type registration @@ -31,7 +31,7 @@ For information about using `magicgui` (for users and plugin developers) see For the full list of types registered, see [](magicgui-parameter-annotations). All 'layer' types provide a `choices` callable when registering. -This means that annotating with these types will result in an input +This means that annotating with these types creates an {class}`~magicgui.widgets.bases.CategoricalWidget`, which will get updated via the `choices` callable. This callable is either `get_layers_data` or `get_layers`. These functions retrieve the closest parent `Viewer` of the native From 5b772a5564b7a4663185f9cde29d861d9ae74b41 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Wed, 10 Apr 2024 15:14:31 +1000 Subject: [PATCH 09/11] review --- docs/developers/architecture/index.md | 6 ++--- .../architecture/magicgui_type_reg.md | 26 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/developers/architecture/index.md b/docs/developers/architecture/index.md index c3ac417a6..d1fc09907 100644 --- a/docs/developers/architecture/index.md +++ b/docs/developers/architecture/index.md @@ -11,6 +11,6 @@ code base. For advanced napari usage documentation, see [](explanations). connected to Qt classes and Vispy classes. - [](app-model): Explains the napari application model, a declarative schema for keeping track of commands, menus and keybindings of the napari GUI. -- [](magicgui_type_registration): Explains how `magicgui` widgets are - automatically created, inputs updated and outputs added to the `Viewer` for - registered `napari` types. +- [](magicgui_type_registration): Explains how `magicgui` widgets declared by users + or plugins are automatically created, inputs updated and outputs added to the + `Viewer` for registered `napari` types. diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index 30c92e3bf..b88c0c29e 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -26,13 +26,15 @@ For information about using `magicgui` (for users and plugin developers) see ## Registration details `napari` types are either registered via the -{func}`@register_type ` decorator when they are defined or in +{func}`@register_type ` decorator when they are +defined or in [`napari/types.py`](https://github.com/napari/napari/blob/main/napari/types.py). For the full list of types registered, see [](magicgui-parameter-annotations). -All 'layer' types provide a `choices` callable when registering. -This means that annotating with these types creates an -{class}`~magicgui.widgets.bases.CategoricalWidget`, which will get updated via the +All 'layer' types provide a `choices` callable when they are registered with +`magicgui`. This means that annotating with these types creates an +{class}`~magicgui.widgets.bases.CategoricalWidget`, which will have a dropdown +selection will get updated via the `choices` callable. This callable is either `get_layers_data` or `get_layers`. These functions retrieve the closest parent `Viewer` of the native {class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of @@ -57,13 +59,13 @@ to the closest parent `Viewer` of the native widget when a 'layer' type is a ret annotation. {class}`~napari.viewer.Viewer` differs from the Layer types. `napari` simply specifies -that the {class}`~napari.viewer.Viewer` be bound to a widget (technically a -hidden {class}`~magicgui.widgets.EmptyWidget`). The user will need to specify -the widget type for this annotation. +that the closest parent {class}`~napari.viewer.Viewer` (technically a public proxy of +the {class}`~napari.viewer.Viewer` that prevents private attribute access) be bound to +the widget (technically it's bound to a hidden child +{class}`~magicgui.widgets.EmptyWidget`). This allows the {class}`~napari.viewer.Viewer` +to be used in the `magicgui` widget. -```{important} -`magicgui` type registration allows `napari` to specify useful defaults when creating -widgets for specific types. It does **not** provide the type object. -This is done via dependency injection by "providers" (see -[](app_model_dep_inj_result) for details). +```{note} +When widget is not a `magicgui` widget, the {class}`~napari.viewer.Viewer` is provided +via a wrapper `napari` adds around widget contributions. ``` From fa5e7abcca872d6a42fbf984cbcd75681a89160d Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Fri, 12 Apr 2024 16:01:48 +1000 Subject: [PATCH 10/11] review --- .../developers/architecture/magicgui_type_reg.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index b88c0c29e..ade72084d 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -31,14 +31,18 @@ defined or in [`napari/types.py`](https://github.com/napari/napari/blob/main/napari/types.py). For the full list of types registered, see [](magicgui-parameter-annotations). -All 'layer' types provide a `choices` callable when they are registered with -`magicgui`. This means that annotating with these types creates an +### Layer types + +The 'layer' types registered are {class}`~napari.layers.Layer` and its subclasses, and +`Data` types. +All these types provide a `choices` callable when they are registered with +`magicgui`. This means that annotating with these types creates a {class}`~magicgui.widgets.bases.CategoricalWidget`, which will have a dropdown -selection will get updated via the +selection whose options will be updated via the `choices` callable. This callable is either `get_layers_data` or `get_layers`. These functions retrieve the closest parent `Viewer` of the native {class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of -{class}`~napari.layers.Layer` or tuple of format ('layer name', `Data`). +{class}`~napari.layers.Layer` or tuple of format `('layer name', Data)`. This callable is set to the `choices` attribute of the {class}`~magicgui.widgets.bases.CategoricalWidget` in its {meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method and thus @@ -58,7 +62,9 @@ The 'layer' types also specify a `return_callback` function that adds the layer to the closest parent `Viewer` of the native widget when a 'layer' type is a return annotation. -{class}`~napari.viewer.Viewer` differs from the Layer types. `napari` simply specifies +### `Viewer` type + +{class}`~napari.viewer.Viewer` differs from the layer types. `napari` simply specifies that the closest parent {class}`~napari.viewer.Viewer` (technically a public proxy of the {class}`~napari.viewer.Viewer` that prevents private attribute access) be bound to the widget (technically it's bound to a hidden child From 00506696aa4fcb2113c01d90548a94aa0c008e5f Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Fri, 12 Apr 2024 16:52:01 +1000 Subject: [PATCH 11/11] review --- docs/developers/architecture/magicgui_type_reg.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/developers/architecture/magicgui_type_reg.md b/docs/developers/architecture/magicgui_type_reg.md index ade72084d..5a5d19ed2 100644 --- a/docs/developers/architecture/magicgui_type_reg.md +++ b/docs/developers/architecture/magicgui_type_reg.md @@ -39,7 +39,9 @@ All these types provide a `choices` callable when they are registered with `magicgui`. This means that annotating with these types creates a {class}`~magicgui.widgets.bases.CategoricalWidget`, which will have a dropdown selection whose options will be updated via the -`choices` callable. This callable is either `get_layers_data` or `get_layers`. +`choices` callable. + +This callable is either `get_layers_data` or `get_layers`. These functions retrieve the closest parent `Viewer` of the native {class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of {class}`~napari.layers.Layer` or tuple of format `('layer name', Data)`. @@ -49,6 +51,7 @@ This callable is set to the `choices` attribute of the gets called via {class}`~magicgui.widgets.bases.CategoricalWidget`'s `choices` setter. `napari` {meth}`~napari.qt.Window.add_dock_widget` checks if the dock widget has a `reset_choices` attribute and if so, connects it to layer events. + Note that `magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call `reset_choices` on all subwidgets. This means that when the dock widget is a {class}`~magicgui.widgets.bases.ContainerWidget`, any subwidgets (e.g.,