napari-timestamper 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.
- napari-timestamper-0.0.1/.github/workflows/test_and_deploy.yml +90 -0
- napari-timestamper-0.0.1/.gitignore +84 -0
- napari-timestamper-0.0.1/.napari-hub/DESCRIPTION.md +9 -0
- napari-timestamper-0.0.1/.napari-hub/config.yml +1 -0
- napari-timestamper-0.0.1/.pre-commit-config.yaml +21 -0
- napari-timestamper-0.0.1/42.0.0 +12 -0
- napari-timestamper-0.0.1/LICENSE +28 -0
- napari-timestamper-0.0.1/MANIFEST.in +5 -0
- napari-timestamper-0.0.1/PKG-INFO +89 -0
- napari-timestamper-0.0.1/README.md +60 -0
- napari-timestamper-0.0.1/pyproject.toml +58 -0
- napari-timestamper-0.0.1/setup.cfg +63 -0
- napari-timestamper-0.0.1/src/napari_timestamper/__init__.py +7 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_tests/__init__.py +0 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_tests/test_timestamp_overlay.py +15 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_tests/test_widget.py +67 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_timestamp_overlay.py +233 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_version.py +4 -0
- napari-timestamper-0.0.1/src/napari_timestamper/_widget.py +220 -0
- napari-timestamper-0.0.1/src/napari_timestamper/napari.yaml +10 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/PKG-INFO +89 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/SOURCES.txt +26 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/dependency_links.txt +1 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/entry_points.txt +2 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/requires.txt +10 -0
- napari-timestamper-0.0.1/src/napari_timestamper.egg-info/top_level.txt +1 -0
- napari-timestamper-0.0.1/tox.ini +32 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# This workflows will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
name: tests
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
- npe2
|
|
11
|
+
tags:
|
|
12
|
+
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
|
|
13
|
+
pull_request:
|
|
14
|
+
branches:
|
|
15
|
+
- main
|
|
16
|
+
- npe2
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
name: ${{ matrix.platform }} py${{ matrix.python-version }}
|
|
22
|
+
runs-on: ${{ matrix.platform }}
|
|
23
|
+
strategy:
|
|
24
|
+
matrix:
|
|
25
|
+
platform: [ubuntu-latest, windows-latest, macos-latest]
|
|
26
|
+
python-version: ['3.8', '3.9', '3.10']
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v3
|
|
30
|
+
|
|
31
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
32
|
+
uses: actions/setup-python@v4
|
|
33
|
+
with:
|
|
34
|
+
python-version: ${{ matrix.python-version }}
|
|
35
|
+
|
|
36
|
+
# these libraries enable testing on Qt on linux
|
|
37
|
+
- uses: tlambert03/setup-qt-libs@v1
|
|
38
|
+
|
|
39
|
+
# strategy borrowed from vispy for installing opengl libs on windows
|
|
40
|
+
- name: Install Windows OpenGL
|
|
41
|
+
if: runner.os == 'Windows'
|
|
42
|
+
run: |
|
|
43
|
+
git clone --depth 1 https://github.com/pyvista/gl-ci-helpers.git
|
|
44
|
+
powershell gl-ci-helpers/appveyor/install_opengl.ps1
|
|
45
|
+
|
|
46
|
+
# note: if you need dependencies from conda, considering using
|
|
47
|
+
# setup-miniconda: https://github.com/conda-incubator/setup-miniconda
|
|
48
|
+
# and
|
|
49
|
+
# tox-conda: https://github.com/tox-dev/tox-conda
|
|
50
|
+
- name: Install dependencies
|
|
51
|
+
run: |
|
|
52
|
+
python -m pip install --upgrade pip
|
|
53
|
+
python -m pip install setuptools tox tox-gh-actions
|
|
54
|
+
|
|
55
|
+
# this runs the platform-specific tests declared in tox.ini
|
|
56
|
+
- name: Test with tox
|
|
57
|
+
uses: aganders3/headless-gui@v1
|
|
58
|
+
with:
|
|
59
|
+
run: python -m tox
|
|
60
|
+
env:
|
|
61
|
+
PLATFORM: ${{ matrix.platform }}
|
|
62
|
+
|
|
63
|
+
- name: Coverage
|
|
64
|
+
uses: codecov/codecov-action@v3
|
|
65
|
+
|
|
66
|
+
deploy:
|
|
67
|
+
# this will run when you have tagged a commit, starting with "v*"
|
|
68
|
+
# and requires that you have put your twine API key in your
|
|
69
|
+
# github secrets (see readme for details)
|
|
70
|
+
needs: [test]
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
if: contains(github.ref, 'tags')
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/checkout@v3
|
|
75
|
+
- name: Set up Python
|
|
76
|
+
uses: actions/setup-python@v4
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.x"
|
|
79
|
+
- name: Install dependencies
|
|
80
|
+
run: |
|
|
81
|
+
python -m pip install --upgrade pip
|
|
82
|
+
pip install -U setuptools setuptools_scm wheel twine build
|
|
83
|
+
- name: Build and publish
|
|
84
|
+
env:
|
|
85
|
+
TWINE_USERNAME: __token__
|
|
86
|
+
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
|
|
87
|
+
run: |
|
|
88
|
+
git tag
|
|
89
|
+
python -m build .
|
|
90
|
+
twine upload dist/*
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
env/
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
# Usually these files are written by a python script from a template
|
|
29
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.coverage
|
|
41
|
+
.coverage.*
|
|
42
|
+
.cache
|
|
43
|
+
nosetests.xml
|
|
44
|
+
coverage.xml
|
|
45
|
+
*,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.napari_cache
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Django stuff:
|
|
54
|
+
*.log
|
|
55
|
+
local_settings.py
|
|
56
|
+
|
|
57
|
+
# Flask instance folder
|
|
58
|
+
instance/
|
|
59
|
+
|
|
60
|
+
# Sphinx documentation
|
|
61
|
+
docs/_build/
|
|
62
|
+
|
|
63
|
+
# MkDocs documentation
|
|
64
|
+
/site/
|
|
65
|
+
|
|
66
|
+
# PyBuilder
|
|
67
|
+
target/
|
|
68
|
+
|
|
69
|
+
# Pycharm and VSCode
|
|
70
|
+
.idea/
|
|
71
|
+
venv/
|
|
72
|
+
.vscode/
|
|
73
|
+
|
|
74
|
+
# IPython Notebook
|
|
75
|
+
.ipynb_checkpoints
|
|
76
|
+
|
|
77
|
+
# pyenv
|
|
78
|
+
.python-version
|
|
79
|
+
|
|
80
|
+
# OS
|
|
81
|
+
.DS_Store
|
|
82
|
+
|
|
83
|
+
# written by setuptools_scm
|
|
84
|
+
**/_version.py
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- This file is a placeholder for customizing description of your plugin
|
|
2
|
+
on the napari hub if you wish. The readme file will be used by default if
|
|
3
|
+
you wish not to do any customization for the napari hub listing.
|
|
4
|
+
|
|
5
|
+
If you need some help writing a good description, check out our
|
|
6
|
+
[guide](https://github.com/chanzuckerberg/napari-hub/wiki/Writing-the-Perfect-Description-for-your-Plugin)
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
The developer has not yet provided a napari-hub specific description.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
visibility: disabled
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.4.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-docstring-first
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
exclude: ^\.napari-hub/.*
|
|
9
|
+
- id: check-yaml # checks for correct yaml syntax for github actions ex.
|
|
10
|
+
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
|
11
|
+
rev: v0.0.286
|
|
12
|
+
hooks:
|
|
13
|
+
- id: ruff
|
|
14
|
+
- repo: https://github.com/psf/black
|
|
15
|
+
rev: 23.7.0
|
|
16
|
+
hooks:
|
|
17
|
+
- id: black
|
|
18
|
+
- repo: https://github.com/tlambert03/napari-plugin-checks
|
|
19
|
+
rev: v0.3.0
|
|
20
|
+
hooks:
|
|
21
|
+
- id: napari-plugin-checks
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Requirement already satisfied: setuptools in c:\users\benig\documents\python_dev\napari-timestamper\.tox\.package\lib\site-packages (68.1.2)
|
|
2
|
+
Requirement already satisfied: wheel in c:\users\benig\documents\python_dev\napari-timestamper\.tox\.package\lib\site-packages (0.41.2)
|
|
3
|
+
Collecting setuptools_scm
|
|
4
|
+
Using cached setuptools_scm-7.1.0-py3-none-any.whl (43 kB)
|
|
5
|
+
Collecting packaging>=20.0 (from setuptools_scm)
|
|
6
|
+
Using cached packaging-23.1-py3-none-any.whl (48 kB)
|
|
7
|
+
Collecting typing-extensions (from setuptools_scm)
|
|
8
|
+
Obtaining dependency information for typing-extensions from https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl.metadata
|
|
9
|
+
Using cached typing_extensions-4.7.1-py3-none-any.whl.metadata (3.1 kB)
|
|
10
|
+
Using cached typing_extensions-4.7.1-py3-none-any.whl (33 kB)
|
|
11
|
+
Installing collected packages: typing-extensions, packaging, setuptools_scm
|
|
12
|
+
Successfully installed packaging-23.1 setuptools_scm-7.1.0 typing-extensions-4.7.1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
Copyright (c) 2023, Benjamin Grädel
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
* Neither the name of copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: napari-timestamper
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A simple plugin to add a timestamp overlay to napari
|
|
5
|
+
Home-page: https://github.com/bgraedel/napari-timestamper
|
|
6
|
+
Author: Benjamin Grädel
|
|
7
|
+
Author-email: benjamin.graedel@unibe.ch
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/bgraedel/napari-timestamper/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/bgraedel/napari-timestamper#README.md
|
|
11
|
+
Project-URL: Source Code, https://github.com/bgraedel/napari-timestamper
|
|
12
|
+
Project-URL: User Support, https://github.com/bgraedel/napari-timestamper/issues
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Provides-Extra: testing
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
|
|
30
|
+
# napari-timestamper
|
|
31
|
+
|
|
32
|
+
[](https://github.com/bgraedel/napari-timestamper/raw/main/LICENSE)
|
|
33
|
+
[](https://pypi.org/project/napari-timestamper)
|
|
34
|
+
[](https://python.org)
|
|
35
|
+
[](https://github.com/bgraedel/napari-timestamper/actions)
|
|
36
|
+
[](https://codecov.io/gh/bgraedel/napari-timestamper)
|
|
37
|
+
<!-- [](https://napari-hub.org/plugins/napari-timestamper) -->
|
|
38
|
+
|
|
39
|
+
A simple plugin to add a timestamp overlay to napari
|
|
40
|
+
uses some private overlay functionality, so may break in the future
|
|
41
|
+
|
|
42
|
+
----------------------------------
|
|
43
|
+
|
|
44
|
+
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
You can install `napari-timestamper` via [pip]: [not yet on PyPI]
|
|
49
|
+
|
|
50
|
+
pip install napari-timestamper
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
To install latest development version :
|
|
55
|
+
|
|
56
|
+
pip install git+https://github.com/bgraedel/napari-timestamper.git
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
|
|
61
|
+
Contributions are very welcome. Tests can be run with [tox], please ensure
|
|
62
|
+
the coverage at least stays the same before you submit a pull request.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
Distributed under the terms of the [BSD-3] license,
|
|
67
|
+
"napari-timestamper" is free and open source software
|
|
68
|
+
|
|
69
|
+
## Issues
|
|
70
|
+
|
|
71
|
+
If you encounter any problems, please [file an issue] along with a detailed description.
|
|
72
|
+
|
|
73
|
+
[napari]: https://github.com/napari/napari
|
|
74
|
+
[Cookiecutter]: https://github.com/audreyr/cookiecutter
|
|
75
|
+
[@napari]: https://github.com/napari
|
|
76
|
+
[MIT]: http://opensource.org/licenses/MIT
|
|
77
|
+
[BSD-3]: http://opensource.org/licenses/BSD-3-Clause
|
|
78
|
+
[GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
|
|
79
|
+
[GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
80
|
+
[Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
|
|
81
|
+
[Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
|
|
82
|
+
[cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
|
|
83
|
+
|
|
84
|
+
[file an issue]: https://github.com/bgraedel/napari-timestamper/issues
|
|
85
|
+
|
|
86
|
+
[napari]: https://github.com/napari/napari
|
|
87
|
+
[tox]: https://tox.readthedocs.io/en/latest/
|
|
88
|
+
[pip]: https://pypi.org/project/pip/
|
|
89
|
+
[PyPI]: https://pypi.org/
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# napari-timestamper
|
|
2
|
+
|
|
3
|
+
[](https://github.com/bgraedel/napari-timestamper/raw/main/LICENSE)
|
|
4
|
+
[](https://pypi.org/project/napari-timestamper)
|
|
5
|
+
[](https://python.org)
|
|
6
|
+
[](https://github.com/bgraedel/napari-timestamper/actions)
|
|
7
|
+
[](https://codecov.io/gh/bgraedel/napari-timestamper)
|
|
8
|
+
<!-- [](https://napari-hub.org/plugins/napari-timestamper) -->
|
|
9
|
+
|
|
10
|
+
A simple plugin to add a timestamp overlay to napari
|
|
11
|
+
uses some private overlay functionality, so may break in the future
|
|
12
|
+
|
|
13
|
+
----------------------------------
|
|
14
|
+
|
|
15
|
+
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
You can install `napari-timestamper` via [pip]: [not yet on PyPI]
|
|
20
|
+
|
|
21
|
+
pip install napari-timestamper
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
To install latest development version :
|
|
26
|
+
|
|
27
|
+
pip install git+https://github.com/bgraedel/napari-timestamper.git
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Contributing
|
|
31
|
+
|
|
32
|
+
Contributions are very welcome. Tests can be run with [tox], please ensure
|
|
33
|
+
the coverage at least stays the same before you submit a pull request.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
Distributed under the terms of the [BSD-3] license,
|
|
38
|
+
"napari-timestamper" is free and open source software
|
|
39
|
+
|
|
40
|
+
## Issues
|
|
41
|
+
|
|
42
|
+
If you encounter any problems, please [file an issue] along with a detailed description.
|
|
43
|
+
|
|
44
|
+
[napari]: https://github.com/napari/napari
|
|
45
|
+
[Cookiecutter]: https://github.com/audreyr/cookiecutter
|
|
46
|
+
[@napari]: https://github.com/napari
|
|
47
|
+
[MIT]: http://opensource.org/licenses/MIT
|
|
48
|
+
[BSD-3]: http://opensource.org/licenses/BSD-3-Clause
|
|
49
|
+
[GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
|
|
50
|
+
[GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
51
|
+
[Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
|
|
52
|
+
[Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
|
|
53
|
+
[cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
|
|
54
|
+
|
|
55
|
+
[file an issue]: https://github.com/bgraedel/napari-timestamper/issues
|
|
56
|
+
|
|
57
|
+
[napari]: https://github.com/napari/napari
|
|
58
|
+
[tox]: https://tox.readthedocs.io/en/latest/
|
|
59
|
+
[pip]: https://pypi.org/project/pip/
|
|
60
|
+
[PyPI]: https://pypi.org/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42.0.0", "wheel", "setuptools_scm"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[tool.setuptools_scm]
|
|
7
|
+
write_to = "src/napari_timestamper/_version.py"
|
|
8
|
+
|
|
9
|
+
[tool.black]
|
|
10
|
+
line-length = 79
|
|
11
|
+
target-version = ['py38', 'py39', 'py310']
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
[tool.ruff]
|
|
15
|
+
line-length = 79
|
|
16
|
+
select = [
|
|
17
|
+
"E", "F", "W", #flake8
|
|
18
|
+
"UP", # pyupgrade
|
|
19
|
+
"I", # isort
|
|
20
|
+
"BLE", # flake8-blind-exception
|
|
21
|
+
"B", # flake8-bugbear
|
|
22
|
+
"A", # flake8-builtins
|
|
23
|
+
"C4", # flake8-comprehensions
|
|
24
|
+
"ISC", # flake8-implicit-str-concat
|
|
25
|
+
"G", # flake8-logging-format
|
|
26
|
+
"PIE", # flake8-pie
|
|
27
|
+
"SIM", # flake8-simplify
|
|
28
|
+
]
|
|
29
|
+
ignore = [
|
|
30
|
+
"E501", # line too long. let black handle this
|
|
31
|
+
"UP006", "UP007", # type annotation. As using magicgui require runtime type annotation then we disable this.
|
|
32
|
+
"SIM117", # flake8-simplify - some of merged with statements are not looking great with black, reanble after drop python 3.9
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
exclude = [
|
|
36
|
+
".bzr",
|
|
37
|
+
".direnv",
|
|
38
|
+
".eggs",
|
|
39
|
+
".git",
|
|
40
|
+
".mypy_cache",
|
|
41
|
+
".pants.d",
|
|
42
|
+
".ruff_cache",
|
|
43
|
+
".svn",
|
|
44
|
+
".tox",
|
|
45
|
+
".venv",
|
|
46
|
+
"__pypackages__",
|
|
47
|
+
"_build",
|
|
48
|
+
"buck-out",
|
|
49
|
+
"build",
|
|
50
|
+
"dist",
|
|
51
|
+
"node_modules",
|
|
52
|
+
"venv",
|
|
53
|
+
"*vendored*",
|
|
54
|
+
"*_vendor*",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
target-version = "py38"
|
|
58
|
+
fix = true
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = napari-timestamper
|
|
3
|
+
description = A simple plugin to add a timestamp overlay to napari
|
|
4
|
+
long_description = file: README.md
|
|
5
|
+
long_description_content_type = text/markdown
|
|
6
|
+
url = https://github.com/bgraedel/napari-timestamper
|
|
7
|
+
author = Benjamin Grädel
|
|
8
|
+
author_email = benjamin.graedel@unibe.ch
|
|
9
|
+
license = BSD-3-Clause
|
|
10
|
+
license_files = LICENSE
|
|
11
|
+
classifiers =
|
|
12
|
+
Development Status :: 2 - Pre-Alpha
|
|
13
|
+
Intended Audience :: Developers
|
|
14
|
+
License :: OSI Approved :: BSD License
|
|
15
|
+
Operating System :: OS Independent
|
|
16
|
+
Programming Language :: Python
|
|
17
|
+
Programming Language :: Python :: 3
|
|
18
|
+
Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Programming Language :: Python :: 3.8
|
|
20
|
+
Programming Language :: Python :: 3.9
|
|
21
|
+
Programming Language :: Python :: 3.10
|
|
22
|
+
Programming Language :: Python :: 3.11
|
|
23
|
+
Topic :: Scientific/Engineering :: Image Processing
|
|
24
|
+
project_urls =
|
|
25
|
+
Bug Tracker = https://github.com/bgraedel/napari-timestamper/issues
|
|
26
|
+
Documentation = https://github.com/bgraedel/napari-timestamper#README.md
|
|
27
|
+
Source Code = https://github.com/bgraedel/napari-timestamper
|
|
28
|
+
User Support = https://github.com/bgraedel/napari-timestamper/issues
|
|
29
|
+
|
|
30
|
+
[options]
|
|
31
|
+
packages = find:
|
|
32
|
+
install_requires =
|
|
33
|
+
numpy
|
|
34
|
+
qtpy
|
|
35
|
+
python_requires = >=3.8
|
|
36
|
+
include_package_data = True
|
|
37
|
+
package_dir =
|
|
38
|
+
=src
|
|
39
|
+
setup_requires = setuptools_scm
|
|
40
|
+
|
|
41
|
+
[options.packages.find]
|
|
42
|
+
where = src
|
|
43
|
+
|
|
44
|
+
[options.entry_points]
|
|
45
|
+
napari.manifest =
|
|
46
|
+
napari-timestamper = napari_timestamper:napari.yaml
|
|
47
|
+
|
|
48
|
+
[options.extras_require]
|
|
49
|
+
testing =
|
|
50
|
+
tox
|
|
51
|
+
pytest # https://docs.pytest.org/en/latest/contents.html
|
|
52
|
+
pytest-cov # https://pytest-cov.readthedocs.io/en/latest/
|
|
53
|
+
pytest-qt # https://pytest-qt.readthedocs.io/en/latest/
|
|
54
|
+
napari
|
|
55
|
+
pyqt5
|
|
56
|
+
|
|
57
|
+
[options.package_data]
|
|
58
|
+
* = *.yaml
|
|
59
|
+
|
|
60
|
+
[egg_info]
|
|
61
|
+
tag_build =
|
|
62
|
+
tag_date = 0
|
|
63
|
+
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from napari_timestamper._timestamp_overlay import (
|
|
2
|
+
TimestampOverlay,
|
|
3
|
+
VispyTimestampOverlay,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_timestamp_overlay():
|
|
8
|
+
overlay = TimestampOverlay()
|
|
9
|
+
assert overlay is not None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_text_instantiation(make_napari_viewer):
|
|
13
|
+
viewer = make_napari_viewer()
|
|
14
|
+
model = TimestampOverlay()
|
|
15
|
+
VispyTimestampOverlay(overlay=model, viewer=viewer)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from qtpy import QtCore
|
|
3
|
+
|
|
4
|
+
from napari_timestamper._widget import TimestampWidget
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def timestamp_options(make_napari_viewer, qtbot):
|
|
9
|
+
viewer = make_napari_viewer()
|
|
10
|
+
widget = TimestampWidget(viewer)
|
|
11
|
+
viewer.window.add_dock_widget(widget)
|
|
12
|
+
return widget
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_initial_values(timestamp_options):
|
|
16
|
+
assert timestamp_options.time_axis.value() == 0
|
|
17
|
+
assert timestamp_options.start_time.value() == 0
|
|
18
|
+
assert timestamp_options.step_time.value() == 1
|
|
19
|
+
assert timestamp_options.prefix.text() == ""
|
|
20
|
+
assert timestamp_options.suffix.text() == "HH:MM:SS"
|
|
21
|
+
assert timestamp_options.position.currentText() == "top_center"
|
|
22
|
+
assert timestamp_options.ts_size.value() == 12
|
|
23
|
+
assert timestamp_options.x_shift.value() == 0
|
|
24
|
+
assert timestamp_options.y_shift.value() == 0
|
|
25
|
+
assert timestamp_options.time_format.currentText() == "HH:MM:SS"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_set_color(timestamp_options, qtbot):
|
|
29
|
+
assert timestamp_options.chosen_color == "white"
|
|
30
|
+
qtbot.mouseClick(timestamp_options.color, QtCore.Qt.LeftButton)
|
|
31
|
+
timestamp_options.color_dialog.done(1)
|
|
32
|
+
assert timestamp_options.chosen_color != "white"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_set_timestamp_overlay_options(timestamp_options):
|
|
36
|
+
timestamp_options.time_axis.setValue(1)
|
|
37
|
+
timestamp_options.start_time.setValue(10)
|
|
38
|
+
timestamp_options.step_time.setValue(2)
|
|
39
|
+
timestamp_options.prefix.setText("Time =")
|
|
40
|
+
timestamp_options.suffix.setText("s")
|
|
41
|
+
timestamp_options.position.setCurrentIndex(2)
|
|
42
|
+
timestamp_options.ts_size.setValue(20)
|
|
43
|
+
timestamp_options.x_shift.setValue(5)
|
|
44
|
+
timestamp_options.y_shift.setValue(-5)
|
|
45
|
+
timestamp_options.time_format.setCurrentIndex(1)
|
|
46
|
+
|
|
47
|
+
timestamp_options._set_timestamp_overlay_options()
|
|
48
|
+
|
|
49
|
+
assert timestamp_options.viewer._overlays["timestamp"].time_axis == 1
|
|
50
|
+
assert timestamp_options.viewer._overlays["timestamp"].start_time == 10
|
|
51
|
+
assert timestamp_options.viewer._overlays["timestamp"].step_size == 2
|
|
52
|
+
assert timestamp_options.viewer._overlays["timestamp"].prefix == "Time ="
|
|
53
|
+
assert timestamp_options.viewer._overlays["timestamp"].suffix == "s"
|
|
54
|
+
assert (
|
|
55
|
+
timestamp_options.viewer._overlays["timestamp"].position == "top_right"
|
|
56
|
+
)
|
|
57
|
+
assert timestamp_options.viewer._overlays["timestamp"].size == 20
|
|
58
|
+
assert (
|
|
59
|
+
timestamp_options.viewer._overlays["timestamp"].x_position_offset == 5
|
|
60
|
+
)
|
|
61
|
+
assert (
|
|
62
|
+
timestamp_options.viewer._overlays["timestamp"].y_position_offset == -5
|
|
63
|
+
)
|
|
64
|
+
assert (
|
|
65
|
+
timestamp_options.viewer._overlays["timestamp"].time_format
|
|
66
|
+
== "HH:MM:SS.ss"
|
|
67
|
+
)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the TimestampOverlay class and the VispyTimestampOverlay class.
|
|
3
|
+
|
|
4
|
+
The TimestampOverlay class is a canvas overlay that displays a timestamp on a napari viewer.
|
|
5
|
+
It has several customizable properties such as color, size, prefix, suffix, time, start_time, step_size,
|
|
6
|
+
time_format, y_position_offset, x_position_offset, and time_axis.
|
|
7
|
+
|
|
8
|
+
The VispyTimestampOverlay class is a vispy canvas overlay that displays the TimestampOverlay on a napari viewer.
|
|
9
|
+
It inherits from the ViewerOverlayMixin and VispyCanvasOverlay classes.
|
|
10
|
+
|
|
11
|
+
This structure is adapted from the napari dev example.
|
|
12
|
+
"""
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from napari._vispy.overlays.base import ViewerOverlayMixin, VispyCanvasOverlay
|
|
16
|
+
from napari.components._viewer_constants import CanvasPosition
|
|
17
|
+
from napari.components.overlays import CanvasOverlay
|
|
18
|
+
from napari.utils.color import ColorValue
|
|
19
|
+
from vispy.scene.visuals import Text
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TimestampOverlay(CanvasOverlay):
|
|
26
|
+
"""
|
|
27
|
+
Timestamp Overlay.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
color: ColorValue = (0, 1, 0, 1)
|
|
31
|
+
size: int = 10
|
|
32
|
+
prefix: str = ""
|
|
33
|
+
suffix: str = "MM:SS"
|
|
34
|
+
time: int = 0
|
|
35
|
+
start_time: int = 0
|
|
36
|
+
step_size: float = 1
|
|
37
|
+
time_format: str = "MM:SS"
|
|
38
|
+
y_position_offset: int = 0
|
|
39
|
+
x_position_offset: int = 0
|
|
40
|
+
time_axis: int = 0
|
|
41
|
+
|
|
42
|
+
def _timestamp_string(self):
|
|
43
|
+
"""
|
|
44
|
+
Returns the formatted timestamp string.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
str
|
|
49
|
+
The formatted timestamp string.
|
|
50
|
+
"""
|
|
51
|
+
timestamp = self._format_timestamp(self.time, self.time_format)
|
|
52
|
+
return f"{self.prefix} {timestamp} {self.suffix}"
|
|
53
|
+
|
|
54
|
+
def _format_timestamp(self, total_time, format_specifier):
|
|
55
|
+
"""
|
|
56
|
+
Formats the timestamp string based on the format specifier.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
total_time : int
|
|
61
|
+
The total time in seconds.
|
|
62
|
+
format_specifier : str
|
|
63
|
+
The format specifier for the timestamp text.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
str
|
|
68
|
+
The formatted timestamp string.
|
|
69
|
+
"""
|
|
70
|
+
time = float(self.start_time + self.time * self.step_size)
|
|
71
|
+
hours = int(time // 3600)
|
|
72
|
+
minutes = int((time % 3600) // 60)
|
|
73
|
+
seconds = time % 60
|
|
74
|
+
|
|
75
|
+
if format_specifier == "HH:MM:SS":
|
|
76
|
+
return f"{hours:02}:{minutes:02}:{seconds:02.0f}"
|
|
77
|
+
if format_specifier == "HH:MM:SS.ss":
|
|
78
|
+
return f"{hours:02}:{minutes:02}:{seconds:05.2f}"
|
|
79
|
+
elif format_specifier == "H:M:S":
|
|
80
|
+
return f"{hours}:{minutes}:{seconds:.0f}"
|
|
81
|
+
elif format_specifier == "H:M:S.ss":
|
|
82
|
+
return f"{hours}:{minutes}:{seconds:.2f}"
|
|
83
|
+
elif format_specifier == "MM:SS":
|
|
84
|
+
return f"{minutes:02}:{seconds:02.0f}"
|
|
85
|
+
elif format_specifier == "MM:SS.ss":
|
|
86
|
+
return f"{minutes:02}:{seconds:05.2f}"
|
|
87
|
+
elif format_specifier == "M:S":
|
|
88
|
+
return f"{minutes}:{seconds:.0f}"
|
|
89
|
+
elif format_specifier == "M:S.ss":
|
|
90
|
+
return f"{minutes}:{seconds:.2f}"
|
|
91
|
+
elif format_specifier == "SS":
|
|
92
|
+
return f"{total_time:02.0f}"
|
|
93
|
+
elif format_specifier == "SS.ss":
|
|
94
|
+
return f"{total_time:05.2f}"
|
|
95
|
+
elif format_specifier == "F":
|
|
96
|
+
return f"{total_time:.0f}"
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError(f"Unknown format specifier: {format_specifier}")
|
|
99
|
+
|
|
100
|
+
def _get_allowed_format_specifiers():
|
|
101
|
+
"""
|
|
102
|
+
Returns a list of allowed format specifiers.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
list of str
|
|
107
|
+
A list of allowed format specifiers.
|
|
108
|
+
"""
|
|
109
|
+
return [
|
|
110
|
+
"HH:MM:SS",
|
|
111
|
+
"HH:MM:SS.ss",
|
|
112
|
+
"H:M:S",
|
|
113
|
+
"H:M:S.ss",
|
|
114
|
+
"MM:SS",
|
|
115
|
+
"MM:SS.ss",
|
|
116
|
+
"M:S",
|
|
117
|
+
"M:S.ss",
|
|
118
|
+
"SS",
|
|
119
|
+
"SS.ss",
|
|
120
|
+
"F",
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def text(self):
|
|
125
|
+
"""
|
|
126
|
+
Returns the formatted timestamp string.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
str
|
|
131
|
+
The formatted timestamp string.
|
|
132
|
+
"""
|
|
133
|
+
return self._timestamp_string()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class VispyTimestampOverlay(ViewerOverlayMixin, VispyCanvasOverlay):
|
|
137
|
+
"""
|
|
138
|
+
Vispy Timestamp Overlay.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, *, viewer, overlay, parent=None):
|
|
142
|
+
super().__init__(
|
|
143
|
+
node=Text(
|
|
144
|
+
text=overlay.text,
|
|
145
|
+
color=overlay.color,
|
|
146
|
+
font_size=overlay.size,
|
|
147
|
+
pos=(0, 0),
|
|
148
|
+
),
|
|
149
|
+
viewer=viewer,
|
|
150
|
+
overlay=overlay,
|
|
151
|
+
parent=parent,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# setup callbacks
|
|
155
|
+
self.overlay.events.color.connect(self._on_color_change)
|
|
156
|
+
self.overlay.events.size.connect(self._on_size_change)
|
|
157
|
+
self.overlay.events.text.connect(self._on_text_change)
|
|
158
|
+
self.overlay.events.y_position_offset.connect(self._on_position_change)
|
|
159
|
+
self.overlay.events.x_position_offset.connect(self._on_position_change)
|
|
160
|
+
self.overlay.events.time_format.connect(self._on_text_change)
|
|
161
|
+
self.overlay.events.start_time.connect(self._on_time_change)
|
|
162
|
+
self.overlay.events.step_size.connect(self._on_time_change)
|
|
163
|
+
self.overlay.events.prefix.connect(self._on_text_change)
|
|
164
|
+
self.overlay.events.suffix.connect(self._on_text_change)
|
|
165
|
+
self.overlay.events.time_axis.connect(self._on_text_change)
|
|
166
|
+
self.viewer.dims.events.current_step.connect(self._on_time_change)
|
|
167
|
+
|
|
168
|
+
self.reset()
|
|
169
|
+
|
|
170
|
+
def _on_color_change(self, event=None):
|
|
171
|
+
"""
|
|
172
|
+
Callback function for when the color of the overlay is changed.
|
|
173
|
+
"""
|
|
174
|
+
self.node.color = self.overlay.color
|
|
175
|
+
|
|
176
|
+
def _on_position_change(self, event=None):
|
|
177
|
+
"""
|
|
178
|
+
Callback function for when the position of the overlay is changed.
|
|
179
|
+
"""
|
|
180
|
+
super()._on_position_change()
|
|
181
|
+
position = self.overlay.position
|
|
182
|
+
|
|
183
|
+
if position == CanvasPosition.TOP_LEFT:
|
|
184
|
+
anchors = ("left", "bottom")
|
|
185
|
+
elif position == CanvasPosition.TOP_RIGHT:
|
|
186
|
+
anchors = ("right", "bottom")
|
|
187
|
+
elif position == CanvasPosition.TOP_CENTER:
|
|
188
|
+
anchors = ("center", "bottom")
|
|
189
|
+
elif position == CanvasPosition.BOTTOM_RIGHT:
|
|
190
|
+
anchors = ("right", "top")
|
|
191
|
+
elif position == CanvasPosition.BOTTOM_LEFT:
|
|
192
|
+
anchors = ("left", "top")
|
|
193
|
+
elif position == CanvasPosition.BOTTOM_CENTER:
|
|
194
|
+
anchors = ("center", "top")
|
|
195
|
+
|
|
196
|
+
self.node.anchors = anchors
|
|
197
|
+
self.node.pos = (
|
|
198
|
+
self.overlay.x_position_offset,
|
|
199
|
+
self.overlay.y_position_offset,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def _on_size_change(self, event=None):
|
|
203
|
+
"""
|
|
204
|
+
Callback function for when the size of the overlay is changed.
|
|
205
|
+
"""
|
|
206
|
+
self.node.font_size = self.overlay.size
|
|
207
|
+
self._on_position_change()
|
|
208
|
+
|
|
209
|
+
def _on_text_change(self, event=None):
|
|
210
|
+
"""
|
|
211
|
+
Callback function for when the text of the overlay is changed.
|
|
212
|
+
"""
|
|
213
|
+
self.node.text = self.overlay.text
|
|
214
|
+
|
|
215
|
+
def _on_time_change(self, event=None):
|
|
216
|
+
"""
|
|
217
|
+
Callback function for when the time of the overlay is changed.
|
|
218
|
+
"""
|
|
219
|
+
self.overlay.time = self.viewer.dims.current_step[
|
|
220
|
+
self.overlay.time_axis
|
|
221
|
+
]
|
|
222
|
+
self.node.text = self.overlay.text
|
|
223
|
+
|
|
224
|
+
def reset(self):
|
|
225
|
+
"""
|
|
226
|
+
Resets the overlay to its initial state.
|
|
227
|
+
"""
|
|
228
|
+
super().reset()
|
|
229
|
+
self._on_color_change()
|
|
230
|
+
self._on_size_change()
|
|
231
|
+
self._on_position_change()
|
|
232
|
+
self._on_text_change()
|
|
233
|
+
self._on_time_change()
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dock widget for napari-timestamper
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
import napari
|
|
9
|
+
from napari._vispy.utils.visual import overlay_to_visual
|
|
10
|
+
from napari.components._viewer_constants import CanvasPosition
|
|
11
|
+
from qtpy import QtWidgets
|
|
12
|
+
|
|
13
|
+
from napari_timestamper._timestamp_overlay import (
|
|
14
|
+
TimestampOverlay,
|
|
15
|
+
VispyTimestampOverlay,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TimestampWidget(QtWidgets.QWidget):
|
|
20
|
+
"""
|
|
21
|
+
A widget that provides options for the timestamp overlay in napari viewer.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
viewer : napari.Viewer
|
|
26
|
+
The napari viewer instance.
|
|
27
|
+
parent : QWidget, optional
|
|
28
|
+
The parent widget, by default None.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
overlay_set: bool = False
|
|
32
|
+
|
|
33
|
+
def __init__(self, viewer: napari.viewer.Viewer, parent=None):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the timestamp_options widget.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
viewer : napari.Viewer
|
|
40
|
+
The napari viewer instance.
|
|
41
|
+
parent : QWidget, optional
|
|
42
|
+
The parent widget, by default None.
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(parent)
|
|
45
|
+
self.chosen_color = "white"
|
|
46
|
+
self.viewer = viewer
|
|
47
|
+
self._setupUi()
|
|
48
|
+
self._connect_all_changes()
|
|
49
|
+
self._setup_overlay()
|
|
50
|
+
|
|
51
|
+
def _setup_overlay(self):
|
|
52
|
+
with warnings.catch_warnings():
|
|
53
|
+
warnings.simplefilter("ignore")
|
|
54
|
+
try:
|
|
55
|
+
self.viewer._overlays["timestamp"]
|
|
56
|
+
except KeyError:
|
|
57
|
+
self.viewer._overlays["timestamp"] = TimestampOverlay(
|
|
58
|
+
visible=True
|
|
59
|
+
)
|
|
60
|
+
overlay_to_visual[TimestampOverlay] = VispyTimestampOverlay
|
|
61
|
+
self.viewer.window._qt_viewer._add_overlay(
|
|
62
|
+
self.viewer._overlays["timestamp"]
|
|
63
|
+
)
|
|
64
|
+
self.timestamp_overlay = self.viewer._overlays["timestamp"]
|
|
65
|
+
self._set_timestamp_overlay_options()
|
|
66
|
+
self.overlay_set = True
|
|
67
|
+
|
|
68
|
+
def _toggle_overlay(self):
|
|
69
|
+
if not self.overlay_set:
|
|
70
|
+
self._setup_overlay()
|
|
71
|
+
self.toggle_timestamp.setText("Remove Timestamp")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
if self.timestamp_overlay.visible:
|
|
75
|
+
self.timestamp_overlay.visible = False
|
|
76
|
+
self.toggle_timestamp.setText("Add Timestamp")
|
|
77
|
+
else:
|
|
78
|
+
self.timestamp_overlay.visible = True
|
|
79
|
+
self.toggle_timestamp.setText("Remove Timestamp")
|
|
80
|
+
|
|
81
|
+
def _setupUi(self):
|
|
82
|
+
self.setObjectName("Timestamp Options")
|
|
83
|
+
self.gridLayout = QtWidgets.QGridLayout()
|
|
84
|
+
|
|
85
|
+
self.time_axis_label = QtWidgets.QLabel("Time Axis")
|
|
86
|
+
self.time_axis = QtWidgets.QSpinBox()
|
|
87
|
+
self.time_axis.setRange(-10, 10)
|
|
88
|
+
|
|
89
|
+
self.start_time_label = QtWidgets.QLabel("Start Time")
|
|
90
|
+
self.start_time = QtWidgets.QSpinBox()
|
|
91
|
+
self.start_time.setRange(0, 10000)
|
|
92
|
+
self.start_time.setValue(0)
|
|
93
|
+
|
|
94
|
+
self.step_time_label = QtWidgets.QLabel("Step Time")
|
|
95
|
+
self.step_time = QtWidgets.QDoubleSpinBox()
|
|
96
|
+
self.step_time.setRange(0, 10000)
|
|
97
|
+
self.step_time.setValue(1)
|
|
98
|
+
|
|
99
|
+
self.prefix_label = QtWidgets.QLabel("Prefix")
|
|
100
|
+
self.prefix = QtWidgets.QLineEdit()
|
|
101
|
+
self.prefix.setText("")
|
|
102
|
+
|
|
103
|
+
self.suffix_label = QtWidgets.QLabel("Suffix")
|
|
104
|
+
self.suffix = QtWidgets.QLineEdit()
|
|
105
|
+
self.suffix.setText("HH:MM:SS")
|
|
106
|
+
|
|
107
|
+
self.position_label = QtWidgets.QLabel("Position")
|
|
108
|
+
self.position = QtWidgets.QComboBox()
|
|
109
|
+
self.position.addItems(CanvasPosition)
|
|
110
|
+
self.position.setCurrentIndex(1)
|
|
111
|
+
|
|
112
|
+
self.size_label = QtWidgets.QLabel("Size")
|
|
113
|
+
self.ts_size = QtWidgets.QSpinBox()
|
|
114
|
+
self.ts_size.setRange(0, 1000)
|
|
115
|
+
self.ts_size.setValue(12)
|
|
116
|
+
|
|
117
|
+
self.shift_label = QtWidgets.QLabel("XY Shift")
|
|
118
|
+
self.shiftlayout = QtWidgets.QHBoxLayout()
|
|
119
|
+
self.x_shift = QtWidgets.QSpinBox()
|
|
120
|
+
self.x_shift.setRange(-1000, 1000)
|
|
121
|
+
self.x_shift.setValue(0)
|
|
122
|
+
self.y_shift = QtWidgets.QSpinBox()
|
|
123
|
+
self.y_shift.setRange(-1000, 1000)
|
|
124
|
+
self.y_shift.setValue(0)
|
|
125
|
+
self.shiftlayout.addWidget(self.x_shift)
|
|
126
|
+
self.shiftlayout.addWidget(self.y_shift)
|
|
127
|
+
|
|
128
|
+
self.time_format_label = QtWidgets.QLabel("Time Format")
|
|
129
|
+
self.time_format = QtWidgets.QComboBox()
|
|
130
|
+
self.time_format.addItems(
|
|
131
|
+
TimestampOverlay._get_allowed_format_specifiers()
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
self.color = QtWidgets.QPushButton("Choose Color")
|
|
135
|
+
self.color_display = QtWidgets.QFrame()
|
|
136
|
+
|
|
137
|
+
self.toggle_timestamp = QtWidgets.QPushButton("Add Timestamp")
|
|
138
|
+
|
|
139
|
+
self.gridLayout.addWidget(self.time_axis_label, 0, 0)
|
|
140
|
+
self.gridLayout.addWidget(self.time_axis, 0, 1)
|
|
141
|
+
self.gridLayout.addWidget(self.start_time_label, 1, 0)
|
|
142
|
+
self.gridLayout.addWidget(self.start_time, 1, 1)
|
|
143
|
+
self.gridLayout.addWidget(self.step_time_label, 2, 0)
|
|
144
|
+
self.gridLayout.addWidget(self.step_time, 2, 1)
|
|
145
|
+
self.gridLayout.addWidget(self.prefix_label, 3, 0)
|
|
146
|
+
self.gridLayout.addWidget(self.prefix, 3, 1)
|
|
147
|
+
self.gridLayout.addWidget(self.suffix_label, 4, 0)
|
|
148
|
+
self.gridLayout.addWidget(self.suffix, 4, 1)
|
|
149
|
+
self.gridLayout.addWidget(self.position_label, 5, 0)
|
|
150
|
+
self.gridLayout.addWidget(self.position, 5, 1)
|
|
151
|
+
self.gridLayout.addWidget(self.size_label, 6, 0)
|
|
152
|
+
self.gridLayout.addWidget(self.ts_size, 6, 1)
|
|
153
|
+
self.gridLayout.addWidget(self.shift_label, 7, 0)
|
|
154
|
+
self.gridLayout.addLayout(self.shiftlayout, 7, 1)
|
|
155
|
+
self.gridLayout.addWidget(self.time_format_label, 8, 0)
|
|
156
|
+
self.gridLayout.addWidget(self.time_format, 8, 1)
|
|
157
|
+
self.gridLayout.addWidget(self.color, 9, 1)
|
|
158
|
+
self.gridLayout.addWidget(self.color_display, 9, 0)
|
|
159
|
+
self.gridLayout.addWidget(self.toggle_timestamp, 10, 0, 1, 2)
|
|
160
|
+
self.setLayout(self.gridLayout)
|
|
161
|
+
|
|
162
|
+
self.spacer = QtWidgets.QSpacerItem(
|
|
163
|
+
20,
|
|
164
|
+
40,
|
|
165
|
+
QtWidgets.QSizePolicy.Minimum,
|
|
166
|
+
QtWidgets.QSizePolicy.Expanding,
|
|
167
|
+
)
|
|
168
|
+
self.gridLayout.addItem(self.spacer, 11, 0, 1, 2)
|
|
169
|
+
|
|
170
|
+
self.color_display.setStyleSheet(
|
|
171
|
+
"QWidget {background-color: %s}" % self.chosen_color
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def _open_color_dialog(self):
|
|
175
|
+
self.color_dialog = QtWidgets.QColorDialog(parent=self)
|
|
176
|
+
self.color_dialog.open(self._set_colour)
|
|
177
|
+
|
|
178
|
+
def _set_colour(self):
|
|
179
|
+
color = self.color_dialog.selectedColor()
|
|
180
|
+
if color.isValid():
|
|
181
|
+
self.color_display.setStyleSheet(
|
|
182
|
+
"QWidget {background-color: %s}" % color.name()
|
|
183
|
+
)
|
|
184
|
+
self.chosen_color = color.name()
|
|
185
|
+
self._set_timestamp_overlay_options()
|
|
186
|
+
|
|
187
|
+
def _set_timestamp_overlay_options(self):
|
|
188
|
+
with warnings.catch_warnings():
|
|
189
|
+
warnings.simplefilter("ignore")
|
|
190
|
+
timestamp_overlay = self.viewer._overlays["timestamp"]
|
|
191
|
+
timestamp_overlay.color = self.chosen_color
|
|
192
|
+
timestamp_overlay.size = self.ts_size.value()
|
|
193
|
+
timestamp_overlay.position = self.position.currentText()
|
|
194
|
+
timestamp_overlay.prefix = self.prefix.text()
|
|
195
|
+
timestamp_overlay.suffix = self.suffix.text()
|
|
196
|
+
timestamp_overlay.start_time = self.start_time.value()
|
|
197
|
+
timestamp_overlay.step_size = self.step_time.value()
|
|
198
|
+
timestamp_overlay.time_format = self.time_format.currentText()
|
|
199
|
+
timestamp_overlay.x_position_offset = self.x_shift.value()
|
|
200
|
+
timestamp_overlay.y_position_offset = self.y_shift.value()
|
|
201
|
+
timestamp_overlay.time_axis = self.time_axis.value()
|
|
202
|
+
|
|
203
|
+
def _connect_all_changes(self):
|
|
204
|
+
for i in [
|
|
205
|
+
self.time_axis,
|
|
206
|
+
self.start_time,
|
|
207
|
+
self.step_time,
|
|
208
|
+
self.ts_size,
|
|
209
|
+
self.x_shift,
|
|
210
|
+
self.y_shift,
|
|
211
|
+
]:
|
|
212
|
+
i.valueChanged.connect(self._set_timestamp_overlay_options)
|
|
213
|
+
for i in [self.prefix, self.suffix]:
|
|
214
|
+
i.textChanged.connect(self._set_timestamp_overlay_options)
|
|
215
|
+
for i in [self.position, self.time_format]:
|
|
216
|
+
i.currentTextChanged.connect(self._set_timestamp_overlay_options)
|
|
217
|
+
self.toggle_timestamp.clicked.connect(self._toggle_overlay)
|
|
218
|
+
|
|
219
|
+
self.color.clicked.connect(self._open_color_dialog)
|
|
220
|
+
self.color.clicked.connect(self._set_timestamp_overlay_options)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
name: napari-timestamper
|
|
2
|
+
display_name: Timestamper
|
|
3
|
+
contributions:
|
|
4
|
+
commands:
|
|
5
|
+
- id: napari-timestamper.timestamper
|
|
6
|
+
python_name: napari_timestamper._widget:TimestampWidget
|
|
7
|
+
title: Make Timestamp Widget
|
|
8
|
+
widgets:
|
|
9
|
+
- command: napari-timestamper.timestamper
|
|
10
|
+
display_name: Timestamper
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: napari-timestamper
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A simple plugin to add a timestamp overlay to napari
|
|
5
|
+
Home-page: https://github.com/bgraedel/napari-timestamper
|
|
6
|
+
Author: Benjamin Grädel
|
|
7
|
+
Author-email: benjamin.graedel@unibe.ch
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/bgraedel/napari-timestamper/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/bgraedel/napari-timestamper#README.md
|
|
11
|
+
Project-URL: Source Code, https://github.com/bgraedel/napari-timestamper
|
|
12
|
+
Project-URL: User Support, https://github.com/bgraedel/napari-timestamper/issues
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Provides-Extra: testing
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
|
|
30
|
+
# napari-timestamper
|
|
31
|
+
|
|
32
|
+
[](https://github.com/bgraedel/napari-timestamper/raw/main/LICENSE)
|
|
33
|
+
[](https://pypi.org/project/napari-timestamper)
|
|
34
|
+
[](https://python.org)
|
|
35
|
+
[](https://github.com/bgraedel/napari-timestamper/actions)
|
|
36
|
+
[](https://codecov.io/gh/bgraedel/napari-timestamper)
|
|
37
|
+
<!-- [](https://napari-hub.org/plugins/napari-timestamper) -->
|
|
38
|
+
|
|
39
|
+
A simple plugin to add a timestamp overlay to napari
|
|
40
|
+
uses some private overlay functionality, so may break in the future
|
|
41
|
+
|
|
42
|
+
----------------------------------
|
|
43
|
+
|
|
44
|
+
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
You can install `napari-timestamper` via [pip]: [not yet on PyPI]
|
|
49
|
+
|
|
50
|
+
pip install napari-timestamper
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
To install latest development version :
|
|
55
|
+
|
|
56
|
+
pip install git+https://github.com/bgraedel/napari-timestamper.git
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
|
|
61
|
+
Contributions are very welcome. Tests can be run with [tox], please ensure
|
|
62
|
+
the coverage at least stays the same before you submit a pull request.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
Distributed under the terms of the [BSD-3] license,
|
|
67
|
+
"napari-timestamper" is free and open source software
|
|
68
|
+
|
|
69
|
+
## Issues
|
|
70
|
+
|
|
71
|
+
If you encounter any problems, please [file an issue] along with a detailed description.
|
|
72
|
+
|
|
73
|
+
[napari]: https://github.com/napari/napari
|
|
74
|
+
[Cookiecutter]: https://github.com/audreyr/cookiecutter
|
|
75
|
+
[@napari]: https://github.com/napari
|
|
76
|
+
[MIT]: http://opensource.org/licenses/MIT
|
|
77
|
+
[BSD-3]: http://opensource.org/licenses/BSD-3-Clause
|
|
78
|
+
[GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
|
|
79
|
+
[GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
80
|
+
[Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
|
|
81
|
+
[Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
|
|
82
|
+
[cookiecutter-napari-plugin]: https://github.com/napari/cookiecutter-napari-plugin
|
|
83
|
+
|
|
84
|
+
[file an issue]: https://github.com/bgraedel/napari-timestamper/issues
|
|
85
|
+
|
|
86
|
+
[napari]: https://github.com/napari/napari
|
|
87
|
+
[tox]: https://tox.readthedocs.io/en/latest/
|
|
88
|
+
[pip]: https://pypi.org/project/pip/
|
|
89
|
+
[PyPI]: https://pypi.org/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
.pre-commit-config.yaml
|
|
3
|
+
42.0.0
|
|
4
|
+
LICENSE
|
|
5
|
+
MANIFEST.in
|
|
6
|
+
README.md
|
|
7
|
+
pyproject.toml
|
|
8
|
+
setup.cfg
|
|
9
|
+
tox.ini
|
|
10
|
+
.github/workflows/test_and_deploy.yml
|
|
11
|
+
.napari-hub/DESCRIPTION.md
|
|
12
|
+
.napari-hub/config.yml
|
|
13
|
+
src/napari_timestamper/__init__.py
|
|
14
|
+
src/napari_timestamper/_timestamp_overlay.py
|
|
15
|
+
src/napari_timestamper/_version.py
|
|
16
|
+
src/napari_timestamper/_widget.py
|
|
17
|
+
src/napari_timestamper/napari.yaml
|
|
18
|
+
src/napari_timestamper.egg-info/PKG-INFO
|
|
19
|
+
src/napari_timestamper.egg-info/SOURCES.txt
|
|
20
|
+
src/napari_timestamper.egg-info/dependency_links.txt
|
|
21
|
+
src/napari_timestamper.egg-info/entry_points.txt
|
|
22
|
+
src/napari_timestamper.egg-info/requires.txt
|
|
23
|
+
src/napari_timestamper.egg-info/top_level.txt
|
|
24
|
+
src/napari_timestamper/_tests/__init__.py
|
|
25
|
+
src/napari_timestamper/_tests/test_timestamp_overlay.py
|
|
26
|
+
src/napari_timestamper/_tests/test_widget.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
napari_timestamper
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[tox]
|
|
2
|
+
envlist = py{38,39,310}-{linux,macos,windows}
|
|
3
|
+
isolated_build=true
|
|
4
|
+
|
|
5
|
+
[gh-actions]
|
|
6
|
+
python =
|
|
7
|
+
3.8: py38
|
|
8
|
+
3.9: py39
|
|
9
|
+
3.10: py310
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[gh-actions:env]
|
|
13
|
+
PLATFORM =
|
|
14
|
+
ubuntu-latest: linux
|
|
15
|
+
macos-latest: macos
|
|
16
|
+
windows-latest: windows
|
|
17
|
+
|
|
18
|
+
[testenv]
|
|
19
|
+
platform =
|
|
20
|
+
macos: darwin
|
|
21
|
+
linux: linux
|
|
22
|
+
windows: win32
|
|
23
|
+
passenv =
|
|
24
|
+
CI
|
|
25
|
+
GITHUB_ACTIONS
|
|
26
|
+
DISPLAY
|
|
27
|
+
XAUTHORITY
|
|
28
|
+
NUMPY_EXPERIMENTAL_ARRAY_FUNCTION
|
|
29
|
+
PYVISTA_OFF_SCREEN
|
|
30
|
+
extras =
|
|
31
|
+
testing
|
|
32
|
+
commands = pytest -v --color=yes --cov=napari_timestamper --cov-report=xml
|