Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
6087d69
Add ColormapSet for color blending
izkgao Apr 30, 2025
d6961b2
Update Image.make_active to support CARTA 5.0.0+ API changes
izkgao Apr 30, 2025
9fa89de
Add ColorBlending class with layer management and color blending func…
izkgao May 5, 2025
85b3528
Add color blending documentation and update image handling examples
izkgao May 5, 2025
06445bc
Add ColorBlending.from_files method to create blended images directly…
izkgao Sep 10, 2025
266f1c1
Add tests for ColorBlending and Layer classes
izkgao Sep 10, 2025
606c9f3
Improve mock consistency
izkgao Sep 11, 2025
8e560b9
Merge branch 'dev' into color_blending
izkgao Apr 10, 2026
34bf41b
Fix duplicate imports and docstring formatting in colorblending and i…
izkgao Apr 10, 2026
5d4fca4
Add colorblending module to API documentation
izkgao Apr 10, 2026
032949d
Refactor layers property to use Layer.from_list method
izkgao Apr 10, 2026
2e0abce
Fix indentation in Layer class docstring
izkgao Apr 10, 2026
5c52466
Rename reorder_layers to set_layer_sequence and add support for layer…
izkgao Apr 14, 2026
06fff60
Simplify layer_list implementation by using frames.length instead of …
izkgao Apr 14, 2026
b7af8dc
Add explicit validation to prevent deletion of base layer in ColorBle…
izkgao Apr 14, 2026
fbcf72f
Add validation to prevent alpha list length mismatch in set_alpha method
izkgao Apr 14, 2026
6596ab3
Refactor ColorBlending to use store_id instead of image_id and add fr…
izkgao Apr 14, 2026
9a20cfa
Add color_blending_list method to Session class for retrieving open c…
izkgao Apr 14, 2026
d9bcab4
Update quickstart documentation to clarify ColorBlending layer termin…
izkgao Apr 14, 2026
a34e646
Drop support for carta version < 5.0 for make_active
izkgao Apr 14, 2026
4ce4429
Update documentation references to use new wcs_overlay module paths f…
izkgao Apr 14, 2026
25e0baa
Add validation test for invalid colormap name in Layer.set_colormap m…
izkgao Apr 14, 2026
cb22ed5
Fix indentation in Layer.from_layer_ids docstring return section
izkgao Apr 14, 2026
91e952d
Add validation to prevent exceeding maximum initial layer count in Co…
izkgao Apr 14, 2026
3f1c4fe
Rename set_vectoroverlay_visible method to set_vector_overlay_visible…
izkgao Apr 14, 2026
d337759
Rename ColormapSet.Rainbow to RAINBOW for consistent uppercase enum n…
izkgao Apr 14, 2026
345c17d
Add module docstring to colorblending.py describing color blending fu…
izkgao Apr 14, 2026
2bf9d1f
Replace explicit type annotations with placeholder format strings in …
izkgao Apr 14, 2026
e16c9f4
Change periods to colons in ColorBlending section introductory senten…
izkgao Apr 14, 2026
2812ffa
Update test_make_active to use setActiveImageByFileId action instead …
izkgao Apr 14, 2026
509d4e2
Fix ColorBlending initialization to rebuild layers from requested ima…
izkgao Apr 15, 2026
af48cf3
Add validation to reject duplicate layer indices in ColorBlending.set…
izkgao Apr 15, 2026
2f7d76b
Remove automatic inversion reset when applying colormap sets in Color…
izkgao Apr 15, 2026
56e019d
Fix imageview_id property to search imageList by store_id instead of …
izkgao Apr 15, 2026
6e02ba2
Convert layer_indices to list before comparing with current_layer_ind…
izkgao Apr 15, 2026
9ea1a66
Style check
izkgao Apr 15, 2026
f024c5c
Merge branch 'dev' into zhenkai/color_blending
izkgao Apr 20, 2026
50a21bc
Implement ImageBase class
izkgao Apr 20, 2026
6c2d9a1
Update Image.__repr__ to use [Closed] label instead of [Invalid] for …
izkgao Apr 20, 2026
ace85cb
Update ColorBlending and Layer to use color_blending_id and improve _…
izkgao Apr 20, 2026
566dfb0
Add get_image method and refactor image list handling to support hete…
izkgao Apr 20, 2026
07353f1
Filter image_list to return only Image instances in _images helper me…
izkgao Apr 20, 2026
4302da5
Update tests
izkgao Apr 20, 2026
d424e90
Update terminology from "file-based" to "frame-backed" in image docst…
izkgao Apr 20, 2026
5c9e334
Set default colormap set based on number of files for open_as_color_b…
izkgao Apr 20, 2026
ac58b5f
Extract ImageBase class into separate module
izkgao Apr 20, 2026
7be1e65
Add parametrized test for default colormap set selection in open_as_c…
izkgao Apr 20, 2026
4a06836
Reorganize ColorBlending methods with section comments for better cod…
izkgao Apr 20, 2026
ec1501a
Add image_view_order property to Layer class and update __repr__ to u…
izkgao Apr 20, 2026
68f6b27
Set default colormap set based on number of images for create_color_b…
izkgao Apr 20, 2026
57bdb55
Add documentation for image list inspection and update color blending…
izkgao Apr 20, 2026
e7a8370
Add validation to prevent rebasing existing color blendings when crea…
izkgao Apr 21, 2026
e13fd3a
Rename active_frame to active_image and add support for ColorBlending…
izkgao Apr 22, 2026
b47334b
Rename get_image to image_by_id and update signature to use keyword a…
izkgao Apr 22, 2026
a4630de
Update set_cursor to use activeFrame.setCursorPosition action and rem…
izkgao Apr 22, 2026
c6e8a54
Remove ColorBlending factory methods and simplify create_color_blendi…
izkgao Apr 22, 2026
d6a479e
Reorganize Session methods with section comments for color blending a…
izkgao Apr 22, 2026
9d86616
Fix default colormap set selection to use layer count instead of tota…
izkgao Apr 22, 2026
bebe9e4
Add carta_version property to Session and include it in __repr__ when…
izkgao Apr 22, 2026
76a6c37
Update documentation references to use new wcs module paths and impro…
izkgao Apr 22, 2026
49e4e78
Add zoom_to_size method to ColorBlending class with validation and de…
izkgao Apr 22, 2026
21b22f0
Refactor ImageBase to use ABC pattern, move image_view_order implemen…
izkgao Apr 22, 2026
1c5b8ab
Update quickstart documentation to use unpacked layer variables inste…
izkgao Apr 22, 2026
8940ca3
Update test regex pattern to match abstract method error messages mor…
izkgao Apr 22, 2026
f0b86ae
Fix a test warning
izkgao Apr 22, 2026
1e4d2fe
Add test coverage
izkgao Apr 22, 2026
8620e85
Improve error messages to include actual values and update tests to v…
izkgao Apr 22, 2026
c982c89
Add session.clear_spatial_reference() call before setting new spatial…
izkgao Apr 22, 2026
ab1753d
Add validation to reject empty file list in open_as_color_blending me…
izkgao Apr 22, 2026
8f6ab9e
Optimize active_image method to retrieve activeImage object once inst…
izkgao Apr 22, 2026
65eaced
Refactor _find_image_view_order to use imageViewConfigStore.getImageL…
izkgao Apr 23, 2026
298043c
Rename loop variable from 'call' to 'call_' to avoid shadowing built-…
izkgao Apr 23, 2026
9693fa5
Add connection validation during session creation with minimum CARTA …
izkgao Apr 28, 2026
5a7a924
Update uv sync command to include all extras in contributor setup ins…
izkgao Apr 28, 2026
d421fe9
Simplify __repr__ method by removing manual cache checking and relyin…
izkgao Apr 28, 2026
c69d57a
Merge branch 'dev' into zhenkai/color_blending
izkgao Apr 28, 2026
6484a3e
Merge branch 'zhenkai/color_blending' into zhenkai/check_version
izkgao Apr 28, 2026
9b3b423
Add carta_version_requirement parameter to session creation methods t…
izkgao Apr 30, 2026
8a5e6f2
Rename colorblending module to color_blending and update all referenc…
izkgao May 4, 2026
e4c89bf
Remove set_layer_sequence method from ColorBlending class because it …
izkgao May 21, 2026
c411b4c
Remove set_layer_sequence tests after removing the method from ColorB…
izkgao May 21, 2026
0d8c8bc
Rename set_layer to set_layer_image and swap parameter order to put l…
izkgao Aug 12, 2026
955ff8c
Refactor ImageBase to use class-level IMAGE_TYPE constant and add ima…
izkgao Aug 12, 2026
51e2c48
Add False parameter to setSpatialReference action call to disable col…
izkgao Aug 12, 2026
d46311f
Add Layer.delete method and update ColorBlending.delete_layer to hand…
izkgao Aug 12, 2026
4168f29
Refactor set_alpha validation to use Evaluate validator with depth at…
izkgao Aug 12, 2026
69aedb9
Add colormap and alpha properties to Layer class and improve docstrin…
izkgao Aug 13, 2026
638b032
Add Layer.set_image method and allow ColorBlending.set_layer_image to…
izkgao Aug 13, 2026
26f4983
Add Session.images and Session.color_blendings methods to retrieve im…
izkgao Aug 13, 2026
9f44dec
Update quickstart documentation
izkgao Aug 13, 2026
14de716
Rename image_ids parameter to file_ids in wcs_overlay for consistency…
izkgao Aug 13, 2026
e6d93ce
Add inverted property to Layer class and include it in repr output
izkgao Aug 13, 2026
2f49b2a
Update validate decorator to support keyword-only parameters by inclu…
izkgao Aug 13, 2026
8ea65e8
Add validation decorators to ColorBlending.add_layer, ColorBlending.s…
izkgao Aug 13, 2026
272366c
Simplify Session.image_by_id to directly instantiate Image and ColorB…
izkgao Aug 13, 2026
03be496
Rename image_view_order to view_index and file_id to image_id through…
izkgao Aug 13, 2026
189a3a7
Rename alpha to alphas and layer_list to layers, add optional layer_i…
izkgao Aug 13, 2026
8643125
Add validation to ColorBlending.set_layer_image to prevent duplicate …
izkgao Aug 13, 2026
eac8119
Add optional images parameter to Session.create_color_blending to all…
izkgao Aug 13, 2026
665cef5
Refactor views, images, and color_blendings methods to use single fro…
izkgao Aug 14, 2026
c33caa5
Remove unnecessary docstring modifications
izkgao Aug 14, 2026
ef07dae
Refactor views, images, and color_blendings methods to use walrus ope…
izkgao Aug 14, 2026
a88097c
Add deprecated decorator for marking deprecated callables, fix logger…
izkgao Aug 17, 2026
526f347
Add deprecated decorator to Session.image_list, Session.image_by_id, …
izkgao Aug 17, 2026
bffaac7
Avoid the incorrect use of self.get_value("activeImage") and retrieve…
izkgao Aug 17, 2026
06fd465
Refactor Session.image_by_id to use view_by_id
izkgao Aug 17, 2026
b8b7642
Change deprecated directive to warning directive in docstrings for im…
izkgao Aug 17, 2026
3ff2a72
Merge branch 'zhenkai/color_blending' into zhenkai/check_version
izkgao Aug 18, 2026
05efdcc
Refactor version checking
izkgao Aug 18, 2026
ad8a189
Merge branch 'dev' into zhenkai/check_version
izkgao Aug 18, 2026
516cdb2
Remove minimum_carta_version parameter and change default version_mis…
izkgao Aug 19, 2026
7e11b37
Remove outdated frontendVersion error message from connection failure…
izkgao Aug 19, 2026
b645f39
Removing unnecessary line breaks
izkgao Aug 19, 2026
f99c537
Remove frontendVersion from connection failure test assertion
izkgao Aug 19, 2026
a10d17b
Remove unnecessary line break
izkgao Aug 19, 2026
a4f428a
Refactor version compatibility checking to use data-driven approach w…
izkgao Aug 25, 2026
393dc17
Add pull request template with comprehensive checklist for developmen…
izkgao Aug 25, 2026
e3306eb
Add development documentation covering version compatibility workflow…
izkgao Aug 25, 2026
54f61b4
Add extra line break before compatibility suggestions in error messages
izkgao Aug 25, 2026
8fa5c9d
Update package name to carta-python and revise installation instructi…
izkgao Aug 25, 2026
2973266
Update error handling for CARTA versions earlier than 6.0.0
izkgao Aug 25, 2026
d1b0502
Update version to 2.0.0-dev for development cycle
izkgao Aug 25, 2026
97e79cf
Update quickstart documentation to reflect PyPI release and compatibi…
izkgao Aug 25, 2026
769af6a
Update pull request template headers to use markdown heading syntax
izkgao Aug 25, 2026
3b5dfa9
Relax compatibility range restrictions to allow cross-major CARTA ver…
izkgao Aug 26, 2026
09d010c
Reorganize development documentation by moving release cycle content …
izkgao Aug 26, 2026
b6732c4
Refactor error message formatting into dedicated module with improved…
izkgao Aug 26, 2026
31d0225
Add carta_python.egg-info to gitignore for new package name
izkgao Aug 27, 2026
3a47c86
Improve readability of upgrade instructions by separating environment…
izkgao Aug 27, 2026
976292b
Merge branch 'dev' into zhenkai/check_version
izkgao Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### Description

Linked issues and companion PRs (if any), what is implemented or fixed, and
how to test it.

### Checklist

For linked issues (if any):

- [ ] assignee and labels added
- [ ] GitHub Project estimate added

For the pull request:

- [ ] reviewers and assignee added
- [ ] GitHub Project estimate added
- [ ] tests added or updated / no tests needed
- [ ] documentation updated / no documentation update needed

For dependencies and compatibility:

- [ ] companion frontend or backend PRs linked / no companion PRs needed
- [ ] `pyproject.toml` and `uv.lock` updated / no dependency changes
- [ ] CARTA compatibility table updated when the minimum CARTA series changes
or `dev` starts a new `carta-python` major/minor series / no compatibility
change

For pull requests targeting `main`:

- [ ] `VERSION.txt` finalized for the release
- [ ] final compatibility entry matches the release major/minor version and
minimum supported CARTA series
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ __pycache__
build
dist
carta.egg-info
carta_python.egg-info
.DS_Store
.coverage
.coverage.*
Expand Down
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ carta-python

This is a prototype of a scripting interface which uses a generic HTTP interface in the CARTA backend as a proxy to call actions on the CARTA frontend.

This package is not yet published on PyPi, but can be installed from the local repository directory with `pip`. Requires Python 3.10 or later. Ensure that you're using the corresponding `pip`, either using a `virtualenv` or the appropriate system executable, which may be called `pip3`. Required dependencies should be installed automatically:
Install the latest release from PyPI. Requires Python 3.10 or later. Required dependencies are installed automatically:

pip install .
pip install --upgrade carta-python

To create a new frontend session which is controlled by the wrapper instead of connecting to an existing frontend session, you also need to install the `selenium` Python library:
To create a new frontend session which is controlled by the wrapper instead of connecting to an existing frontend session, install the optional browser dependencies:

pip install selenium

If you prefer installing the optional browser dependencies via package extras, the equivalent command is:

pip install ".[browser]"
pip install --upgrade "carta-python[browser]"

You also need to make sure that your desired browser is installed, together with a corresponding web driver.

Expand Down Expand Up @@ -53,7 +49,7 @@ Alternative contributor setup with uv
If you prefer a synced local development environment, `uv` is also supported:

```
uv sync
uv sync --all-extras
```

This creates a local virtual environment, installs the package in editable mode, and syncs the default development groups.
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.0
2.0.0-dev
99 changes: 89 additions & 10 deletions carta/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,46 @@

import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
try:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
except ModuleNotFoundError as error:
if error.name != "selenium":
raise
webdriver = None
Options = None
By = None
NoSuchElementException = None
_SELENIUM_IMPORT_ERROR = error
else:
_SELENIUM_IMPORT_ERROR = None

from .backend import Backend
from .util import CartaBadSession
from .constants import VersionMismatchAction
from .util import CartaBadSession, logger
from .protocol import Protocol
from .session import Session


def _require_selenium():
if _SELENIUM_IMPORT_ERROR is not None:
raise CartaBadSession(
"Browser automation requires the optional Selenium dependency.\n\n"
"Install it with pip:\n"
" python -m pip install --upgrade \"carta-python[browser]\"\n\n"
"For a uv-managed script:\n"
" uv add --script your_script.py \"carta-python[browser]\"\n"
" uv run your_script.py\n\n"
"If your script already pins carta-python, include the same "
"version constraint when adding the browser extra, for example:\n"
" uv add --script your_script.py "
"\"carta-python[browser]~=2.0.0\" --upgrade-package carta-python\n"
" uv run your_script.py"
) from _SELENIUM_IMPORT_ERROR


class Browser:
"""The top-level browser class.

Expand All @@ -34,7 +63,7 @@ class Browser:
def __init__(self, driver_class, **kwargs):
self.driver = driver_class(**kwargs)

def new_session_from_url(self, frontend_url, token=None, backend=None, timeout=10, debug_no_auth=False):
def new_session_from_url(self, frontend_url, token=None, backend=None, timeout=10, debug_no_auth=False, connection_check_timeout=10, version_mismatch_action=VersionMismatchAction.ERROR):
"""Create a new session by connecting to an existing backend.

You can use :obj:`carta.session.Session.create`, which wraps this method.
Expand All @@ -51,6 +80,12 @@ def new_session_from_url(self, frontend_url, token=None, backend=None, timeout=1
The number of seconds to spend parsing the frontend for connection information. 10 seconds by default.
debug_no_auth : boolean
Disable authentication. This should be set if the backend has been started with the ``--debug_no_auth`` option. This is provided for debugging purposes only and should not be used under normal circumstances.
connection_check_timeout : number, optional
Maximum time in seconds to wait for the startup validation action.
Default: 10 seconds.
version_mismatch_action : :obj:`carta.constants.VersionMismatchAction`, optional
Whether a version mismatch should produce a warning or raise an
exception. ``VersionMismatchAction.ERROR`` is the default.

Returns
-------
Expand All @@ -65,8 +100,13 @@ def new_session_from_url(self, frontend_url, token=None, backend=None, timeout=1
If an invalid URL was provided.
CartaBadSession
If the session object could not be created.
CartaUnsupportedVersion
If the wrapper cannot verify the connected CARTA version or it
does not satisfy the minimum requirement, and
``version_mismatch_action`` is ``VersionMismatchAction.ERROR``.
"""

_require_selenium()
protocol = Protocol(frontend_url, token, debug_no_auth=debug_no_auth)

if protocol.controller_auth:
Expand Down Expand Up @@ -97,9 +137,15 @@ def new_session_from_url(self, frontend_url, token=None, backend=None, timeout=1
if not session_id:
self.exit(f"Could not parse session ID from frontend. Last error: {last_error}")

return Session(session_id, protocol, browser=self, backend=backend)
session = Session(session_id, protocol, browser=self, backend=backend)
try:
session._validate_session(timeout=connection_check_timeout, version_mismatch_action=version_mismatch_action)
except Exception:
self._close_after_error()
raise
return session

def new_session_with_backend(self, executable_path="carta", remote_host=None, params=tuple(), timeout=10, token=None, frontend_url_timeout=10):
def new_session_with_backend(self, executable_path="carta", remote_host=None, params=tuple(), timeout=10, token=None, frontend_url_timeout=10, connection_check_timeout=10, version_mismatch_action=VersionMismatchAction.ERROR):
"""Create a new session after launching a new backend process.

You can use :obj:`carta.session.Session.start_and_create`, which wraps this method. This method starts a backend process, parses the frontend URL from the output, and calls :obj:`carta.browser.Browser.new_session_from_url`.
Expand All @@ -118,6 +164,12 @@ def new_session_with_backend(self, executable_path="carta", remote_host=None, pa
The security token to use. Parsed from the backend output by default.
frontend_url_timeout : integer
How long to keep checking the backend output for the frontend URL. Default: 10 seconds.
connection_check_timeout : number, optional
Maximum time in seconds to wait for the startup validation action.
Default: 10 seconds.
version_mismatch_action : :obj:`carta.constants.VersionMismatchAction`, optional
Whether a version mismatch should produce a warning or raise an
exception. ``VersionMismatchAction.ERROR`` is the default.

Returns
-------
Expand All @@ -132,26 +184,52 @@ def new_session_with_backend(self, executable_path="carta", remote_host=None, pa
If an invalid URL was provided.
CartaBadSession
If the session object could not be created.
CartaUnsupportedVersion
If the wrapper cannot verify the connected CARTA version or it
does not satisfy the minimum requirement, and
``version_mismatch_action`` is ``VersionMismatchAction.ERROR``.
"""

backend = Backend(("--no_browser", "--enable_scripting", *params), executable_path, remote_host, token, frontend_url_timeout=frontend_url_timeout, session_creation_timeout=0)
if not backend.start():
self.exit(f"CARTA backend exited unexpectedly:\n{''.join(backend.errors)}")

if backend.frontend_url is None:
backend.stop()
self.exit("Could not parse CARTA frontend URL from backend output.")

return self.new_session_from_url(backend.frontend_url, backend.token, backend=backend, timeout=timeout, debug_no_auth=backend.debug_no_auth)
try:
return self.new_session_from_url(
backend.frontend_url,
backend.token,
backend=backend,
timeout=timeout,
debug_no_auth=backend.debug_no_auth,
connection_check_timeout=connection_check_timeout,
version_mismatch_action=version_mismatch_action,
)
except Exception:
try:
backend.stop()
except Exception:
logger.exception("Failed to stop CARTA backend after session creation failed.")
raise

def exit(self, msg):
"""Exit the browser with an error."""
self.close()
self._close_after_error()
raise CartaBadSession(msg)

def close(self):
"""Shut down the browser driver."""
self.driver.quit()

def _close_after_error(self):
try:
self.close()
except Exception:
pass


class Chrome(Browser):
"""Chrome or Chromium, optionally headless.
Expand All @@ -169,6 +247,7 @@ class Chrome(Browser):
"""

def __init__(self, headless=True, browser_path=None, driver_path=None, options=tuple()):
_require_selenium()
chrome_options = Options()
if headless:
chrome_options.add_argument("--headless")
Expand Down
19 changes: 19 additions & 0 deletions carta/compatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Compatibility data for CARTA and carta-python releases."""


COMPATIBILITY_DATA = (
{"carta_min": "6.1", "carta_max": None, "wrapper": "2.0"},
)
"""The recommended compatibility between CARTA and carta-python versions.

Each entry contains the oldest and newest CARTA series covered by the range and
the latest compatible carta-python series. ``carta_max=None`` means that the
range remains compatible with all later CARTA series until a known breaking
change requires an explicit upper bound and a new entry.

Ranges must be listed from oldest to newest and must not overlap. Only the final
entry may omit ``carta_max``. The ``wrapper`` value in the final entry must
match the major and minor version in ``VERSION.txt``. Its ``carta_min`` value
defines the minimum CARTA version for which the current carta-python release
provides complete functionality.
"""
6 changes: 6 additions & 0 deletions carta/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class ImageType(IntEnum):
PV_PREVIEW = 2


class VersionMismatchAction(StrEnum):
"""Action to take when a CARTA version check does not pass."""
WARN = "warn"
ERROR = "error"


Scaling = IntEnum('Scaling', ('LINEAR', 'LOG', 'SQRT', 'SQUARE', 'POWER', 'GAMMA'), start=0)
Scaling.__doc__ = """Colormap scaling types."""

Expand Down
Loading
Loading