diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..9df8c1b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,44 @@ +--- +name: 🐛 Bug Report +about: Report a bug +# based on matplotlib issue template +--- + + + + +### Bug report + +**Bug summary** + + + +**Code for reproduction** + + + +**Expected outcome** + + + + +**Version Info** + + * Operating system: + * Matplotlib version: + * Matplotlib backend (`print(matplotlib.get_backend())`): + * Python version: + * Jupyter version (if applicable): + * Other libraries: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a934fdb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser +blank_issues_enabled: true # default +contact_links: + - name: ❓ Question/Support/Other + url: https://discourse.matplotlib.org/c/3rdparty/18 + about: If you have a usage question diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..12e5a52 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,29 @@ +--- +name: 📖 Documentation improvement +about: Report parts of the docs that are wrong or unclear +labels: documentation, bug +--- + + + + +### Problem + + + + +### Suggested Improvement + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e5ab361 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,35 @@ +--- +name: 🚀 Enhancement/Feature Request +about: Suggest something that could be improved or a New Feature to add +labels: enhancement +--- + + + +### Problem + + + +### Proposed Solution + + + +### Additional context + + + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..ef4e4ca --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: Lint + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: psf/black@stable + with: + args: ". --check" + - name: docstrings + run: | + pip install flit + pushd $(mktemp -d) + git clone https://github.com/Carreau/velin.git --single-branch --depth 1 + cd velin + flit install + popd + velin . --check diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..494b409 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +# heavily based on https://github.com/jupyterlab/jupyterlab-git/blob/v0.22.2/.github/workflows/publish.yml +name: Publish Package + +on: + release: + types: [published] + + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install packaging setuptools twine wheel build + - name: Publish the Python package + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + python -m build -s -w + twine upload dist/* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49df861 --- /dev/null +++ b/.gitignore @@ -0,0 +1,155 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# mac stuff +.DS_store + +# editors + +## vim +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] +[._]*.un~ + +## vscode +.vscode \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b09c575 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, Ian Hunt-Isaak +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1244920 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# mpl-point-clicker + +A multiclass point clicker for matplotlib + +## Installation + +You can install using `pip`: + +```bash +pip install mpl_point_clicker +``` + +## Development Installation + + +```bash +pip install -e ".[dev]" +``` + diff --git a/docs/API.rst b/docs/API.rst new file mode 100644 index 0000000..36182a6 --- /dev/null +++ b/docs/API.rst @@ -0,0 +1,17 @@ +=== +API +=== + +Example Module +--------------- + +The example module just contains one function - which is just an example +function to extend matplotlib functionality. + +.. currentmodule:: mpl_point_clicker._clicker +.. autosummary:: + :toctree: autoapi + :nosignatures: + :recursive: + + clicker diff --git a/docs/Contributing.rst b/docs/Contributing.rst new file mode 100644 index 0000000..37f3b24 --- /dev/null +++ b/docs/Contributing.rst @@ -0,0 +1,73 @@ +============ +Contributing +============ + +Thanks for thinking of a way to help improve this library! Remember that contributions come in all +shapes and sizes beyond writing bug fixes. Contributing to documentation, opening new `issues `_, +asking for clarification on things you find unclear, and requesting new features, are all super valuable contributions. + +Code Improvements +----------------- + +All development for this library happens at https://github.com/ianhi/mpl-point-clicker, + +First set up a dev environment. The instructions here use `mamba `_ which is open source +implementation of `conda` that offers significant speed ups. You can substitute ``conda`` for ``mamba`` or use ``venv`` without any ill-effects. + +.. code-block:: bash + + mamba create -n mpl_point_clicker-dev python + conda activate mpl_point_clicker-dev + +Now clone your fork of the Git repository and make an editable (``-e``) install. + +.. code-block:: bash + + git clone + cd mpl-point-clicker + pip install -e ".[dev]" + + +Working with Git +^^^^^^^^^^^^^^^^ + +Using Git/Github can confusing (https://xkcd.com/1597/) so if you're new to Git, you may find +it helpful to use a program like `Github Desktop `_ and to follow +a `guide `_. + +Also feel free to ask for help/advice on the relevant Github `issue `_. + +Documentation +------------- + +Following changes to the source files, you can view recent adjustments by building the documentation. + +1. Make sure you have installed the requirements for building the documentation: + +.. code-block:: bash + + cd mpl-point-clicker + pip install -e ".[doc]" + +2. Run the following commands: + +.. code-block:: bash + + cd docs + make html + +If you open the ``_build/html/index.html`` file in your browser you should now be able to see the rendered documentation. + +Autobuild the documentation +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Alternatively, you can use `sphinx-autobuild `_ to continuously watch the documentation for changes and rebuild it for you. +Sphinx-autobuild will be installed automatically by the above ``pip`` command, and we've added it to the ``Makefile``. All you need to do is: + +.. code-block:: bash + + cd docs + make watch + +In a few seconds your web browser should open up the documentation. Now whenever you save a file +the documentation will automatically regenerate and the webpage will refresh for you! diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..c1f1003 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,23 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +watch: + sphinx-autobuild . _build/html --open-browser --watch ../examples + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/_static/images/front-page.apng b/docs/_static/images/front-page.apng new file mode 100644 index 0000000..26117b1 Binary files /dev/null and b/docs/_static/images/front-page.apng differ diff --git a/docs/autoapi/mpl_point_clicker._clicker.clicker.rst b/docs/autoapi/mpl_point_clicker._clicker.clicker.rst new file mode 100644 index 0000000..856c91d --- /dev/null +++ b/docs/autoapi/mpl_point_clicker._clicker.clicker.rst @@ -0,0 +1,23 @@ +mpl\_point\_clicker.\_clicker.clicker +===================================== + +.. currentmodule:: mpl_point_clicker._clicker + +.. autoclass:: clicker + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~clicker.__init__ + ~clicker.get_positions + + + + + + \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..1990278 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,71 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath('../mpl_point_clicker')) + + +# -- Project information ----------------------------------------------------- + +project = 'mpl_point_clicker' +copyright = '2021, Ian Hunt-Isaak' +author = 'Ian Hunt-Isaak' + +# The full version, including alpha/beta/rc tags +from mpl_point_clicker import __version__ as release + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx_copybutton", + "sphinx_gallery.gen_gallery", + "sphinx.ext.napoleon", + "numpydoc", + 'sphinx.ext.autodoc', + 'sphinx.ext.inheritance_diagram', + 'autoapi.sphinx', +] + +sphinx_gallery_conf = { + "examples_dirs": "../examples", # path to your example scripts + "gallery_dirs": "examples", # path to where to save gallery generated output + "filename_pattern": "/.*", + "ignore_pattern": "/_.*", # https://www.debuggex.com/ +} + + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/docs/examples/example.ipynb b/docs/examples/example.ipynb new file mode 100644 index 0000000..05dd7a2 --- /dev/null +++ b/docs/examples/example.ipynb @@ -0,0 +1,54 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Basic Usage\n\nA short example showcasing how to use the library. The docstrings will be\nturns in REST by sphinx-gallery.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\nimport matplotlib.pyplot as plt\nfrom mpl_point_clicker import clicker\n\n\nimage = np.load('example_image.npy')\n\nfig, ax = plt.subplots()\nax.imshow(image, cmap='gray')\nklicker = clicker(ax, ['cells', 'pdms', 'media'], markers=['o', 'x', '*'])\nplt.show()\n\n\nprint(klicker.get_positions())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/docs/examples/example.py b/docs/examples/example.py new file mode 100644 index 0000000..c052b9d --- /dev/null +++ b/docs/examples/example.py @@ -0,0 +1,23 @@ +""" +----------- +Basic Usage +----------- + +A short example showcasing how to use the library. The docstrings will be +turns in REST by sphinx-gallery. +""" + +import numpy as np +import matplotlib.pyplot as plt +from mpl_point_clicker import clicker + + +image = np.load('example_image.npy') + +fig, ax = plt.subplots() +ax.imshow(image, cmap='gray') +klicker = clicker(ax, ['cells', 'pdms', 'media'], markers=['o', 'x', '*']) +plt.show() + + +print(klicker.get_positions()) diff --git a/docs/examples/example.py.md5 b/docs/examples/example.py.md5 new file mode 100644 index 0000000..d72eb3f --- /dev/null +++ b/docs/examples/example.py.md5 @@ -0,0 +1 @@ +e6fdb5c56f903dc51d9cb2b761a68a7e \ No newline at end of file diff --git a/docs/examples/example.rst b/docs/examples/example.rst new file mode 100644 index 0000000..184b68e --- /dev/null +++ b/docs/examples/example.rst @@ -0,0 +1,101 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "examples/example.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_examples_example.py: + + +----------- +Basic Usage +----------- + +A short example showcasing how to use the library. The docstrings will be +turns in REST by sphinx-gallery. + +.. GENERATED FROM PYTHON SOURCE LINES 9-24 + + + +.. image:: /examples/images/sphx_glr_example_001.png + :alt: example + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + Out: + + .. code-block:: none + + {'cells': array([], dtype=float64), 'pdms': array([], dtype=float64), 'media': array([], dtype=float64)} + + + + + + +| + +.. code-block:: default + + + import numpy as np + import matplotlib.pyplot as plt + from mpl_point_clicker import clicker + + + image = np.load('example_image.npy') + + fig, ax = plt.subplots() + ax.imshow(image, cmap='gray') + klicker = clicker(ax, ['cells', 'pdms', 'media'], markers=['o', 'x', '*']) + plt.show() + + + print(klicker.get_positions()) + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.343 seconds) + + +.. _sphx_glr_download_examples_example.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: example.py ` + + + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: example.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/docs/examples/example_codeobj.pickle b/docs/examples/example_codeobj.pickle new file mode 100644 index 0000000..b212b98 Binary files /dev/null and b/docs/examples/example_codeobj.pickle differ diff --git a/docs/examples/examples_jupyter.zip b/docs/examples/examples_jupyter.zip new file mode 100644 index 0000000..0f63f92 Binary files /dev/null and b/docs/examples/examples_jupyter.zip differ diff --git a/docs/examples/examples_python.zip b/docs/examples/examples_python.zip new file mode 100644 index 0000000..953e79a Binary files /dev/null and b/docs/examples/examples_python.zip differ diff --git a/docs/examples/images/sphx_glr_example_001.png b/docs/examples/images/sphx_glr_example_001.png new file mode 100644 index 0000000..46e6f87 Binary files /dev/null and b/docs/examples/images/sphx_glr_example_001.png differ diff --git a/docs/examples/images/thumb/sphx_glr_example_thumb.png b/docs/examples/images/thumb/sphx_glr_example_thumb.png new file mode 100644 index 0000000..10d7f36 Binary files /dev/null and b/docs/examples/images/thumb/sphx_glr_example_thumb.png differ diff --git a/docs/examples/index.rst b/docs/examples/index.rst new file mode 100644 index 0000000..c9a5a1d --- /dev/null +++ b/docs/examples/index.rst @@ -0,0 +1,62 @@ +:orphan: + + + +.. _sphx_glr_examples: + +--------------- +Example Gallery +--------------- + +Some explanatory text. See https://sphinx-gallery.github.io/stable/index.html for how to configure this page. + + + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /examples/images/thumb/sphx_glr_example_thumb.png + :alt: Basic Usage + + :ref:`sphx_glr_examples_example.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /examples/example +.. raw:: html + +
+ + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-gallery + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: examples_python.zip ` + + + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: examples_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/docs/examples/sg_execution_times.rst b/docs/examples/sg_execution_times.rst new file mode 100644 index 0000000..17f6ec2 --- /dev/null +++ b/docs/examples/sg_execution_times.rst @@ -0,0 +1,12 @@ + +:orphan: + +.. _sphx_glr_examples_sg_execution_times: + +Computation times +================= +**00:00.343** total execution time for **examples** files: + ++------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_examples_example.py` (``example.py``) | 00:00.343 | 0.0 MB | ++------------------------------------------------------+-----------+--------+ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..16e02f0 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,60 @@ +Welcome to mpl_point_clicker's documentation! +=================================================================== + +A library for interactively labelling points with an arbitrary number of classes + + + +.. image:: _static/images/front-page.apng + +Installation +^^^^^^^^^^^^^ + +.. code-block:: bash + + pip install mpl_point_clicker + +Usage +^^^^^ + +.. code-block:: python + + fig, ax = plt.subplots(constrained_layout=True) + image = np.random.rand(512, 512) + ax.imshow(image, cmap="gray") + klicker = clicker( + ax, + ["cell", "pdms", "media"], + markers=["o", "x", "*"] + ) + +You can then access the positions via ``klicker.get_positions``. + + + +Getting Help +^^^^^^^^^^^^ + +If you have a question on how to do something with ``mpl_point_clicker`` a great place +to ask it is: https://discourse.matplotlib.org/c/3rdparty/18. + +Reporting Issues +^^^^^^^^^^^^^^^^ + +Please report issues to https://github.com/ianhi/mpl-point-clicker/issues/new/choose + +.. toctree:: + :maxdepth: 3 + + examples/index + API + Contributing + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 0000000..358efc5 --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,5 @@ +--------------- +Example Gallery +--------------- + +Some explanatory text. See https://sphinx-gallery.github.io/stable/index.html for how to configure this page. diff --git a/examples/_example-playback.json b/examples/_example-playback.json new file mode 100644 index 0000000..1e25104 --- /dev/null +++ b/examples/_example-playback.json @@ -0,0 +1 @@ +{"figures": ["fig"], "schema-version": [0, 1, 0], "events": [{"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 596, "xdata": null, "y": 66, "ydata": null, "fig": "fig", "time": 0.526752233505249}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 563, "xdata": null, "y": 76, "ydata": null, "fig": "fig", "time": 0.5325524806976318}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 534, "xdata": null, "y": 87, "ydata": null, "fig": "fig", "time": 0.540614128112793}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 505, "xdata": 2004.2792207792209, "y": 99, "ydata": 1791.4999999999998, "fig": "fig", "time": 0.5502822399139404}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 483, "xdata": 1882.374458874459, "y": 112, "ydata": 1719.4653679653677, "fig": "fig", "time": 0.5597002506256104}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 462, "xdata": 1766.010822510823, "y": 126, "ydata": 1641.8896103896102, "fig": "fig", "time": 0.5643894672393799}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 441, "xdata": 1649.6471861471864, "y": 136, "ydata": 1586.4783549783547, "fig": "fig", "time": 0.5727107524871826}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 423, "xdata": 1549.9069264069267, "y": 149, "ydata": 1514.4437229437226, "fig": "fig", "time": 0.5825636386871338}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 414, "xdata": 1500.0367965367968, "y": 154, "ydata": 1486.738095238095, "fig": "fig", "time": 0.5889320373535156}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 410, "xdata": 1477.8722943722946, "y": 158, "ydata": 1464.5735930735927, "fig": "fig", "time": 0.5964739322662354}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 408, "xdata": 1466.7900432900433, "y": 160, "ydata": 1453.4913419913419, "fig": "fig", "time": 0.6049017906188965}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 408, "xdata": 1466.7900432900433, "y": 161, "ydata": 1447.9502164502162, "fig": "fig", "time": 0.6126611232757568}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 407, "xdata": 1461.2489177489178, "y": 163, "ydata": 1436.867965367965, "fig": "fig", "time": 0.621382474899292}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 406, "xdata": 1455.7077922077924, "y": 165, "ydata": 1425.785714285714, "fig": "fig", "time": 0.6285004615783691}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 402, "xdata": 1433.5432900432902, "y": 170, "ydata": 1398.0800865800863, "fig": "fig", "time": 0.6495261192321777}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 400, "xdata": 1422.4610389610393, "y": 172, "ydata": 1386.9978354978352, "fig": "fig", "time": 0.6521155834197998}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 397, "xdata": 1405.8376623376626, "y": 173, "ydata": 1381.4567099567098, "fig": "fig", "time": 0.6626441478729248}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 391, "xdata": 1372.5909090909095, "y": 178, "ydata": 1353.7510822510822, "fig": "fig", "time": 0.66805100440979}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 383, "xdata": 1328.261904761905, "y": 182, "ydata": 1331.58658008658, "fig": "fig", "time": 0.6763927936553955}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 378, "xdata": 1300.5562770562774, "y": 188, "ydata": 1298.3398268398266, "fig": "fig", "time": 0.6839990615844727}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 374, "xdata": 1278.3917748917752, "y": 192, "ydata": 1276.1753246753244, "fig": "fig", "time": 0.6922166347503662}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 371, "xdata": 1261.7683982683984, "y": 195, "ydata": 1259.5519480519479, "fig": "fig", "time": 0.6999232769012451}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 370, "xdata": 1256.227272727273, "y": 196, "ydata": 1254.0108225108222, "fig": "fig", "time": 0.7079756259918213}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 370, "xdata": 1256.227272727273, "y": 197, "ydata": 1248.4696969696968, "fig": "fig", "time": 0.7162230014801025}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 369, "xdata": 1250.6861471861473, "y": 200, "ydata": 1231.84632034632, "fig": "fig", "time": 0.7241995334625244}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_press_event", "step": 0, "x": 369, "xdata": 1250.6861471861473, "y": 200, "ydata": 1231.84632034632, "fig": "fig", "time": 0.897329568862915}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_release_event", "step": 0, "x": 369, "xdata": 1250.6861471861473, "y": 200, "ydata": 1231.84632034632, "fig": "fig", "time": 0.9002337455749512}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 335, "xdata": 1062.287878787879, "y": 210, "ydata": 1176.4350649350647, "fig": "fig", "time": 0.9011614322662354}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_press_event", "step": 0, "x": 335, "xdata": 1062.287878787879, "y": 210, "ydata": 1176.4350649350647, "fig": "fig", "time": 1.032003402709961}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 280, "xdata": 757.5259740259742, "y": 218, "ydata": 1132.1060606060603, "fig": "fig", "time": 1.0373563766479492}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_release_event", "step": 0, "x": 280, "xdata": 757.5259740259742, "y": 218, "ydata": 1132.1060606060603, "fig": "fig", "time": 1.037902593612671}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 271, "xdata": 707.6558441558443, "y": 232, "ydata": 1054.5303030303028, "fig": "fig", "time": 1.0389089584350586}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 270, "xdata": 702.1147186147186, "y": 234, "ydata": 1043.4480519480517, "fig": "fig", "time": 1.0443437099456787}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 269, "xdata": 696.5735930735932, "y": 236, "ydata": 1032.3658008658006, "fig": "fig", "time": 1.0618090629577637}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 268, "xdata": 691.0324675324675, "y": 239, "ydata": 1015.742424242424, "fig": "fig", "time": 1.06854248046875}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 267, "xdata": 685.4913419913421, "y": 241, "ydata": 1004.6601731601729, "fig": "fig", "time": 1.0766525268554688}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_press_event", "step": 0, "x": 267, "xdata": 685.4913419913421, "y": 241, "ydata": 1004.6601731601729, "fig": "fig", "time": 1.20650053024292}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 264, "xdata": 668.8679653679656, "y": 266, "ydata": 866.1320346320342, "fig": "fig", "time": 1.2098562717437744}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_release_event", "step": 0, "x": 264, "xdata": 668.8679653679656, "y": 266, "ydata": 866.1320346320342, "fig": "fig", "time": 1.2101666927337646}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 284, "xdata": 779.6904761904764, "y": 304, "ydata": 655.5692640692637, "fig": "fig", "time": 1.2109184265136719}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_press_event", "step": 0, "x": 284, "xdata": 779.6904761904764, "y": 304, "ydata": 655.5692640692637, "fig": "fig", "time": 1.3297996520996094}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 285, "xdata": 785.2316017316018, "y": 305, "ydata": 650.0281385281382, "fig": "fig", "time": 1.332697868347168}, {"button": 1, "dblclick": false, "inaxes": "ax", "key": null, "name": "button_release_event", "step": 0, "x": 285, "xdata": 785.2316017316018, "y": 305, "ydata": 650.0281385281382, "fig": "fig", "time": 1.3328969478607178}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 343, "xdata": 1106.6168831168832, "y": 321, "ydata": 561.3701298701296, "fig": "fig", "time": 1.3333680629730225}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 368, "xdata": 1245.1450216450219, "y": 325, "ydata": 539.2056277056274, "fig": "fig", "time": 1.342395305633545}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 388, "xdata": 1355.9675324675327, "y": 331, "ydata": 505.9588744588741, "fig": "fig", "time": 1.3491580486297607}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 403, "xdata": 1439.0844155844156, "y": 335, "ydata": 483.7943722943719, "fig": "fig", "time": 1.3625781536102295}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 420, "xdata": 1533.28354978355, "y": 340, "ydata": 456.08874458874425, "fig": "fig", "time": 1.3659026622772217}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 432, "xdata": 1599.7770562770565, "y": 344, "ydata": 433.92424242424204, "fig": "fig", "time": 1.372999906539917}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 439, "xdata": 1638.564935064935, "y": 345, "ydata": 428.3831168831166, "fig": "fig", "time": 1.3805038928985596}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 443, "xdata": 1660.7294372294373, "y": 347, "ydata": 417.3008658008655, "fig": "fig", "time": 1.3888559341430664}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 448, "xdata": 1688.4350649350654, "y": 349, "ydata": 406.2186147186144, "fig": "fig", "time": 1.3962314128875732}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 455, "xdata": 1727.222943722944, "y": 351, "ydata": 395.1363636363633, "fig": "fig", "time": 1.404836893081665}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 457, "xdata": 1738.3051948051952, "y": 351, "ydata": 395.1363636363633, "fig": "fig", "time": 1.4123609066009521}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 461, "xdata": 1760.469696969697, "y": 352, "ydata": 389.5952380952376, "fig": "fig", "time": 1.4207994937896729}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 462, "xdata": 1766.010822510823, "y": 353, "ydata": 384.0541125541122, "fig": "fig", "time": 1.4281563758850098}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 463, "xdata": 1771.5519480519483, "y": 353, "ydata": 384.0541125541122, "fig": "fig", "time": 1.436608076095581}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 465, "xdata": 1782.6341991341992, "y": 354, "ydata": 378.51298701298674, "fig": "fig", "time": 1.4464635848999023}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 467, "xdata": 1793.7164502164505, "y": 354, "ydata": 378.51298701298674, "fig": "fig", "time": 1.4561822414398193}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 471, "xdata": 1815.8809523809527, "y": 355, "ydata": 372.9718614718611, "fig": "fig", "time": 1.4624698162078857}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 476, "xdata": 1843.5865800865804, "y": 356, "ydata": 367.43073593073564, "fig": "fig", "time": 1.4709932804107666}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 482, "xdata": 1876.8333333333335, "y": 359, "ydata": 350.80735930735887, "fig": "fig", "time": 1.4774906635284424}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 489, "xdata": 1915.6212121212125, "y": 361, "ydata": 339.72510822510776, "fig": "fig", "time": 1.485335350036621}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 496, "xdata": 1954.409090909091, "y": 366, "ydata": 312.0194805194801, "fig": "fig", "time": 1.4935824871063232}, {"button": null, "dblclick": false, "inaxes": "ax", "key": null, "name": "motion_notify_event", "step": 0, "x": 504, "xdata": 1998.7380952380954, "y": 368, "ydata": 300.937229437229, "fig": "fig", "time": 1.5006871223449707}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 517, "xdata": null, "y": 372, "ydata": null, "fig": "fig", "time": 1.5078258514404297}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 531, "xdata": null, "y": 379, "ydata": null, "fig": "fig", "time": 1.5156428813934326}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 548, "xdata": null, "y": 384, "ydata": null, "fig": "fig", "time": 1.525132656097412}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 558, "xdata": null, "y": 386, "ydata": null, "fig": "fig", "time": 1.532383680343628}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 568, "xdata": null, "y": 391, "ydata": null, "fig": "fig", "time": 1.5403234958648682}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 572, "xdata": null, "y": 393, "ydata": null, "fig": "fig", "time": 1.5480124950408936}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 573, "xdata": null, "y": 393, "ydata": null, "fig": "fig", "time": 1.5560240745544434}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 576, "xdata": null, "y": 394, "ydata": null, "fig": "fig", "time": 1.5638339519500732}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 576, "xdata": null, "y": 393, "ydata": null, "fig": "fig", "time": 1.7012977600097656}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 576, "xdata": null, "y": 392, "ydata": null, "fig": "fig", "time": 1.7091434001922607}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 576, "xdata": null, "y": 390, "ydata": null, "fig": "fig", "time": 1.7167606353759766}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 578, "xdata": null, "y": 385, "ydata": null, "fig": "fig", "time": 1.7250325679779053}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 579, "xdata": null, "y": 382, "ydata": null, "fig": "fig", "time": 1.7332217693328857}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 580, "xdata": null, "y": 380, "ydata": null, "fig": "fig", "time": 1.7412590980529785}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 580, "xdata": null, "y": 379, "ydata": null, "fig": "fig", "time": 1.7489633560180664}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 581, "xdata": null, "y": 378, "ydata": null, "fig": "fig", "time": 1.7574255466461182}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 582, "xdata": null, "y": 378, "ydata": null, "fig": "fig", "time": 1.7878508567810059}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 584, "xdata": null, "y": 378, "ydata": null, "fig": "fig", "time": 1.8038747310638428}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 586, "xdata": null, "y": 378, "ydata": null, "fig": "fig", "time": 1.8118035793304443}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 588, "xdata": null, "y": 378, "ydata": null, "fig": "fig", "time": 1.8199374675750732}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 590, "xdata": null, "y": 379, "ydata": null, "fig": "fig", "time": 1.8292319774627686}, {"button": null, "dblclick": false, "inaxes": null, "key": null, "name": "motion_notify_event", "step": 0, "x": 592, "xdata": null, "y": 381, "ydata": null, "fig": "fig", "time": 1.8378210067749023}, {"artist": \ No newline at end of file diff --git a/examples/_gen_playback.py b/examples/_gen_playback.py new file mode 100644 index 0000000..9f88d3c --- /dev/null +++ b/examples/_gen_playback.py @@ -0,0 +1,4 @@ +from mpl_playback.record import record_file + +# record_file("heatmap_slicer.py", "fig") +record_file("example.py", "fig") diff --git a/examples/_playback.py b/examples/_playback.py new file mode 100644 index 0000000..c34ec9b --- /dev/null +++ b/examples/_playback.py @@ -0,0 +1,3 @@ +from mpl_playback.playback import playback_file + +playback_file("_example-playback.json", "example.py", "ohboi.gif") diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..c052b9d --- /dev/null +++ b/examples/example.py @@ -0,0 +1,23 @@ +""" +----------- +Basic Usage +----------- + +A short example showcasing how to use the library. The docstrings will be +turns in REST by sphinx-gallery. +""" + +import numpy as np +import matplotlib.pyplot as plt +from mpl_point_clicker import clicker + + +image = np.load('example_image.npy') + +fig, ax = plt.subplots() +ax.imshow(image, cmap='gray') +klicker = clicker(ax, ['cells', 'pdms', 'media'], markers=['o', 'x', '*']) +plt.show() + + +print(klicker.get_positions()) diff --git a/examples/example_image.npy b/examples/example_image.npy new file mode 100644 index 0000000..2cc0eaf Binary files /dev/null and b/examples/example_image.npy differ diff --git a/examples/ohboi.gif b/examples/ohboi.gif new file mode 100644 index 0000000..61915f8 Binary files /dev/null and b/examples/ohboi.gif differ diff --git a/mpl_point_clicker/__init__.py b/mpl_point_clicker/__init__.py new file mode 100644 index 0000000..7bbca52 --- /dev/null +++ b/mpl_point_clicker/__init__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright (c) Ian Hunt-Isaak. +# Distributed under the terms of the Modified BSD License. + +from ._version import __version__ + +from ._clicker import clicker diff --git a/mpl_point_clicker/_clicker.py b/mpl_point_clicker/_clicker.py new file mode 100644 index 0000000..632a8c2 --- /dev/null +++ b/mpl_point_clicker/_clicker.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright (c) Ian Hunt-Isaak. +# Distributed under the terms of the Modified BSD License. + +__all__ = [ + "clicker", +] +import numpy as np +from matplotlib.backend_bases import MouseButton +from numbers import Integral + + +class clicker: + def __init__( + self, + ax, + classes, + init_class=None, + markers=None, + colors=None, + legend_bbox=(1.04, 1), + legend_loc="upper left", + pick_dist=10, + **line_kwargs, + ): + """ + Parameters + ---------- + ax : matplotlib axis + classes : int or list + init_class : int, or str, optional + The initial class to use, otherwise will be the first + class in *classes* + markers : list + The marker styles to use. + colors : list + legend_bbox : tuple + bbox to use for the legend + legend_loc : str or int + loc to use for the legend + pick_dist : int + Picker distance for legend + **line_kwargs : kwargs + Line2D objects (from ax.plot) are used to generate the markers. + line_kwargs will be passed through to all of the `ax.plot` calls. + """ + if isinstance(classes, Integral): + self._classes = list(range(classes)) + else: + self._classes = classes + + if init_class is None: + self._current_class = self._classes[0] + else: + if init_class not in self._classes: + raise ValueError( + f"init_class must be a valid class. Got {init_class} " + f"while valid classes are {self._classes}" + ) + self._current_class = init_class + + if colors is None: + colors = [None] * len(self._classes) + if markers is None: + markers = ["o"] * len(self._classes) + + self.ax = ax + self._lines = {} + linestyle = line_kwargs.pop("linestyle", "") + for i, c in enumerate(self._classes): + (self._lines[c],) = self.ax.plot( + [], + [], + color=colors[i], + marker=markers[i], + label=c, + linestyle=linestyle, + **line_kwargs, + ) + self._leg = self.ax.legend(bbox_to_anchor=legend_bbox, loc=legend_loc) + self._leg_artists = {} + self._class_leg_artists = {} + for legline, legtext, klass in zip( + self._leg.get_lines(), self._leg.get_texts(), self._classes + ): + legline.set_picker(pick_dist) + legtext.set_picker(pick_dist) + self._leg_artists[legtext] = klass + self._leg_artists[legline] = klass + self._class_leg_artists[klass] = (legline, legline._legmarker, legtext) + + self._fig = self.ax.figure + self._fig.canvas.mpl_connect("button_press_event", self._clicked) + self._fig.canvas.mpl_connect("pick_event", self._on_pick) + self._positions = {c: [] for c in self._classes} + self._update_legend_alpha() + + def get_positions(self, copy=True): + return {k: np.asarray(v) for k, v in self._positions.items()} + + def _on_pick(self, event): + # On the pick event, find the original line corresponding to the legend + # proxy line, and toggle its visibility. + klass = self._leg_artists[event.artist] + self._current_class = klass + self._update_legend_alpha() + + def _update_legend_alpha(self): + for c in self._classes: + alpha = 1 if c == self._current_class else 0.2 + for a in self._class_leg_artists[c]: + a.set_alpha(alpha) + self._fig.canvas.draw() + + def _clicked(self, event): + if not self._fig.canvas.widgetlock.available(self): + return + print('in clicked') + if event.inaxes is self.ax: + print('in inaxes') + print(event.button) + if event.button is MouseButton.LEFT: + print('in here!') + self._positions[self._current_class].append((event.xdata, event.ydata)) + print(self._positions[self._current_class]) + self._update_points(self._current_class) + elif event.button is MouseButton.RIGHT: + pos = self._positions[self._current_class] + if len(pos) == 0: + return + dists = np.linalg.norm( + np.asarray([event.xdata, event.ydata])[None, None, :] + - np.asarray(pos)[None, :, :], + axis=-1, + ) + idx = np.argmin(dists[0]) + pos.pop(idx) + self._update_points(self._current_class) + + def _update_points(self, klass=None): + if klass is None: + klasses = self._classes + else: + klasses = [klass] + for c in klasses: + new_off = np.array(self._positions[c]) + if new_off.ndim == 1: + # no points left + new_off = np.zeros([0, 2]) + self._lines[c].set_data(new_off.T) + self._fig.canvas.draw() diff --git a/mpl_point_clicker/_version.py b/mpl_point_clicker/_version.py new file mode 100644 index 0000000..e2537dd --- /dev/null +++ b/mpl_point_clicker/_version.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright (c) Ian Hunt-Isaak. +# Distributed under the terms of the Modified BSD License. + +version_info = (0, 1, 0) +__version__ = ".".join(map(str, version_info)) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4417d08 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[tool.black] +skip-string-normalization = true + +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..e67dd0d --- /dev/null +++ b/setup.cfg @@ -0,0 +1,36 @@ +[metadata] +name=mpl_point_clicker +author=Ian Hunt-Isaak +author_email=ianhuntisaak@gmail.com +url = https://github.com/ianhi/mpl-point-clicker +license=BSD 3-Clause +packages = find: + +[options] +install_requires = + matplotlib + numpy + +classifiers = + Intended Audience :: Developers + Intended Audience :: Science/Research + License :: OSI Approved :: BSD License + Programming Language :: Python + Programming Language :: Python :: 3 + Framework :: Matplotlib + +[options.extras_require] +test = + black + pytest + pytest-mpl +doc = + sphinx + numpydoc + sphinx_rtd_theme + sphinx-copybutton + sphinx-autobuild + sphinx_gallery>=0.8.2 +dev = + %(test)s + %(doc)s diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ce5f9a9 --- /dev/null +++ b/setup.py @@ -0,0 +1,17 @@ +from os import path + +from setuptools import find_packages, setup + +# extract version +path = path.realpath("mpl_point_clicker/_version.py") +version_ns = {} +with open(path, encoding="utf8") as f: + exec(f.read(), {}, version_ns) +version = version_ns["__version__"] + +setup_args = dict( + version=version, +) + +if __name__ == "__main__": + setup(**setup_args)