napari-plugin-manager 0.1.6__py3-none-any.whl → 0.1.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- napari_plugin_manager/_tests/conftest.py +10 -10
- napari_plugin_manager/_tests/test_installer_process.py +123 -82
- napari_plugin_manager/_tests/test_npe2api.py +12 -11
- napari_plugin_manager/_tests/test_qt_plugin_dialog.py +91 -93
- napari_plugin_manager/_tests/test_utils.py +7 -7
- napari_plugin_manager/_version.py +16 -3
- napari_plugin_manager/base_qt_package_installer.py +144 -68
- napari_plugin_manager/base_qt_plugin_dialog.py +176 -157
- napari_plugin_manager/npe2api.py +3 -3
- napari_plugin_manager/qt_package_installer.py +44 -16
- napari_plugin_manager/qt_plugin_dialog.py +40 -30
- napari_plugin_manager/qt_warning_dialog.py +5 -3
- napari_plugin_manager/qt_widgets.py +3 -3
- napari_plugin_manager/styles.qss +30 -13
- napari_plugin_manager/utils.py +3 -3
- {napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/METADATA +8 -35
- napari_plugin_manager-0.1.8.dist-info/RECORD +23 -0
- {napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/WHEEL +1 -1
- napari_plugin_manager-0.1.6.dist-info/RECORD +0 -23
- {napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/licenses/LICENSE +0 -0
- {napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/top_level.txt +0 -0
napari_plugin_manager/npe2api.py
CHANGED
|
@@ -104,7 +104,7 @@ def iter_napari_plugin_info() -> Iterator[tuple[PackageMetadata, bool, dict]]:
|
|
|
104
104
|
info_copy.pop('display_name', None)
|
|
105
105
|
pypi_versions = info_copy.pop('pypi_versions')
|
|
106
106
|
conda_versions = info_copy.pop('conda_versions')
|
|
107
|
-
info_ = cast(_ShortSummaryDict, info_copy)
|
|
107
|
+
info_ = cast('_ShortSummaryDict', info_copy)
|
|
108
108
|
home_page = get_homepage_url(info_copy)
|
|
109
109
|
# this URL is used for the widget, so we have to replace the home_page
|
|
110
110
|
# link here as well as returning it in extra_info
|
|
@@ -122,12 +122,12 @@ def iter_napari_plugin_info() -> Iterator[tuple[PackageMetadata, bool, dict]]:
|
|
|
122
122
|
'conda_versions': conda_versions,
|
|
123
123
|
}
|
|
124
124
|
info_['name'] = normalized_name(info_['name'])
|
|
125
|
-
meta = PackageMetadata(**info_)
|
|
125
|
+
meta = PackageMetadata(**info_)
|
|
126
126
|
|
|
127
127
|
yield meta, (info_['name'] in conda_set), extra_info
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
def cache_clear():
|
|
130
|
+
def cache_clear() -> None:
|
|
131
131
|
"""Clear the cache for all cached functions in this module."""
|
|
132
132
|
plugin_summaries.cache_clear()
|
|
133
133
|
conda_map.cache_clear()
|
|
@@ -10,28 +10,30 @@ by a `deque` of `*InstallerTool` dataclasses (`NapariPipInstallerTool` and
|
|
|
10
10
|
import atexit
|
|
11
11
|
import os
|
|
12
12
|
import sys
|
|
13
|
-
from collections.abc import Sequence
|
|
14
13
|
from functools import lru_cache
|
|
15
14
|
from pathlib import Path
|
|
16
15
|
from tempfile import NamedTemporaryFile
|
|
17
16
|
|
|
18
|
-
from napari._version import
|
|
19
|
-
|
|
17
|
+
from napari._version import (
|
|
18
|
+
version as _napari_version,
|
|
19
|
+
version_tuple as _napari_version_tuple,
|
|
20
|
+
)
|
|
20
21
|
|
|
21
22
|
from napari_plugin_manager.base_qt_package_installer import (
|
|
22
23
|
CondaInstallerTool,
|
|
23
24
|
InstallerQueue,
|
|
24
25
|
PipInstallerTool,
|
|
26
|
+
UvInstallerTool,
|
|
25
27
|
)
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
def _get_python_exe():
|
|
30
|
+
def _get_python_exe() -> str:
|
|
29
31
|
# Note: is_bundled_app() returns False even if using a Briefcase bundle...
|
|
30
32
|
# Workaround: see if sys.executable is set to something something napari on Mac
|
|
31
33
|
if (
|
|
32
|
-
sys.executable.endswith(
|
|
34
|
+
sys.executable.endswith('napari')
|
|
33
35
|
and sys.platform == 'darwin'
|
|
34
|
-
and (python := Path(sys.prefix) /
|
|
36
|
+
and (python := Path(sys.prefix) / 'bin' / 'python3').is_file()
|
|
35
37
|
):
|
|
36
38
|
# sys.prefix should be <napari.app>/Contents/Resources/Support/Python/Resources
|
|
37
39
|
return str(python)
|
|
@@ -40,30 +42,52 @@ def _get_python_exe():
|
|
|
40
42
|
|
|
41
43
|
class NapariPipInstallerTool(PipInstallerTool):
|
|
42
44
|
@classmethod
|
|
43
|
-
def executable(cls):
|
|
45
|
+
def executable(cls) -> str:
|
|
44
46
|
return str(_get_python_exe())
|
|
45
47
|
|
|
46
48
|
@staticmethod
|
|
47
|
-
def constraints() ->
|
|
49
|
+
def constraints() -> list[str]:
|
|
48
50
|
"""
|
|
49
51
|
Version constraints to limit unwanted changes in installation.
|
|
50
52
|
"""
|
|
51
|
-
return [f
|
|
53
|
+
return [f'napari=={_napari_version}']
|
|
52
54
|
|
|
53
55
|
@classmethod
|
|
54
56
|
@lru_cache(maxsize=0)
|
|
55
57
|
def _constraints_file(cls) -> str:
|
|
56
58
|
with NamedTemporaryFile(
|
|
57
|
-
|
|
59
|
+
'w', suffix='-napari-constraints.txt', delete=False
|
|
58
60
|
) as f:
|
|
59
|
-
f.write(
|
|
61
|
+
f.write('\n'.join(cls.constraints()))
|
|
60
62
|
atexit.register(os.unlink, f.name)
|
|
61
63
|
return f.name
|
|
62
64
|
|
|
63
65
|
|
|
66
|
+
class NapariUvInstallerTool(UvInstallerTool):
|
|
67
|
+
@staticmethod
|
|
68
|
+
def constraints() -> list[str]:
|
|
69
|
+
"""
|
|
70
|
+
Version constraints to limit unwanted changes in installation.
|
|
71
|
+
"""
|
|
72
|
+
return [f'napari=={_napari_version}']
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
@lru_cache(maxsize=0)
|
|
76
|
+
def _constraints_file(cls) -> str:
|
|
77
|
+
with NamedTemporaryFile(
|
|
78
|
+
'w', suffix='-napari-constraints.txt', delete=False
|
|
79
|
+
) as f:
|
|
80
|
+
f.write('\n'.join(cls.constraints()))
|
|
81
|
+
atexit.register(os.unlink, f.name)
|
|
82
|
+
return f.name
|
|
83
|
+
|
|
84
|
+
def _python_executable(self) -> str:
|
|
85
|
+
return str(_get_python_exe())
|
|
86
|
+
|
|
87
|
+
|
|
64
88
|
class NapariCondaInstallerTool(CondaInstallerTool):
|
|
65
89
|
@staticmethod
|
|
66
|
-
def constraints() ->
|
|
90
|
+
def constraints() -> list[str]:
|
|
67
91
|
# FIXME
|
|
68
92
|
# dev or rc versions might not be available in public channels
|
|
69
93
|
# but only installed locally - if we try to pin those, mamba
|
|
@@ -71,14 +95,18 @@ class NapariCondaInstallerTool(CondaInstallerTool):
|
|
|
71
95
|
# in the remote index, only locally; to work around this bug
|
|
72
96
|
# we will have to pin to e.g. 0.4.* instead of 0.4.17.* for now
|
|
73
97
|
version_lower = _napari_version.lower()
|
|
74
|
-
is_dev =
|
|
98
|
+
is_dev = 'rc' in version_lower or 'dev' in version_lower
|
|
75
99
|
pin_level = 2 if is_dev else 3
|
|
76
|
-
version =
|
|
100
|
+
version = '.'.join([str(x) for x in _napari_version_tuple[:pin_level]])
|
|
77
101
|
|
|
78
|
-
return [f
|
|
102
|
+
return [f'napari={version}']
|
|
79
103
|
|
|
80
104
|
|
|
81
105
|
class NapariInstallerQueue(InstallerQueue):
|
|
82
|
-
|
|
106
|
+
PYPI_INSTALLER_TOOL_CLASS = (
|
|
107
|
+
NapariUvInstallerTool
|
|
108
|
+
if NapariUvInstallerTool.available()
|
|
109
|
+
else NapariPipInstallerTool
|
|
110
|
+
)
|
|
83
111
|
CONDA_INSTALLER_TOOL_CLASS = NapariCondaInstallerTool
|
|
84
112
|
BASE_PACKAGE_NAME = 'napari'
|
|
@@ -4,7 +4,10 @@ from pathlib import Path
|
|
|
4
4
|
import napari.plugins
|
|
5
5
|
import napari.resources
|
|
6
6
|
import npe2
|
|
7
|
-
from napari._qt.qt_resources import
|
|
7
|
+
from napari._qt.qt_resources import (
|
|
8
|
+
QColoredSVGIcon,
|
|
9
|
+
get_stylesheet,
|
|
10
|
+
)
|
|
8
11
|
from napari._qt.qthreading import create_worker
|
|
9
12
|
from napari._qt.widgets.qt_tooltip import QtToolTipLabel
|
|
10
13
|
from napari.plugins.utils import normalized_name
|
|
@@ -50,35 +53,35 @@ class PluginListItem(BasePluginListItem):
|
|
|
50
53
|
"""An entry in the plugin dialog. This will include the package name, summary,
|
|
51
54
|
author, source, version, and buttons to update, install/uninstall, etc."""
|
|
52
55
|
|
|
53
|
-
BASE_PACKAGE_NAME = 'napari'
|
|
56
|
+
BASE_PACKAGE_NAME: str = 'napari'
|
|
54
57
|
|
|
55
|
-
def _warning_icon(self):
|
|
58
|
+
def _warning_icon(self) -> QColoredSVGIcon:
|
|
56
59
|
# TODO: This color should come from the theme but the theme needs
|
|
57
60
|
# to provide the right color. Default warning should be orange, not
|
|
58
61
|
# red. Code example:
|
|
59
62
|
# theme_name = get_settings().appearance.theme
|
|
60
63
|
# napari.utils.theme.get_theme(theme_name, as_dict=False).warning.as_hex()
|
|
61
|
-
return QColoredSVGIcon.from_resources(
|
|
62
|
-
color=
|
|
64
|
+
return QColoredSVGIcon.from_resources('warning').colored(
|
|
65
|
+
color='#E3B617'
|
|
63
66
|
)
|
|
64
67
|
|
|
65
|
-
def _collapsed_icon(self):
|
|
68
|
+
def _collapsed_icon(self) -> QColoredSVGIcon:
|
|
66
69
|
return QColoredSVGIcon.from_resources('right_arrow').colored(
|
|
67
70
|
color='white'
|
|
68
71
|
)
|
|
69
72
|
|
|
70
|
-
def _expanded_icon(self):
|
|
73
|
+
def _expanded_icon(self) -> QColoredSVGIcon:
|
|
71
74
|
return QColoredSVGIcon.from_resources('down_arrow').colored(
|
|
72
75
|
color='white'
|
|
73
76
|
)
|
|
74
77
|
|
|
75
|
-
def _warning_tooltip(self):
|
|
78
|
+
def _warning_tooltip(self) -> QtToolTipLabel:
|
|
76
79
|
return QtToolTipLabel(self)
|
|
77
80
|
|
|
78
|
-
def _trans(self, text, **kwargs):
|
|
81
|
+
def _trans(self, text, **kwargs) -> str:
|
|
79
82
|
return trans._(text, **kwargs)
|
|
80
83
|
|
|
81
|
-
def _handle_plugin_api_version(self, plugin_api_version):
|
|
84
|
+
def _handle_plugin_api_version(self, plugin_api_version: str) -> None:
|
|
82
85
|
if plugin_api_version in (None, 1):
|
|
83
86
|
return
|
|
84
87
|
|
|
@@ -93,7 +96,7 @@ class PluginListItem(BasePluginListItem):
|
|
|
93
96
|
)
|
|
94
97
|
self.set_status(icon.pixmap(20, 20), text)
|
|
95
98
|
|
|
96
|
-
def _on_enabled_checkbox(self, state: int):
|
|
99
|
+
def _on_enabled_checkbox(self, state: int) -> None:
|
|
97
100
|
"""Called with `state` when checkbox is clicked."""
|
|
98
101
|
enabled = bool(state)
|
|
99
102
|
plugin_name = self.plugin_name.text()
|
|
@@ -113,15 +116,17 @@ class PluginListItem(BasePluginListItem):
|
|
|
113
116
|
)
|
|
114
117
|
return
|
|
115
118
|
|
|
116
|
-
def _warn_pypi_install(self):
|
|
119
|
+
def _warn_pypi_install(self) -> bool:
|
|
117
120
|
return running_as_constructor_app() or is_conda_package(
|
|
118
121
|
'napari'
|
|
119
122
|
) # or True
|
|
120
123
|
|
|
121
|
-
def _action_validation(
|
|
124
|
+
def _action_validation(
|
|
125
|
+
self, tool: InstallerTools, action: InstallerActions
|
|
126
|
+
) -> bool:
|
|
122
127
|
global DISMISS_WARN_PYPI_INSTALL_DLG
|
|
123
128
|
if (
|
|
124
|
-
tool == InstallerTools.
|
|
129
|
+
tool == InstallerTools.PYPI
|
|
125
130
|
and action == InstallerActions.INSTALL
|
|
126
131
|
and self._warn_pypi_install()
|
|
127
132
|
and not DISMISS_WARN_PYPI_INSTALL_DLG
|
|
@@ -157,29 +162,34 @@ class PluginListItem(BasePluginListItem):
|
|
|
157
162
|
|
|
158
163
|
|
|
159
164
|
class QPluginList(BaseQPluginList):
|
|
160
|
-
|
|
161
165
|
PLUGIN_LIST_ITEM_CLASS = PluginListItem
|
|
162
166
|
|
|
163
|
-
def _trans(self, text, **kwargs):
|
|
167
|
+
def _trans(self, text: str, **kwargs) -> str:
|
|
164
168
|
return trans._(text, **kwargs)
|
|
165
169
|
|
|
166
170
|
|
|
167
171
|
class QtPluginDialog(BaseQtPluginDialog):
|
|
168
|
-
|
|
169
172
|
PACKAGE_METADATA_CLASS = npe2.PackageMetadata
|
|
170
173
|
PROJECT_INFO_VERSION_CLASS = ProjectInfoVersions
|
|
171
174
|
PLUGIN_LIST_CLASS = QPluginList
|
|
172
175
|
INSTALLER_QUEUE_CLASS = NapariInstallerQueue
|
|
173
176
|
BASE_PACKAGE_NAME = 'napari'
|
|
174
177
|
|
|
175
|
-
def _setup_theme_update(self):
|
|
176
|
-
get_settings()
|
|
178
|
+
def _setup_theme_update(self) -> None:
|
|
179
|
+
settings = get_settings()
|
|
180
|
+
settings.appearance.events.theme.connect(self._update_theme)
|
|
181
|
+
settings.appearance.events.font_size.connect(self._update_theme)
|
|
177
182
|
|
|
178
|
-
def _update_theme(self, event):
|
|
179
|
-
|
|
183
|
+
def _update_theme(self, event) -> None:
|
|
184
|
+
settings = get_settings()
|
|
185
|
+
theme = settings.appearance.theme
|
|
186
|
+
font_variable = {'font_size': f'{settings.appearance.font_size}pt'}
|
|
187
|
+
stylesheet = get_stylesheet(
|
|
188
|
+
theme, extra=[STYLES_PATH], extra_variables=font_variable
|
|
189
|
+
)
|
|
180
190
|
self.setStyleSheet(stylesheet)
|
|
181
191
|
|
|
182
|
-
def _add_installed(self, pkg_name=None):
|
|
192
|
+
def _add_installed(self, pkg_name: str | None = None) -> None:
|
|
183
193
|
use_npe2_adaptor = get_settings().plugins.use_npe2_adaptor
|
|
184
194
|
pm2 = npe2.PluginManager.instance()
|
|
185
195
|
pm2.discover(include_npe1=use_npe2_adaptor)
|
|
@@ -231,7 +241,7 @@ class QtPluginDialog(BaseQtPluginDialog):
|
|
|
231
241
|
self.installed_list.setCurrentItem(item)
|
|
232
242
|
break
|
|
233
243
|
|
|
234
|
-
def _fetch_available_plugins(self, clear_cache: bool = False):
|
|
244
|
+
def _fetch_available_plugins(self, clear_cache: bool = False) -> None:
|
|
235
245
|
settings = get_settings()
|
|
236
246
|
use_npe2_adaptor = settings.plugins.use_npe2_adaptor
|
|
237
247
|
|
|
@@ -249,26 +259,26 @@ class QtPluginDialog(BaseQtPluginDialog):
|
|
|
249
259
|
pm2 = npe2.PluginManager.instance()
|
|
250
260
|
pm2.discover(include_npe1=use_npe2_adaptor)
|
|
251
261
|
|
|
252
|
-
def _loading_gif(self):
|
|
253
|
-
load_gif = str(Path(napari.resources.__file__).parent /
|
|
262
|
+
def _loading_gif(self) -> QMovie:
|
|
263
|
+
load_gif = str(Path(napari.resources.__file__).parent / 'loading.gif')
|
|
254
264
|
mov = QMovie(load_gif)
|
|
255
265
|
mov.setScaledSize(QSize(18, 18))
|
|
256
266
|
return mov
|
|
257
267
|
|
|
258
|
-
def _on_bundle(self):
|
|
268
|
+
def _on_bundle(self) -> bool:
|
|
259
269
|
return running_as_constructor_app()
|
|
260
270
|
|
|
261
|
-
def _show_info(self, info):
|
|
271
|
+
def _show_info(self, info: str) -> None:
|
|
262
272
|
show_info(info)
|
|
263
273
|
|
|
264
|
-
def _show_warning(self, warning):
|
|
274
|
+
def _show_warning(self, warning: str) -> None:
|
|
265
275
|
show_warning(warning)
|
|
266
276
|
|
|
267
|
-
def _trans(self, text, **kwargs):
|
|
277
|
+
def _trans(self, text: str, **kwargs) -> str:
|
|
268
278
|
return trans._(text, **kwargs)
|
|
269
279
|
|
|
270
280
|
|
|
271
|
-
if __name__ ==
|
|
281
|
+
if __name__ == '__main__':
|
|
272
282
|
from qtpy.QtWidgets import QApplication
|
|
273
283
|
|
|
274
284
|
app = QApplication([])
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
|
|
1
3
|
from qtpy.QtWidgets import QDialog, QLabel, QPushButton, QVBoxLayout, QWidget
|
|
2
4
|
|
|
3
5
|
|
|
@@ -7,13 +9,13 @@ class RestartWarningDialog(QDialog):
|
|
|
7
9
|
self.setWindowTitle('Restart napari')
|
|
8
10
|
okay_btn = QPushButton('Okay')
|
|
9
11
|
self.restart_warning_text = """
|
|
10
|
-
Plugins have been
|
|
11
|
-
|
|
12
|
+
Plugins have been added/removed or updated. If you notice any issues
|
|
13
|
+
with plugin functionality, you may need to restart napari.
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
16
|
okay_btn.clicked.connect(self.accept)
|
|
15
17
|
|
|
16
18
|
layout = QVBoxLayout()
|
|
17
|
-
layout.addWidget(QLabel(self.restart_warning_text))
|
|
19
|
+
layout.addWidget(QLabel(textwrap.dedent(self.restart_warning_text)))
|
|
18
20
|
layout.addWidget(okay_btn)
|
|
19
21
|
self.setLayout(layout)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
from qtpy.QtCore import Signal
|
|
2
2
|
from qtpy.QtGui import QMouseEvent
|
|
3
|
-
from qtpy.QtWidgets import QLabel
|
|
3
|
+
from qtpy.QtWidgets import QLabel, QWidget
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class ClickableLabel(QLabel):
|
|
7
7
|
clicked = Signal()
|
|
8
8
|
|
|
9
|
-
def __init__(self, parent=None):
|
|
9
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
|
10
10
|
super().__init__(parent=parent)
|
|
11
11
|
|
|
12
|
-
def mouseReleaseEvent(self, event: QMouseEvent):
|
|
12
|
+
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
|
|
13
13
|
super().mouseReleaseEvent(event)
|
|
14
14
|
self.clicked.emit()
|
napari_plugin_manager/styles.qss
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ------------ Plugin Dialog ------------ */
|
|
2
|
-
/*
|
|
2
|
+
/*
|
|
3
3
|
|
|
4
4
|
To be able to use this custom qss, we must use the `get_current_stylesheet` helper that napari provides.
|
|
5
5
|
By using that function we can replace the variables that are used in this stylesheet, e.g. `foreground`.
|
|
@@ -11,15 +11,26 @@ from napari._qt.qt_resources import get_current_stylesheet
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
*/
|
|
14
|
+
|
|
15
|
+
QLineEdit {
|
|
16
|
+
border: 1px solid white;
|
|
17
|
+
font-size: {{ increase(font_size, 2) }};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
QLineEdit:focus {
|
|
21
|
+
border: 1px solid {{ current }};
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
QCollapsible#install_info_button {
|
|
15
25
|
background-color: {{ darken(foreground, 20) }};
|
|
16
26
|
color: {{ darken(text, 15) }};
|
|
27
|
+
font-size: {{ font_size }};
|
|
17
28
|
}
|
|
18
|
-
QWidget#info_widget {
|
|
29
|
+
QWidget#info_widget, QWidget#info_widget > QLabel {
|
|
19
30
|
background-color: {{ darken(foreground, 20) }};
|
|
20
31
|
margin: 0px;
|
|
21
32
|
padding: 0px;
|
|
22
|
-
font:
|
|
33
|
+
font-size: {{ font_size }};
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
QLabel#author_text {
|
|
@@ -30,6 +41,9 @@ QElidingLabel#summary_text {
|
|
|
30
41
|
color: {{ darken(text, 35) }};
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
QLabel#available_message {
|
|
45
|
+
background-color: {{ console }};
|
|
46
|
+
}
|
|
33
47
|
|
|
34
48
|
QLabel#install_choice {
|
|
35
49
|
background-color: {{ current }};
|
|
@@ -56,7 +70,7 @@ QWidget#install_choice_widget {
|
|
|
56
70
|
color: {{ darken(text, 35) }};
|
|
57
71
|
margin: 0px;
|
|
58
72
|
padding: 0px;
|
|
59
|
-
font:
|
|
73
|
+
font-size: {{ font_size }};
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
QPluginList {
|
|
@@ -82,19 +96,19 @@ PluginListItem QCheckBox::indicator:disabled {
|
|
|
82
96
|
image: url("theme_{{ id }}:/check_50.svg");
|
|
83
97
|
}
|
|
84
98
|
|
|
85
|
-
QPushButton#install_button {
|
|
99
|
+
QPushButton#install_button, QPushButton#refresh_button {
|
|
86
100
|
background-color: {{ current }}
|
|
87
101
|
}
|
|
88
102
|
|
|
89
|
-
QPushButton#install_button:hover {
|
|
103
|
+
QPushButton#install_button:hover, QPushButton#refresh_button:hover {
|
|
90
104
|
background-color: {{ lighten(current, 10) }}
|
|
91
105
|
}
|
|
92
106
|
|
|
93
|
-
QPushButton#install_button:pressed {
|
|
107
|
+
QPushButton#install_button:pressed, QPushButton#refresh_button:pressed {
|
|
94
108
|
background-color: {{ darken(current, 10) }}
|
|
95
109
|
}
|
|
96
110
|
|
|
97
|
-
QPushButton#install_button:disabled {
|
|
111
|
+
QPushButton#install_button:disabled, QPushButton#refresh_button:disabled {
|
|
98
112
|
background-color: {{ lighten(current, 20) }}
|
|
99
113
|
}
|
|
100
114
|
|
|
@@ -130,10 +144,6 @@ QPushButton#close_button:disabled {
|
|
|
130
144
|
background-color: {{ lighten(secondary, 10) }}
|
|
131
145
|
}
|
|
132
146
|
|
|
133
|
-
QPushButton#refresh_button:disabled {
|
|
134
|
-
background-color: {{ lighten(secondary, 10) }}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
147
|
#small_text {
|
|
138
148
|
color: {{ opacity(text, 150) }};
|
|
139
149
|
font-size: {{ decrease(font_size, 2) }};
|
|
@@ -215,6 +225,13 @@ QtPluginDialog QSplitter{
|
|
|
215
225
|
padding-right: 2;
|
|
216
226
|
}
|
|
217
227
|
|
|
228
|
+
QtPluginDialog QSplitter::handle {
|
|
229
|
+
width: 4px;
|
|
230
|
+
height: 4px;
|
|
231
|
+
border: none;
|
|
232
|
+
background-color: {{ background }};
|
|
233
|
+
image: url("theme_{{ id }}:/horizontal_separator.svg");
|
|
234
|
+
}
|
|
218
235
|
|
|
219
236
|
QtPluginSorter {
|
|
220
237
|
padding: 20px;
|
|
@@ -355,7 +372,7 @@ QToolButton {
|
|
|
355
372
|
background-color: {{ darken(foreground, 20) }};
|
|
356
373
|
color: {{ darken(text, 15) }};
|
|
357
374
|
padding: 3 12px;
|
|
358
|
-
font:
|
|
375
|
+
font-size: {{ increase(font_size, 2) }};
|
|
359
376
|
}
|
|
360
377
|
|
|
361
378
|
QToolButton:hover {
|
napari_plugin_manager/utils.py
CHANGED
|
@@ -17,8 +17,8 @@ def is_conda_package(pkg: str, prefix: str | None = None) -> bool:
|
|
|
17
17
|
# saved within a ``conda-meta`` folder within the given environment of interest.
|
|
18
18
|
conda_meta_dir = Path(prefix or sys.prefix) / 'conda-meta'
|
|
19
19
|
return any(
|
|
20
|
-
re.match(rf
|
|
21
|
-
for p in conda_meta_dir.glob(f
|
|
20
|
+
re.match(rf'{pkg}-[^-]+-[^-]+.json', p.name)
|
|
21
|
+
for p in conda_meta_dir.glob(f'{pkg}-*-*.json')
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
|
|
@@ -29,7 +29,7 @@ def normalize_label(label: str) -> str:
|
|
|
29
29
|
https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization
|
|
30
30
|
"""
|
|
31
31
|
chars_to_remove = string.punctuation + string.whitespace
|
|
32
|
-
removal_map = str.maketrans(
|
|
32
|
+
removal_map = str.maketrans('', '', chars_to_remove)
|
|
33
33
|
return label.translate(removal_map).lower()
|
|
34
34
|
|
|
35
35
|
|
|
@@ -1,45 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: napari-plugin-manager
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Install plugins for napari, in napari.
|
|
5
5
|
Author-email: napari team <napari-steering-council@googlegroups.com>
|
|
6
|
-
License: BSD
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2018, Napari
|
|
9
|
-
All rights reserved.
|
|
10
|
-
|
|
11
|
-
Redistribution and use in source and binary forms, with or without
|
|
12
|
-
modification, are permitted provided that the following conditions are met:
|
|
13
|
-
|
|
14
|
-
* Redistributions of source code must retain the above copyright notice, this
|
|
15
|
-
list of conditions and the following disclaimer.
|
|
16
|
-
|
|
17
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
-
this list of conditions and the following disclaimer in the documentation
|
|
19
|
-
and/or other materials provided with the distribution.
|
|
20
|
-
|
|
21
|
-
* Neither the name of the copyright holder nor the names of its
|
|
22
|
-
contributors may be used to endorse or promote products derived from
|
|
23
|
-
this software without specific prior written permission.
|
|
24
|
-
|
|
25
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
-
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
36
7
|
Project-URL: homepage, https://github.com/napari/napari-plugin-manager
|
|
37
8
|
Classifier: Development Status :: 3 - Alpha
|
|
38
9
|
Classifier: Environment :: X11 Applications :: Qt
|
|
39
10
|
Classifier: Framework :: napari
|
|
40
11
|
Classifier: Intended Audience :: Education
|
|
41
12
|
Classifier: Intended Audience :: Science/Research
|
|
42
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
43
13
|
Classifier: Programming Language :: C
|
|
44
14
|
Classifier: Programming Language :: Python
|
|
45
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -67,11 +37,14 @@ Requires-Dist: packaging
|
|
|
67
37
|
Provides-Extra: dev
|
|
68
38
|
Requires-Dist: PyQt6; extra == "dev"
|
|
69
39
|
Requires-Dist: pre-commit; extra == "dev"
|
|
40
|
+
Provides-Extra: uv
|
|
41
|
+
Requires-Dist: uv; extra == "uv"
|
|
70
42
|
Provides-Extra: testing
|
|
43
|
+
Requires-Dist: napari-plugin-manager[uv]; extra == "testing"
|
|
71
44
|
Requires-Dist: coverage; extra == "testing"
|
|
72
45
|
Requires-Dist: flaky; extra == "testing"
|
|
73
46
|
Requires-Dist: pytest; extra == "testing"
|
|
74
|
-
Requires-Dist: pytest-qt; extra == "testing"
|
|
47
|
+
Requires-Dist: pytest-qt<4.5; extra == "testing"
|
|
75
48
|
Requires-Dist: virtualenv; extra == "testing"
|
|
76
49
|
Provides-Extra: docs
|
|
77
50
|
Requires-Dist: sphinx>6; extra == "docs"
|
|
@@ -102,7 +75,7 @@ The `napari-plugin-manager` used to be part of the [napari] codebase before the
|
|
|
102
75
|
series. It's now maintained as a separate project and package to allow uncoupled iterations outside
|
|
103
76
|
of the `napari` release cycle.
|
|
104
77
|
|
|
105
|
-
Future work will allow other applications with a plugin ecosytem to customize and
|
|
78
|
+
Future work will allow other applications with a plugin ecosytem to customize and
|
|
106
79
|
use the `plugin-manager`. This package remains under active development and contributions
|
|
107
80
|
are very welcome. Please [open an issue] to discuss potential improvements.
|
|
108
81
|
|
|
@@ -191,7 +164,7 @@ a restart to be properly configured.
|
|
|
191
164
|
|
|
192
165
|
### Installing a plugin via direct entry
|
|
193
166
|
|
|
194
|
-
You can also install a napari plugin or any other package via the direct entry option. The following steps
|
|
167
|
+
You can also install a napari plugin or any other package via the direct entry option. The following steps
|
|
195
168
|
correspond to the options and buttons located at the **bottom of the dialog**.
|
|
196
169
|
|
|
197
170
|
1. You can type either the name of the package, a url to the resource or drag and drop a compressed file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
napari_plugin_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
napari_plugin_manager/_version.py,sha256=Zaz3s9gl_rzsS46-ymJOALojMxviW77EJq_agE8knLk,704
|
|
3
|
+
napari_plugin_manager/base_qt_package_installer.py,sha256=rWsD1BvJL3RG9pBEJEaUH5ugGzg4sVgQVg0RHKnP6us,24487
|
|
4
|
+
napari_plugin_manager/base_qt_plugin_dialog.py,sha256=0NDzTN7Bxek5mr4DZDPJVddh1eDjIwZtUxeKFSCQToA,67432
|
|
5
|
+
napari_plugin_manager/npe2api.py,sha256=gAPOC-g0GWAwN71m0GimM0SW6YHI39XxSJm_qhq3-OY,4352
|
|
6
|
+
napari_plugin_manager/qt_package_installer.py,sha256=QFQDED3MKbQupKF7OjBPz70cK2zrgIP2gu4vdUVV8Tk,3511
|
|
7
|
+
napari_plugin_manager/qt_plugin_dialog.py,sha256=8GcaQuSWF5jZHcdn6j2Ye6liJ5yRDjtnuqenpVSyfkM,10208
|
|
8
|
+
napari_plugin_manager/qt_warning_dialog.py,sha256=DtPoFBn7zDfSmkdMokm1BcuoYasngkC0jwJok-oIuGk,727
|
|
9
|
+
napari_plugin_manager/qt_widgets.py,sha256=OatVQPuJFP5sgYRq_KiaynwXI3UfLGiTwBRlFy-b5mk,399
|
|
10
|
+
napari_plugin_manager/styles.qss,sha256=2oJBNiLT3YtqQP3avQGAOWcSSOZiFHGDsHtI5TjjxuA,7928
|
|
11
|
+
napari_plugin_manager/utils.py,sha256=vGvOndyiwc_JaSHVtXIc9z-dGsewo_vRb31U-26vy24,2247
|
|
12
|
+
napari_plugin_manager/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
napari_plugin_manager/_tests/conftest.py,sha256=Bm8u6hT5-mQXaxamke79tJGrU8BZA3V444YBLPivVr8,1885
|
|
14
|
+
napari_plugin_manager/_tests/test_base_installer_process.py,sha256=Cv-nBnUeNAX6pYUE1zs38I9vGtCE-ahBN4q-xcBH-pw,561
|
|
15
|
+
napari_plugin_manager/_tests/test_installer_process.py,sha256=CshbV0OGzmL0kus9_0AvrEOU_vleMdSmPPo0RbCizMw,12760
|
|
16
|
+
napari_plugin_manager/_tests/test_npe2api.py,sha256=ncBGWHrA5uZjmt6w4lVlx8D-n_3At1bfAZgkMRxK7Lg,1275
|
|
17
|
+
napari_plugin_manager/_tests/test_qt_plugin_dialog.py,sha256=4zGIfLomCCC-rTebkf6ggXd8Tedx6mi7GtmRv6IKTj4,21670
|
|
18
|
+
napari_plugin_manager/_tests/test_utils.py,sha256=rAlqp8y1MR3U4nWCN1jekbvP_avket3fVgEQwJDqpKk,1674
|
|
19
|
+
napari_plugin_manager-0.1.8.dist-info/licenses/LICENSE,sha256=8dAlKbOqTMYe9L-gL_kEx5Xr1Sd0AbaWQDUkpiOp3vI,1506
|
|
20
|
+
napari_plugin_manager-0.1.8.dist-info/METADATA,sha256=tlRDPoC294ZIMKgzCzHVtW2sy4CtgbVs_waCIOlz2dk,12159
|
|
21
|
+
napari_plugin_manager-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
napari_plugin_manager-0.1.8.dist-info/top_level.txt,sha256=pmCqLetuumhY1CKSuTZ5eQsitzazrSvc7V_mkugEPTY,22
|
|
23
|
+
napari_plugin_manager-0.1.8.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
napari_plugin_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
napari_plugin_manager/_version.py,sha256=ESbJO0YD7TYfOUv_WDIJJgWELGepEWsoyhqVifEcXPA,511
|
|
3
|
-
napari_plugin_manager/base_qt_package_installer.py,sha256=Nw6EIjZOmKVrJJCQrb1TjUMVeey4WIbib6hzBXMcSBc,21857
|
|
4
|
-
napari_plugin_manager/base_qt_plugin_dialog.py,sha256=nLqOJs_kUdGmmlTjaQRCu-EWB-4smwjbHlYpoSJ5jZs,66093
|
|
5
|
-
napari_plugin_manager/npe2api.py,sha256=KtfV-fspfADxSHMD_AoBaRt2WFkfeZ-W4aRl0SLyna8,4367
|
|
6
|
-
napari_plugin_manager/qt_package_installer.py,sha256=j-pacW6wHVq3iJaZXsj6D-_VH25Fz-55r7clcd_CQvE,2804
|
|
7
|
-
napari_plugin_manager/qt_plugin_dialog.py,sha256=Ig4TtjCh9Z2Dx_QpiOsfLlgolPJHmJ5_7VuTU2DE0Ag,9607
|
|
8
|
-
napari_plugin_manager/qt_warning_dialog.py,sha256=ue4CeMptlBBkBctPg7qCayamrkm75iLqASSFUvwo_Bc,669
|
|
9
|
-
napari_plugin_manager/qt_widgets.py,sha256=O8t5CbN8r_16cQzshyjvhTEYdUcj7OX0-bfYIiN2uSs,356
|
|
10
|
-
napari_plugin_manager/styles.qss,sha256=9ODPba2IorJybWObWoEO9VGq4AO0IYlAa8brN14tgZU,7379
|
|
11
|
-
napari_plugin_manager/utils.py,sha256=u0OSzNbSTyk7a-NdwGfrqry_q94GiHfKQnWB20r6V9A,2247
|
|
12
|
-
napari_plugin_manager/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
napari_plugin_manager/_tests/conftest.py,sha256=OvzenfBP2oIS6x8ksr9FhPXdsLV3Q_3Kzr6PRJe45Uc,1885
|
|
14
|
-
napari_plugin_manager/_tests/test_base_installer_process.py,sha256=Cv-nBnUeNAX6pYUE1zs38I9vGtCE-ahBN4q-xcBH-pw,561
|
|
15
|
-
napari_plugin_manager/_tests/test_installer_process.py,sha256=qPSDcYWPQ08gzM38av2tcE9XCtruHv-Mo6duQ0sZp-8,11614
|
|
16
|
-
napari_plugin_manager/_tests/test_npe2api.py,sha256=nf3UG68gqoCFyiPDisToZsLpZG0uvzGUUk8H6Fy3dqE,1248
|
|
17
|
-
napari_plugin_manager/_tests/test_qt_plugin_dialog.py,sha256=IRXodt3IrE7rXHZO0R69lhz4_2mMW3MOE9LLQ5tCxWY,21727
|
|
18
|
-
napari_plugin_manager/_tests/test_utils.py,sha256=I9rYJV_JVKg6GeBlhY4A_6aY1N5MbM7jgNz5wewiWIQ,1669
|
|
19
|
-
napari_plugin_manager-0.1.6.dist-info/licenses/LICENSE,sha256=8dAlKbOqTMYe9L-gL_kEx5Xr1Sd0AbaWQDUkpiOp3vI,1506
|
|
20
|
-
napari_plugin_manager-0.1.6.dist-info/METADATA,sha256=HQvATPh6tVJmaZBlNTG9BmG488v4ATn6OoposuwN4zE,13810
|
|
21
|
-
napari_plugin_manager-0.1.6.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
|
|
22
|
-
napari_plugin_manager-0.1.6.dist-info/top_level.txt,sha256=pmCqLetuumhY1CKSuTZ5eQsitzazrSvc7V_mkugEPTY,22
|
|
23
|
-
napari_plugin_manager-0.1.6.dist-info/RECORD,,
|
{napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{napari_plugin_manager-0.1.6.dist-info → napari_plugin_manager-0.1.8.dist-info}/top_level.txt
RENAMED
|
File without changes
|