Source code for napari.plugins
import os
import sys
from inspect import isclass, signature
from types import FunctionType
from typing import (
TYPE_CHECKING,
Callable,
Dict,
List,
Sequence,
Tuple,
Type,
Union,
)
from warnings import warn
from magicgui import magicgui
from napari_plugin_engine import HookImplementation, PluginManager
from ..types import AugmentedWidget
from ..utils._appdirs import user_site_packages
from ..utils.misc import camel_to_spaces, running_as_bundled_app
from . import _builtins, hook_specifications
if sys.platform.startswith('linux') and running_as_bundled_app():
sys.path.append(user_site_packages())
if TYPE_CHECKING:
from magicgui.widgets import FunctionGui
from qtpy.QtWidgets import QWidget
# the main plugin manager instance for the `napari` plugin namespace.
plugin_manager = PluginManager('napari', discover_entry_point='napari.plugin')
with plugin_manager.discovery_blocked():
plugin_manager.add_hookspecs(hook_specifications)
plugin_manager.register(_builtins, name='builtins')
dock_widgets: Dict[
Tuple[str, str],
Tuple[Callable[..., Union['FunctionGui', 'QWidget']], dict],
] = dict()
function_widgets: Dict[Tuple[str, str], Callable] = dict()
[docs]def register_dock_widget(
args: Union[AugmentedWidget, List[AugmentedWidget]],
hookimpl: HookImplementation,
):
from qtpy.QtWidgets import QWidget
plugin_name = hookimpl.plugin_name
hook_name = '`napari_experimental_provide_dock_widget`'
for arg in args if isinstance(args, list) else [args]:
if isinstance(arg, tuple):
if not arg:
warn(
f'Plugin {plugin_name!r} provided an invalid tuple to '
f'{hook_name}. Skipping'
)
continue
_cls = arg[0]
kwargs = arg[1] if len(arg) > 1 else {}
else:
_cls, kwargs = (arg, {})
if not callable(_cls):
warn(
f'Plugin {plugin_name!r} provided a non-callable object '
f'(widget) to {hook_name}: {_cls!r}. Widget ignored.'
)
continue
if not isinstance(kwargs, dict):
warn(
f'Plugin {plugin_name!r} provided invalid kwargs '
f'to {hook_name} for class {_cls.__name__}. Widget ignored.'
)
continue
# Get widget name
name = str(kwargs.get('name', '')) or camel_to_spaces(_cls.__name__)
key = (plugin_name, name)
if key in dock_widgets:
warn(
"Plugin '{}' has already registered a dock widget '{}' "
'which has now been overwritten'.format(*key)
)
dock_widgets[key] = (_cls, kwargs)
_magicgui_sig = {
name
for name, p in signature(magicgui).parameters.items()
if p.kind is p.KEYWORD_ONLY
}
[docs]def register_function_widget(
args: Union[Callable, List[Callable]],
hookimpl: HookImplementation,
):
plugin_name = hookimpl.plugin_name
hook_name = '`napari_experimental_provide_function`'
for func in args if isinstance(args, list) else [args]:
if not isinstance(func, FunctionType):
msg = (
f'Plugin {plugin_name!r} provided a non-callable type to '
f'{hook_name}: {type(func)!r}. Function widget ignored.'
)
if isinstance(func, tuple):
msg += (
" To provide multiple function widgets please use "
"a LIST of callables"
)
warn(msg)
continue
# Get function name
name = func.__name__.replace('_', ' ')
key = (plugin_name, name)
if key in function_widgets:
warn(
"Plugin '{}' has already registered a function widget '{}' "
'which has now been overwritten'.format(*key)
)
function_widgets[key] = func
[docs]def discover_dock_widgets():
"""Trigger discovery of dock_widgets plugins"""
dw_hook = plugin_manager.hook.napari_experimental_provide_dock_widget
dw_hook.call_historic(result_callback=register_dock_widget, with_impl=True)
fw_hook = plugin_manager.hook.napari_experimental_provide_function
fw_hook.call_historic(
result_callback=register_function_widget, with_impl=True
)
#: Template to use for namespacing a plugin item in the menu bar
menu_item_template = '{}: {}'
__all__ = ["PluginManager", "plugin_manager", 'menu_item_template']