napari-file2folder 0.0.1__tar.gz

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.
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2024 Jules Vanaret
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+
4
+ recursive-exclude * __pycache__
5
+ recursive-exclude * *.py[co]
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.1
2
+ Name: napari-file2folder
3
+ Version: 0.0.1
4
+ Summary: Save multidimensional file as folder of tifs
5
+ Author: Jules Vanaret
6
+ Author-email: jules.vanaret@univ-amu.fr
7
+ License:
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2024 Jules Vanaret
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ THE SOFTWARE.
29
+
30
+ Classifier: Development Status :: 2 - Pre-Alpha
31
+ Classifier: Framework :: napari
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
43
+ Requires-Python: >=3.9
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Requires-Dist: numpy
47
+ Requires-Dist: qtpy
48
+ Requires-Dist: magicgui
49
+ Requires-Dist: tifffile
50
+ Requires-Dist: bioio
51
+ Requires-Dist: bioio-ome-tiff
52
+ Requires-Dist: bioio-ome-zarr
53
+ Requires-Dist: bioio-nd2
54
+ Requires-Dist: bioio-czi
55
+ Provides-Extra: testing
56
+ Requires-Dist: tox; extra == "testing"
57
+ Requires-Dist: pytest; extra == "testing"
58
+ Requires-Dist: pytest-cov; extra == "testing"
59
+ Requires-Dist: pytest-qt; extra == "testing"
60
+ Requires-Dist: napari; extra == "testing"
61
+ Requires-Dist: pyqt5; extra == "testing"
62
+
63
+ # napari-file2folder
64
+
65
+ [![License MIT](https://img.shields.io/pypi/l/napari-file2folder.svg?color=green)](https://github.com/jules-vanaret/napari-file2folder/raw/main/LICENSE)
66
+ [![PyPI](https://img.shields.io/pypi/v/napari-file2folder.svg?color=green)](https://pypi.org/project/napari-file2folder)
67
+ [![Python Version](https://img.shields.io/pypi/pyversions/napari-file2folder.svg?color=green)](https://python.org)
68
+ [![tests](https://github.com/jules-vanaret/napari-file2folder/workflows/tests/badge.svg)](https://github.com/jules-vanaret/napari-file2folder/actions)
69
+ [![codecov](https://codecov.io/gh/jules-vanaret/napari-file2folder/branch/main/graph/badge.svg)](https://codecov.io/gh/jules-vanaret/napari-file2folder)
70
+ [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-file2folder)](https://napari-hub.org/plugins/napari-file2folder)
71
+
72
+ <img src="https://github.com/GuignardLab/tapenade/blob/main/imgs/tapenade3.png" width="100">
73
+
74
+ A plugin to inspect bioimages (e.g. .tif, .czi, .nd2, .lsm...) and save them as individual .tif files in a folder.
75
+
76
+ `napari-file2folder` is a [napari] plugin that is part of the [Tapenade](https://github.com/GuignardLab/tapenade) project. Tapenade is a tool for the analysis of dense 3D tissues acquired with deep imaging microscopy. It is designed to be user-friendly and to provide a comprehensive analysis of the data.
77
+
78
+ If you use this plugin for your research, please [cite us](https://github.com/GuignardLab/tapenade/blob/main/README.md#how-to-cite).
79
+
80
+ ## Overview
81
+
82
+ <img src="imgs/napari-file2folder-demo.gif"/>
83
+
84
+ This plugin allows you to inspect (possibly large) bioimages by displaying their shape (number of elements in each dimension), and allowing you to save each element along a chosen dimension as a separate .tif file in a folder. This is useful when you have a large movie or stack of images and you want to save each frame or slice as a separate file. Optionally, the plugin allows the user to visualize the middle element of a given dimension to help the user decide which dimension to save as separate files.
85
+
86
+ The plugin currently supports the following file formats:
87
+ - .tif
88
+ - .ome.tiff
89
+ - .zarr
90
+ - .ome.zarr
91
+ - .nd2
92
+ - .lsm
93
+ - .czi
94
+
95
+ This plugin leverages [tifffile], [bioio], and [zarr] to circumvent loading the entire images in memory, which allows inspection of very large images.
96
+
97
+ > [!CAUTION]
98
+ > When inspecting the middle element of a dimension, or when saving one element of a dimension as a separate file, the plugin loads the element in memory, which means that at least this lone element must fit in memory.
99
+
100
+ ## Installation
101
+
102
+ The plugin obviously requires [napari] to run. If you don't have it yet, follow the instructions [here](https://napari.org/stable/tutorials/fundamentals/installation.html).
103
+
104
+ The simplest way to install `napari-file2folder` is via the [napari] plugin manager. Open Napari, go to `Plugins > Install/Uninstall Packages...` and search for `napari-file2folder`. Click on the install button and you are ready to go!
105
+
106
+ You can install `napari-file2folder` via [pip]:
107
+
108
+ pip install napari-file2folder
109
+
110
+
111
+
112
+
113
+ ## Contributing
114
+
115
+ Contributions are very welcome. Tests can be run with [tox], please ensure
116
+ the coverage at least stays the same before you submit a pull request.
117
+
118
+ ## License
119
+
120
+ Distributed under the terms of the [MIT] license,
121
+ "napari-file2folder" is free and open source software
122
+
123
+ ## Issues
124
+
125
+ If you encounter any problems, please [file an issue] along with a detailed description.
126
+
127
+ ----------------------------------
128
+
129
+ This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
130
+
131
+ <!--
132
+ Don't miss the full getting started guide to set up your new package:
133
+ https://github.com/napari/cookiecutter-napari-plugin#getting-started
134
+
135
+ and review the napari docs for plugin developers:
136
+ https://napari.org/stable/plugins/index.html
137
+ -->
138
+
139
+ [napari]: https://github.com/napari/napari
140
+ [Cookiecutter]: https://github.com/audreyr/cookiecutter
141
+ [@napari]: https://github.com/napari
142
+ [MIT]: http://opensource.org/licenses/MIT
143
+ [BSD-3]: http://opensource.org/licenses/BSD-3-Clause
144
+ [GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
145
+ [GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
146
+ [Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
147
+ [Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
148
+ [cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
149
+
150
+ [napari]: https://github.com/napari/napari
151
+ [tox]: https://tox.readthedocs.io/en/latest/
152
+ [pip]: https://pypi.org/project/pip/
153
+ [PyPI]: https://pypi.org/
154
+ [tifffile]: https://github.com/cgohlke/tifffile
155
+ [bioio]: https://github.com/bioio-devs/bioio
156
+ [zarr]: https://github.com/zarr-developers/zarr-python
@@ -0,0 +1,94 @@
1
+ # napari-file2folder
2
+
3
+ [![License MIT](https://img.shields.io/pypi/l/napari-file2folder.svg?color=green)](https://github.com/jules-vanaret/napari-file2folder/raw/main/LICENSE)
4
+ [![PyPI](https://img.shields.io/pypi/v/napari-file2folder.svg?color=green)](https://pypi.org/project/napari-file2folder)
5
+ [![Python Version](https://img.shields.io/pypi/pyversions/napari-file2folder.svg?color=green)](https://python.org)
6
+ [![tests](https://github.com/jules-vanaret/napari-file2folder/workflows/tests/badge.svg)](https://github.com/jules-vanaret/napari-file2folder/actions)
7
+ [![codecov](https://codecov.io/gh/jules-vanaret/napari-file2folder/branch/main/graph/badge.svg)](https://codecov.io/gh/jules-vanaret/napari-file2folder)
8
+ [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-file2folder)](https://napari-hub.org/plugins/napari-file2folder)
9
+
10
+ <img src="https://github.com/GuignardLab/tapenade/blob/main/imgs/tapenade3.png" width="100">
11
+
12
+ A plugin to inspect bioimages (e.g. .tif, .czi, .nd2, .lsm...) and save them as individual .tif files in a folder.
13
+
14
+ `napari-file2folder` is a [napari] plugin that is part of the [Tapenade](https://github.com/GuignardLab/tapenade) project. Tapenade is a tool for the analysis of dense 3D tissues acquired with deep imaging microscopy. It is designed to be user-friendly and to provide a comprehensive analysis of the data.
15
+
16
+ If you use this plugin for your research, please [cite us](https://github.com/GuignardLab/tapenade/blob/main/README.md#how-to-cite).
17
+
18
+ ## Overview
19
+
20
+ <img src="imgs/napari-file2folder-demo.gif"/>
21
+
22
+ This plugin allows you to inspect (possibly large) bioimages by displaying their shape (number of elements in each dimension), and allowing you to save each element along a chosen dimension as a separate .tif file in a folder. This is useful when you have a large movie or stack of images and you want to save each frame or slice as a separate file. Optionally, the plugin allows the user to visualize the middle element of a given dimension to help the user decide which dimension to save as separate files.
23
+
24
+ The plugin currently supports the following file formats:
25
+ - .tif
26
+ - .ome.tiff
27
+ - .zarr
28
+ - .ome.zarr
29
+ - .nd2
30
+ - .lsm
31
+ - .czi
32
+
33
+ This plugin leverages [tifffile], [bioio], and [zarr] to circumvent loading the entire images in memory, which allows inspection of very large images.
34
+
35
+ > [!CAUTION]
36
+ > When inspecting the middle element of a dimension, or when saving one element of a dimension as a separate file, the plugin loads the element in memory, which means that at least this lone element must fit in memory.
37
+
38
+ ## Installation
39
+
40
+ The plugin obviously requires [napari] to run. If you don't have it yet, follow the instructions [here](https://napari.org/stable/tutorials/fundamentals/installation.html).
41
+
42
+ The simplest way to install `napari-file2folder` is via the [napari] plugin manager. Open Napari, go to `Plugins > Install/Uninstall Packages...` and search for `napari-file2folder`. Click on the install button and you are ready to go!
43
+
44
+ You can install `napari-file2folder` via [pip]:
45
+
46
+ pip install napari-file2folder
47
+
48
+
49
+
50
+
51
+ ## Contributing
52
+
53
+ Contributions are very welcome. Tests can be run with [tox], please ensure
54
+ the coverage at least stays the same before you submit a pull request.
55
+
56
+ ## License
57
+
58
+ Distributed under the terms of the [MIT] license,
59
+ "napari-file2folder" is free and open source software
60
+
61
+ ## Issues
62
+
63
+ If you encounter any problems, please [file an issue] along with a detailed description.
64
+
65
+ ----------------------------------
66
+
67
+ This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
68
+
69
+ <!--
70
+ Don't miss the full getting started guide to set up your new package:
71
+ https://github.com/napari/cookiecutter-napari-plugin#getting-started
72
+
73
+ and review the napari docs for plugin developers:
74
+ https://napari.org/stable/plugins/index.html
75
+ -->
76
+
77
+ [napari]: https://github.com/napari/napari
78
+ [Cookiecutter]: https://github.com/audreyr/cookiecutter
79
+ [@napari]: https://github.com/napari
80
+ [MIT]: http://opensource.org/licenses/MIT
81
+ [BSD-3]: http://opensource.org/licenses/BSD-3-Clause
82
+ [GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
83
+ [GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
84
+ [Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
85
+ [Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
86
+ [cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
87
+
88
+ [napari]: https://github.com/napari/napari
89
+ [tox]: https://tox.readthedocs.io/en/latest/
90
+ [pip]: https://pypi.org/project/pip/
91
+ [PyPI]: https://pypi.org/
92
+ [tifffile]: https://github.com/cgohlke/tifffile
93
+ [bioio]: https://github.com/bioio-devs/bioio
94
+ [zarr]: https://github.com/zarr-developers/zarr-python
@@ -0,0 +1,119 @@
1
+ [project]
2
+ name = "napari-file2folder"
3
+ dynamic = ["version"]
4
+ description = "Save multidimensional file as folder of tifs"
5
+ readme = "README.md"
6
+ license = {file = "LICENSE"}
7
+ authors = [
8
+ {name = "Jules Vanaret"},
9
+ {email = "jules.vanaret@univ-amu.fr"},
10
+ ]
11
+ classifiers = [
12
+ "Development Status :: 2 - Pre-Alpha",
13
+ "Framework :: napari",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Image Processing",
25
+ ]
26
+ requires-python = ">=3.9"
27
+ dependencies = [
28
+ "numpy",
29
+ "qtpy",
30
+ "magicgui",
31
+ "tifffile",
32
+ "bioio",
33
+ "bioio-ome-tiff",
34
+ "bioio-ome-zarr",
35
+ "bioio-nd2",
36
+ "bioio-czi",
37
+ ]
38
+
39
+ [project.optional-dependencies]
40
+ testing = [
41
+ "tox",
42
+ "pytest", # https://docs.pytest.org/en/latest/contents.html
43
+ "pytest-cov", # https://pytest-cov.readthedocs.io/en/latest/
44
+ "pytest-qt", # https://pytest-qt.readthedocs.io/en/latest/
45
+ "napari",
46
+ "pyqt5",
47
+ ]
48
+
49
+ [project.entry-points."napari.manifest"]
50
+ napari-file2folder = "napari_file2folder:napari.yaml"
51
+
52
+
53
+
54
+ [build-system]
55
+ requires = ["setuptools>=42.0.0", "wheel"]
56
+ build-backend = "setuptools.build_meta"
57
+
58
+ [tool.setuptools]
59
+ include-package-data = true
60
+
61
+ [tool.setuptools.packages.find]
62
+ where = ["src"]
63
+
64
+ [tool.setuptools.package-data]
65
+ "*" = ["*.yaml"]
66
+
67
+
68
+ [tool.setuptools.dynamic]
69
+ version = {attr = "napari_file2folder.__init__.__version__"}
70
+
71
+ [tool.black]
72
+ line-length = 79
73
+ target-version = ['py38', 'py39', 'py310']
74
+
75
+ [tool.ruff]
76
+ line-length = 79
77
+ lint.select = [
78
+ "E", "F", "W", #flake8
79
+ "UP", # pyupgrade
80
+ "I", # isort
81
+ "BLE", # flake8-blind-exception
82
+ "B", # flake8-bugbear
83
+ "A", # flake8-builtins
84
+ "C4", # flake8-comprehensions
85
+ "ISC", # flake8-implicit-str-concat
86
+ "G", # flake8-logging-format
87
+ "PIE", # flake8-pie
88
+ "SIM", # flake8-simplify
89
+ ]
90
+ lint.ignore = [
91
+ "E501", # line too long. let black handle this
92
+ "UP006", "UP007", # type annotation. As using magicgui require runtime type annotation then we disable this.
93
+ "SIM117", # flake8-simplify - some of merged with statements are not looking great with black, reanble after drop python 3.9
94
+ ]
95
+
96
+ exclude = [
97
+ ".bzr",
98
+ ".direnv",
99
+ ".eggs",
100
+ ".git",
101
+ ".mypy_cache",
102
+ ".pants.d",
103
+ ".ruff_cache",
104
+ ".svn",
105
+ ".tox",
106
+ ".venv",
107
+ "__pypackages__",
108
+ "_build",
109
+ "buck-out",
110
+ "build",
111
+ "dist",
112
+ "node_modules",
113
+ "venv",
114
+ "*vendored*",
115
+ "*_vendor*",
116
+ ]
117
+
118
+ target-version = "py38"
119
+ fix = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ __version__ = "0.0.1"
2
+ from ._widget import File2FolderWidget
3
+
4
+ __all__ = (
5
+ "File2FolderWidget",
6
+ )
@@ -0,0 +1,95 @@
1
+ from qtpy.QtCore import QPoint, Qt, QRect
2
+ from qtpy.QtWidgets import (
3
+ QApplication,
4
+ QLabel,
5
+ QPushButton,
6
+ )
7
+ from qtpy import QtGui
8
+
9
+
10
+ class HoverTooltipButton(QPushButton):
11
+ def __init__(self, text, parent=None):
12
+ super().__init__(text, parent)
13
+ self.setFixedSize(20, 20) # Small square button
14
+ self.setText("?") # Add interrogation mark inside the button
15
+ self.setCheckable(True) # Toggle button to show/hide the tooltip
16
+
17
+ # Create the custom tooltip as a top-level widget (not a child of the button)
18
+ self.text_box = QLabel()
19
+ self.text_box.setText(text)
20
+ self.text_box.setStyleSheet(
21
+ "background-color: yellow; border: 1px solid black; padding: 5px;"
22
+ )
23
+ self.text_box.setWindowFlags(Qt.ToolTip) # Make it look like a tooltip
24
+ self.text_box.hide()
25
+
26
+ # Enable mouse tracking to track mouse movement inside the button
27
+ self.setMouseTracking(True)
28
+
29
+ def mousePressEvent(self, event):
30
+ if event.button() == Qt.LeftButton:
31
+ if self.isChecked():
32
+ self.setChecked(False) # Uncheck to hide the tooltip
33
+ self.text_box.hide()
34
+ else:
35
+ self.setChecked(True) # Check to show the tooltip
36
+ # move the tooltip to the mouse position
37
+ self.adjust_tooltip_position(event.globalPos())
38
+ self.text_box.show() # Immediately show tooltip on click
39
+ super().mousePressEvent(event)
40
+
41
+ def leaveEvent(self, event):
42
+ # Hide the text box when mouse leaves the button
43
+ self.text_box.hide()
44
+ self.setChecked(False) # Uncheck to hide the tooltip
45
+ super().leaveEvent(event)
46
+
47
+ def mouseMoveEvent(self, event):
48
+ # Update tooltip position to follow the mouse using global coordinates
49
+ if self.isChecked():
50
+ # self.text_box.move(event.globalPos() + QPoint(10, 10)) # Offset for better visibility
51
+ self.adjust_tooltip_position(event.globalPos())
52
+ super().mouseMoveEvent(event)
53
+
54
+ def adjust_tooltip_position(self, cursor_pos):
55
+ screen = QApplication.screenAt(cursor_pos)
56
+
57
+ if screen is not None:
58
+ screen_rect = (
59
+ screen.availableGeometry()
60
+ ) # Get the geometry of the screen with the cursor
61
+ else:
62
+ screen_rect = (
63
+ QApplication.desktop().availableGeometry()
64
+ ) # Fallback to primary screen
65
+
66
+ # Get the size of the tooltip
67
+ tooltip_size = self.text_box.sizeHint()
68
+
69
+ # Calculate the desired position of the tooltip
70
+ new_x = cursor_pos.x() + 10 # Offset for better visibility
71
+ new_y = cursor_pos.y() + 10
72
+
73
+ # Adjust the position if the tooltip goes beyond the screen's right edge
74
+ if new_x + tooltip_size.width() > screen_rect.right():
75
+ new_x = (
76
+ screen_rect.right() - tooltip_size.width() - 10
77
+ ) # Shift left
78
+
79
+ # Adjust the position if the tooltip goes beyond the screen's bottom edge
80
+ if new_y + tooltip_size.height() > screen_rect.bottom():
81
+ new_y = (
82
+ screen_rect.bottom() - tooltip_size.height() - 10
83
+ ) # Shift up
84
+
85
+ # Adjust the position if the tooltip goes beyond the screen's left edge
86
+ if new_x < screen_rect.left():
87
+ new_x = screen_rect.left() + 10 # Shift right
88
+
89
+ # Adjust the position if the tooltip goes beyond the screen's top edge
90
+ if new_y < screen_rect.top():
91
+ new_y = screen_rect.top() + 10 # Shift down
92
+
93
+ # Move the tooltip to the new adjusted position
94
+ self.text_box.move(QPoint(new_x, new_y))
95
+
@@ -0,0 +1,459 @@
1
+
2
+ from typing import TYPE_CHECKING
3
+
4
+ from qtpy.QtWidgets import QVBoxLayout, QPushButton, QWidget, QLabel, QProgressBar
5
+ from napari_file2folder._custom_widgets import HoverTooltipButton
6
+ from magicgui.widgets import (
7
+ CheckBox,
8
+ ComboBox,
9
+ Container,
10
+ EmptyWidget,
11
+ Label,
12
+ create_widget,
13
+ PushButton,
14
+ )
15
+ import os
16
+ import tifffile
17
+ from bioio import BioImage
18
+ import zarr
19
+
20
+ import napari
21
+
22
+
23
+ class File2FolderWidget(QWidget):
24
+
25
+ def __init__(self, viewer: "napari.viewer.Viewer"):
26
+ super().__init__()
27
+
28
+ self._viewer = viewer
29
+
30
+ self._array_file_path = create_widget(
31
+ widget_type="FileEdit",
32
+ label="Array file",
33
+ options={"mode": "r"},
34
+ )
35
+
36
+ self._array_file_path.changed.connect(self._update_dimensions)
37
+ self._array_file_path.changed.connect(self._update_dimension_choices)
38
+
39
+
40
+ self._refresh_button = create_widget(
41
+ widget_type="PushButton",
42
+ label="Refresh",
43
+ )
44
+
45
+ refresh_container = Container(
46
+ widgets=[self._refresh_button],
47
+ labels=False,
48
+ layout="horizontal",
49
+ )
50
+
51
+ self._add_tooltip_button_to_container(
52
+ refresh_container, "Refresh the dimensions"
53
+ )
54
+
55
+ self._refresh_button.native.clicked.connect(self._update_dimensions)
56
+
57
+ self._default_shape_text = f"Shape: <a style=color:#D41159;>None</a>"
58
+ self._shape_text = QLabel(self._default_shape_text)
59
+
60
+ self._dimension_choice_combo = create_widget(
61
+ widget_type="ComboBox",
62
+ options={"nullable": False}
63
+ )
64
+
65
+ dimension_choice_container = Container(
66
+ widgets=[self._dimension_choice_combo],
67
+ labels=False,
68
+ layout="horizontal",
69
+ )
70
+
71
+ self._add_tooltip_button_to_container(
72
+ dimension_choice_container,
73
+ "Choose dimension along which to either\n"
74
+ " (i) select the element at the midpoint along the dimension (e.g for inspection)\n"
75
+ "or (ii) save each element as a separate tif file in the provided folder"
76
+ )
77
+
78
+ self._load_middle_element_button = create_widget(
79
+ widget_type="PushButton",
80
+ label="Load middle element in Napari",
81
+ )
82
+
83
+ self._load_middle_element_button.clicked.connect(
84
+ self._load_middle_element
85
+ )
86
+
87
+ middle_elem_button_container = Container(
88
+ widgets=[self._load_middle_element_button],
89
+ labels=False,
90
+ layout="horizontal",
91
+ )
92
+
93
+ self._add_tooltip_button_to_container(
94
+ middle_elem_button_container,
95
+ (
96
+ "Load middle element as Napari layer,\n"
97
+ "e.g to check if dimensions match your expectations"
98
+ )
99
+ )
100
+
101
+ ###
102
+ self._save_to_folder_path = create_widget(
103
+ widget_type="FileEdit",
104
+ label="Folder path",
105
+ options={"mode": "d"},
106
+ )
107
+ ###
108
+
109
+ self._save_to_folder_compress_checkbox = create_widget(
110
+ widget_type="CheckBox",
111
+ label="Compress when saving",
112
+ options={"value": False},
113
+ )
114
+
115
+ self._save_to_folder_button = create_widget(
116
+ widget_type="PushButton",
117
+ label="Save elements along dimension to folder",
118
+ )
119
+
120
+ self._save_to_folder_button.clicked.connect(
121
+ self._save_to_folder
122
+ )
123
+
124
+ save_to_folder_container = Container(
125
+ widgets=[self._save_to_folder_button],
126
+ labels=False,
127
+ layout="horizontal",
128
+ )
129
+
130
+ self._add_tooltip_button_to_container(
131
+ save_to_folder_container,
132
+ (
133
+ "Save all elements of the specified dimension\n"
134
+ "independently to a folder as tifs."
135
+ )
136
+ )
137
+
138
+ self._progress_bar = QProgressBar()
139
+
140
+
141
+ self.setLayout(QVBoxLayout())
142
+
143
+
144
+ # self.layout().addWidget(self._array_layer_combo.native)
145
+ self.layout().addWidget(QLabel("<u>Select path to tif file:</u>"))
146
+ self.layout().addWidget(self._array_file_path.native)
147
+ self.layout().addWidget(refresh_container.native)
148
+ self.layout().addWidget(QLabel(f"Dimensions of currently selected layer:"))
149
+ self.layout().addWidget(self._shape_text)
150
+ self.layout().addWidget(QLabel(""))
151
+
152
+ self.layout().addWidget(QLabel("<u>Select dimension:</u>"))
153
+ self.layout().addWidget(dimension_choice_container.native)
154
+ self.layout().addWidget(QLabel(""))
155
+
156
+ self.layout().addWidget(QLabel("<u>(optional) Inspect middle element:</u>"))
157
+ self.layout().addWidget(middle_elem_button_container.native)
158
+ self.layout().addWidget(QLabel(""))
159
+
160
+ self.layout().addWidget(QLabel("<u>Select path where the folder will be created:</u>"))
161
+ # self.layout().addWidget(QLabel(f"Path at which to create folder for saving:"))
162
+ self.layout().addWidget(self._save_to_folder_path.native)
163
+ self.layout().addWidget(self._save_to_folder_compress_checkbox.native)
164
+ self.layout().addWidget(save_to_folder_container.native)
165
+ self.layout().addWidget(self._progress_bar)
166
+
167
+ # self._update_layer_combos()
168
+ self._update_dimensions()
169
+ self._update_dimension_choices()
170
+
171
+ self.layout().addStretch(1)
172
+
173
+ def _save_to_folder(self):
174
+ save_path = self._save_to_folder_path.value
175
+ if str(save_path) != "." and os.path.isdir(save_path):
176
+ path = self._array_file_path.value
177
+ if str(path) != "." and os.path.isfile(path):
178
+
179
+ dimension_index, dimension_shape = self._dimension_index_from_str(
180
+ self._dimension_choice_combo.value
181
+ )
182
+
183
+ path_to_folder = f"{save_path}/{path.stem}_dim{dimension_index}"
184
+ self._create_folder_if_needed(path_to_folder)
185
+
186
+ compress_params = {}
187
+ if self._save_to_folder_compress_checkbox.value:
188
+ compress_params.update({"compression": ("zlib", 1)})
189
+
190
+ self._lazy_save_slices(
191
+ slice_dim=dimension_index,
192
+ file=str(path),
193
+ path_to_save=path_to_folder,
194
+ compress_args=compress_params
195
+ )
196
+
197
+ self._progress_bar.reset()
198
+
199
+ napari.utils.notifications.show_info(
200
+ f"Finished saving files!\n"
201
+ f"Saved to {path_to_folder}"
202
+ )
203
+
204
+
205
+
206
+ def _create_folder_if_needed(self, folder_path):
207
+ if not os.path.exists(folder_path):
208
+ os.makedirs(folder_path)
209
+
210
+ def _update_dimension_choices(self):
211
+ path = self._array_file_path.value
212
+ if str(path) != "." and os.path.isfile(path):
213
+ shape = self._lazy_shape(path)
214
+ dimensions_as_str = [f"dim {i} ({s})" for i,s in enumerate(shape)]
215
+ self._dimension_choice_combo.choices = dimensions_as_str
216
+ else:
217
+ self._dimension_choice_combo.choices = ["None"]
218
+
219
+ def _dimension_index_from_str(self, string):
220
+ dim_index = int(string.split(' ')[1])
221
+ dim_shape = int(string.split(' ')[2][1:-1])
222
+
223
+ return dim_index, dim_shape
224
+
225
+ def _load_middle_element(self):
226
+ path = self._array_file_path.value
227
+ if str(path) != "." and os.path.isfile(path):
228
+ dimension_index, dimension_shape = self._dimension_index_from_str(
229
+ self._dimension_choice_combo.value
230
+ )
231
+
232
+ middle_slice = self._lazy_grab_slice(
233
+ element_index=int(dimension_shape/2),
234
+ slice_dim=dimension_index,
235
+ file=str(path)
236
+ )
237
+ if middle_slice is None:
238
+ napari.utils.notifications.show_warning(
239
+ "Please choose a compatible TIF file"
240
+ )
241
+ else:
242
+ self._viewer.add_image(middle_slice)
243
+ else:
244
+ napari.utils.notifications.show_warning(
245
+ "Please choose a compatible TIF file"
246
+ )
247
+
248
+
249
+ def _lazy_save_slices(self, slice_dim: int, file: str, path_to_save: str,
250
+ compress_args: dict = {}):
251
+ """
252
+ Lazily slice a multidimensional file along a specified dimension.
253
+
254
+ Parameters:
255
+ - file: path to the image file.
256
+ - slice_dim: the dimension (axis) along which to slice.
257
+ - path_to_save: path to the folder where to save the slices.
258
+ - compress_args: dictionary of arguments to pass to tifffile.imwrite.
259
+
260
+ Returns:
261
+ - The element of the sliced array at the specified index.
262
+ """
263
+
264
+ file = str(file)
265
+ if file.endswith(".tif") or file.endswith(".tiff") or file.endswith("lsm"):
266
+ with tifffile.TiffFile(file) as tif:
267
+ shape = tif.series[0].shape
268
+
269
+ slices = [slice(None) for _ in range(len(shape))]
270
+
271
+ zarr_store = tif.series[0].aszarr()
272
+ zarr_array = zarr.open(zarr_store)
273
+
274
+ for element_index in range(shape[slice_dim]):
275
+ slices[slice_dim] = element_index
276
+
277
+ stack = zarr_array[tuple(slices)]
278
+
279
+ name_tif = file.split(os.sep)[-1].split('.')[0]
280
+
281
+ tifffile.imwrite(
282
+ f"{path_to_save}/{name_tif}_slice{element_index:03d}.tif",
283
+ stack,
284
+ **compress_args
285
+ )
286
+
287
+ self._progress_bar.setValue(
288
+ int((element_index+1)/shape[slice_dim]*100)
289
+ )
290
+ else:
291
+ img = BioImage(file)
292
+ shape = tuple([elem for elem in img.shape if elem != 1])
293
+ lazy_array = img.dask_data.reshape(shape)
294
+
295
+
296
+ slices = [slice(None) for _ in range(len(shape))]
297
+
298
+ for element_index in range(shape[slice_dim]):
299
+ slices[slice_dim] = element_index
300
+
301
+ stack = lazy_array[tuple(slices)].compute()
302
+
303
+ name_tif = file.split(os.sep)[-1].split('.')[0]
304
+
305
+ tifffile.imwrite(
306
+ f"{path_to_save}/{name_tif}_slice{element_index:03d}.tif",
307
+ stack,
308
+ **compress_args
309
+ )
310
+
311
+ self._progress_bar.setValue(
312
+ int((element_index+1)/shape[slice_dim]*100)
313
+ )
314
+
315
+
316
+
317
+
318
+
319
+ def _lazy_grab_slice(self, slice_dim, file, element_index: int = None):
320
+ """
321
+ Lazily slice a multidimensional file along a specified dimension.
322
+
323
+ Parameters:
324
+ - file: path to the image file.
325
+ - slice_dim: the dimension (axis) along which to slice.
326
+ - element_index: the index of the element to retrieve along the slice dimension.
327
+
328
+ Returns:
329
+ - The element of the sliced array at the specified index.
330
+ """
331
+
332
+ file = str(file)
333
+ if file.endswith(".tif") or file.endswith(".tiff") or file.endswith("lsm"):
334
+ with tifffile.TiffFile(file) as tif:
335
+ shape = tif.series[0].shape
336
+ slices = [slice(None) for _ in range(len(shape))]
337
+ zarr_array = zarr.open(tif.series[0].aszarr())
338
+ slices[slice_dim] = element_index
339
+ stack = zarr_array[tuple(slices)]
340
+
341
+ else:
342
+ img = BioImage(file)
343
+ shape = tuple([elem for elem in img.shape if elem != 1])
344
+ lazy_array = img.dask_data.reshape(shape)
345
+
346
+ slices = [slice(None) for _ in range(len(shape))]
347
+
348
+ slices[slice_dim] = element_index
349
+
350
+ stack = lazy_array[tuple(slices)].compute()
351
+
352
+ return stack
353
+
354
+
355
+ def _lazy_shape(self, file):
356
+ """
357
+ Lazily retrieve the shape of a multidimensional file.
358
+
359
+ Parameters:
360
+ - file: path to the image file.
361
+
362
+ Returns:
363
+ - The shape of the image file.
364
+ """
365
+ file = str(file)
366
+ if file.endswith(".tif") or file.endswith(".tiff") or file.endswith("lsm"):
367
+ with tifffile.TiffFile(file) as tif:
368
+ shape = tif.series[0].shape
369
+ else:
370
+ img = BioImage(file)
371
+ shape = tuple([elem for elem in img.shape if elem != 1])
372
+
373
+ return shape
374
+
375
+ def _update_dimensions(self):
376
+ # layer = self._array_layer_combo.value
377
+ path = self._array_file_path.value
378
+ if str(path) != "." and os.path.isfile(path):
379
+ shape = self._lazy_shape(path)
380
+
381
+ self._shape_text.setText(f"Shape: <a style=color:#1A85FF;>{shape}</a>")
382
+ else:
383
+ self._shape_text.setText(self._default_shape_text)
384
+
385
+ def _bind_layer_combo(self, obj):
386
+ """
387
+ This used so that when calling layer_combo.value, we get the layer object,
388
+ not the name of the layer
389
+ """
390
+ name = obj.native.currentText()
391
+ if name not in ("", "-----"):
392
+ return self._viewer.layers[name]
393
+ else:
394
+ return None
395
+
396
+ def _update_layer_combos(self):
397
+
398
+ ### 1. Clear all combos but keep the previous choice if possible
399
+ previous_text = self._array_layer_combo.native.currentText()
400
+ self._array_layer_combo.native.clear()
401
+
402
+ ### 2. Add layers to combos
403
+ # add layers to compatible combos
404
+ for layer in self._viewer.layers:
405
+ if (
406
+ isinstance(layer, napari.layers.Image | napari.layers.Labels)
407
+ and self._array_layer_combo.enabled
408
+ ):
409
+ self._array_layer_combo.native.addItem(layer.name)
410
+
411
+ ### 3. Reset combo current choice to previous text if possible
412
+ all_choices = [
413
+ self._array_layer_combo.native.itemText(i) for i in range(self._array_layer_combo.native.count())
414
+ ]
415
+ if previous_text in all_choices:
416
+
417
+ # if the previous layer is None, set it to the newest layer
418
+ if previous_text == self._array_layer_combo.native.itemText(0):
419
+ self._array_layer_combo.native.setCurrentIndex(self._array_layer_combo.native.count() - 1)
420
+ else:
421
+ self._array_layer_combo.native.setCurrentText(previous_text)
422
+ else:
423
+ self._array_layer_combo.native.setCurrentIndex(0)
424
+
425
+ def _add_tooltip_button_to_container(self, container, tooltip_text):
426
+ button = HoverTooltipButton(tooltip_text)
427
+ button.native = button
428
+ button._explicitly_hidden = False
429
+ button.name = ""
430
+
431
+ if isinstance(container, Container):
432
+ container.append(button)
433
+ else:
434
+ if isinstance(container, CheckBox | PushButton):
435
+ container = Container(
436
+ widgets=[container, button],
437
+ labels=False,
438
+ layout="horizontal",
439
+ )
440
+ else:
441
+ container_label = container.label
442
+ container.label = ""
443
+ container = Container(
444
+ widgets=[Label(value=container_label), container, button],
445
+ labels=False,
446
+ layout="horizontal",
447
+ )
448
+ return container
449
+ return None
450
+
451
+
452
+ if __name__ == "__main__":
453
+ import napari
454
+
455
+ viewer = napari.Viewer()
456
+ widget = File2FolderWidget(viewer)
457
+ viewer.window.add_dock_widget(widget)
458
+
459
+ napari.run()
@@ -0,0 +1,14 @@
1
+ name: napari-file2folder
2
+ display_name: Save multidimensional file as folder of tifs
3
+ # use 'hidden' to remove plugin from napari hub search results
4
+ visibility: public
5
+ # see https://napari.org/stable/plugins/manifest.html for valid categories
6
+ categories: ["Annotation", "Segmentation", "Acquisition"]
7
+ contributions:
8
+ commands:
9
+ - id: napari-file2folder.make_qwidget
10
+ python_name: napari_file2folder:File2FolderWidget
11
+ title: Save multidimensional file as folder of tifs
12
+ widgets:
13
+ - command: napari-file2folder.make_qwidget
14
+ display_name: Save multidimensional file as folder of tifs
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.1
2
+ Name: napari-file2folder
3
+ Version: 0.0.1
4
+ Summary: Save multidimensional file as folder of tifs
5
+ Author: Jules Vanaret
6
+ Author-email: jules.vanaret@univ-amu.fr
7
+ License:
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2024 Jules Vanaret
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ THE SOFTWARE.
29
+
30
+ Classifier: Development Status :: 2 - Pre-Alpha
31
+ Classifier: Framework :: napari
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
43
+ Requires-Python: >=3.9
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Requires-Dist: numpy
47
+ Requires-Dist: qtpy
48
+ Requires-Dist: magicgui
49
+ Requires-Dist: tifffile
50
+ Requires-Dist: bioio
51
+ Requires-Dist: bioio-ome-tiff
52
+ Requires-Dist: bioio-ome-zarr
53
+ Requires-Dist: bioio-nd2
54
+ Requires-Dist: bioio-czi
55
+ Provides-Extra: testing
56
+ Requires-Dist: tox; extra == "testing"
57
+ Requires-Dist: pytest; extra == "testing"
58
+ Requires-Dist: pytest-cov; extra == "testing"
59
+ Requires-Dist: pytest-qt; extra == "testing"
60
+ Requires-Dist: napari; extra == "testing"
61
+ Requires-Dist: pyqt5; extra == "testing"
62
+
63
+ # napari-file2folder
64
+
65
+ [![License MIT](https://img.shields.io/pypi/l/napari-file2folder.svg?color=green)](https://github.com/jules-vanaret/napari-file2folder/raw/main/LICENSE)
66
+ [![PyPI](https://img.shields.io/pypi/v/napari-file2folder.svg?color=green)](https://pypi.org/project/napari-file2folder)
67
+ [![Python Version](https://img.shields.io/pypi/pyversions/napari-file2folder.svg?color=green)](https://python.org)
68
+ [![tests](https://github.com/jules-vanaret/napari-file2folder/workflows/tests/badge.svg)](https://github.com/jules-vanaret/napari-file2folder/actions)
69
+ [![codecov](https://codecov.io/gh/jules-vanaret/napari-file2folder/branch/main/graph/badge.svg)](https://codecov.io/gh/jules-vanaret/napari-file2folder)
70
+ [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-file2folder)](https://napari-hub.org/plugins/napari-file2folder)
71
+
72
+ <img src="https://github.com/GuignardLab/tapenade/blob/main/imgs/tapenade3.png" width="100">
73
+
74
+ A plugin to inspect bioimages (e.g. .tif, .czi, .nd2, .lsm...) and save them as individual .tif files in a folder.
75
+
76
+ `napari-file2folder` is a [napari] plugin that is part of the [Tapenade](https://github.com/GuignardLab/tapenade) project. Tapenade is a tool for the analysis of dense 3D tissues acquired with deep imaging microscopy. It is designed to be user-friendly and to provide a comprehensive analysis of the data.
77
+
78
+ If you use this plugin for your research, please [cite us](https://github.com/GuignardLab/tapenade/blob/main/README.md#how-to-cite).
79
+
80
+ ## Overview
81
+
82
+ <img src="imgs/napari-file2folder-demo.gif"/>
83
+
84
+ This plugin allows you to inspect (possibly large) bioimages by displaying their shape (number of elements in each dimension), and allowing you to save each element along a chosen dimension as a separate .tif file in a folder. This is useful when you have a large movie or stack of images and you want to save each frame or slice as a separate file. Optionally, the plugin allows the user to visualize the middle element of a given dimension to help the user decide which dimension to save as separate files.
85
+
86
+ The plugin currently supports the following file formats:
87
+ - .tif
88
+ - .ome.tiff
89
+ - .zarr
90
+ - .ome.zarr
91
+ - .nd2
92
+ - .lsm
93
+ - .czi
94
+
95
+ This plugin leverages [tifffile], [bioio], and [zarr] to circumvent loading the entire images in memory, which allows inspection of very large images.
96
+
97
+ > [!CAUTION]
98
+ > When inspecting the middle element of a dimension, or when saving one element of a dimension as a separate file, the plugin loads the element in memory, which means that at least this lone element must fit in memory.
99
+
100
+ ## Installation
101
+
102
+ The plugin obviously requires [napari] to run. If you don't have it yet, follow the instructions [here](https://napari.org/stable/tutorials/fundamentals/installation.html).
103
+
104
+ The simplest way to install `napari-file2folder` is via the [napari] plugin manager. Open Napari, go to `Plugins > Install/Uninstall Packages...` and search for `napari-file2folder`. Click on the install button and you are ready to go!
105
+
106
+ You can install `napari-file2folder` via [pip]:
107
+
108
+ pip install napari-file2folder
109
+
110
+
111
+
112
+
113
+ ## Contributing
114
+
115
+ Contributions are very welcome. Tests can be run with [tox], please ensure
116
+ the coverage at least stays the same before you submit a pull request.
117
+
118
+ ## License
119
+
120
+ Distributed under the terms of the [MIT] license,
121
+ "napari-file2folder" is free and open source software
122
+
123
+ ## Issues
124
+
125
+ If you encounter any problems, please [file an issue] along with a detailed description.
126
+
127
+ ----------------------------------
128
+
129
+ This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
130
+
131
+ <!--
132
+ Don't miss the full getting started guide to set up your new package:
133
+ https://github.com/napari/cookiecutter-napari-plugin#getting-started
134
+
135
+ and review the napari docs for plugin developers:
136
+ https://napari.org/stable/plugins/index.html
137
+ -->
138
+
139
+ [napari]: https://github.com/napari/napari
140
+ [Cookiecutter]: https://github.com/audreyr/cookiecutter
141
+ [@napari]: https://github.com/napari
142
+ [MIT]: http://opensource.org/licenses/MIT
143
+ [BSD-3]: http://opensource.org/licenses/BSD-3-Clause
144
+ [GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
145
+ [GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
146
+ [Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
147
+ [Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
148
+ [cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
149
+
150
+ [napari]: https://github.com/napari/napari
151
+ [tox]: https://tox.readthedocs.io/en/latest/
152
+ [pip]: https://pypi.org/project/pip/
153
+ [PyPI]: https://pypi.org/
154
+ [tifffile]: https://github.com/cgohlke/tifffile
155
+ [bioio]: https://github.com/bioio-devs/bioio
156
+ [zarr]: https://github.com/zarr-developers/zarr-python
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ src/napari_file2folder/__init__.py
6
+ src/napari_file2folder/_custom_widgets.py
7
+ src/napari_file2folder/_widget.py
8
+ src/napari_file2folder/napari.yaml
9
+ src/napari_file2folder.egg-info/PKG-INFO
10
+ src/napari_file2folder.egg-info/SOURCES.txt
11
+ src/napari_file2folder.egg-info/dependency_links.txt
12
+ src/napari_file2folder.egg-info/entry_points.txt
13
+ src/napari_file2folder.egg-info/requires.txt
14
+ src/napari_file2folder.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [napari.manifest]
2
+ napari-file2folder = napari_file2folder:napari.yaml
@@ -0,0 +1,17 @@
1
+ numpy
2
+ qtpy
3
+ magicgui
4
+ tifffile
5
+ bioio
6
+ bioio-ome-tiff
7
+ bioio-ome-zarr
8
+ bioio-nd2
9
+ bioio-czi
10
+
11
+ [testing]
12
+ tox
13
+ pytest
14
+ pytest-cov
15
+ pytest-qt
16
+ napari
17
+ pyqt5
@@ -0,0 +1 @@
1
+ napari_file2folder