Self-contained colormap module for VTK color transfer functions in Trame apps
Project description
trame-colormaps
Self-contained colormap module for managing VTK color transfer functions, colorbar rendering, and interactive preset controls in Trame apps.
Installation
pip install trame-colormaps
Or with uv:
uv add trame-colormaps
Development
# Get code
git clone https://github.com/Kitware/trame-colormaps.git
cd trame-colormaps
# Create venv and install all dependencies
uv sync --all-extras --dev
# Activate environment
source .venv/bin/activate
# Install commit analysis
pre-commit install
pre-commit install --hook-type commit-msg
Run tests:
pytest
Run the example app:
python ./examples/wavelet.py
Lint and format:
pre-commit run --all-files
Releasing
- Bump the version in
pyproject.toml - Commit and tag:
git tag v<version> - Push with tags:
git push --tags - GitHub Actions will build and publish to PyPI automatically
Screenshots
Horizontal and vertical colorbars with preset picker
The wavelet example (examples/wavelet.py) shows four colorbars around a 3D view:
two horizontal bars (top and bottom) and two vertical bars (left and right).
Clicking any colorbar opens its control panel with preset picker, scale mode,
and discrete settings. Only one panel can be open at a time.
Real-world integration — horizontal footer colorbar
A production app using trame-colormaps for climate data visualization.
Each data variable gets its own horizontal colorbar at the bottom of the view,
with symlog tick marks that adapt to the data range.
Public API
Import via the trame namespace:
from trame.dataclasses.colormaps import ColormapConfig
from trame.widgets.colormaps import HorizontalScalarBar, VerticalScalarBar, ColorMapEditor
| Symbol | Module | Purpose |
|---|---|---|
ColormapConfig |
trame_colormaps.dataclasses |
Reactive state model — owns CTF, mapper, presets, range, ticks |
HorizontalScalarBar |
trame_colormaps.widgets |
Horizontal colorbar widget with built-in control panel |
VerticalScalarBar |
trame_colormaps.widgets |
Vertical colorbar widget with built-in control panel |
ColorMapEditor |
trame_colormaps.widgets |
Preset picker / control panel popup (used internally by scalar bars) |
buttons |
trame_colormaps.widgets |
Returns button config dicts for the control panel toolbar |
Preset Data Sources
All colormap presets are stored as JSON files under src/trame_colormaps/presets/.
paraview_colormaps.json — Built-in Presets
- Source: ParaView ColorMaps.json
- License: BSD-3-Clause
- 199 colormaps including Brewer, matplotlib, and other community-contributed presets
- Presets that originally used
IndexedColors(discrete/qualitative) have been converted toRGBPointswith evenly spaced control points for uniform handling
crameri_colormaps.json — Crameri Scientific Colour Maps
- Source: Fabio Crameri's Scientific Colour Maps
- License: MIT
- 60 colormaps — sequential, diverging, multi-sequential, cyclic, and categorical
- All are perceptually uniform and color-blind safe
- Downloaded from the cmcrameri GitHub repository
Colormap Usage Guide
| Category | Use When | Data Character |
|---|---|---|
| Sequential | Magnitude — more/less of something | Temperature, pressure, density |
| Diverging | Deviation — Δ from a reference value | Anomaly, residual, balance |
| Cyclic | Periodic — values that wrap around | Phase, angle, time-of-day |
| Categorical | Discrete labels — no inherent order | Material ID, region, class |
Note: Categorical presets are excluded from
default_presets.jsonbecause trame-colormaps generates its own discrete/categorical colormaps from any preset via the discrete banding feature.
Using Sequential Colormaps
Sequential colormaps encode "more vs less" — data that is ordered and one-sided with no special reference value.
Use when:
- Data interpretation is monotonic: low → high
- There is no meaningful midpoint or zero crossing
Examples: temperature (no reference), density, probability, intensity, error magnitude (|Δ|).
Properties:
- Monotonic lightness — darker always means more (or less)
- No implied midpoint
- Easy to interpret quantitatively
Good defaults: Viridis, Plasma, batlow — perceptually uniform ramps.
Using Diverging Colormaps
Diverging colormaps encode "above vs below reference" — data with a meaningful center value where you care about the direction of deviation.
Use when:
- There is a meaningful center (usually 0, but not always)
- You care about direction: below reference ← neutral → above reference
Examples: Δ = A − B, anomalies (value − mean), residuals, signed errors.
Properties:
- Two symmetric color branches around a neutral center (white/light gray)
- Encodes both sign and magnitude
- Must be centered correctly to avoid misinterpretation
- Should be perceptually balanced on both sides
Common derived difference fields:
-
Absolute difference —
Δ = A − BYour primary case (simulation vs observation). Symmetric, interpretable. -
Relative / percent difference —
Δ = (A − B) / BorΔ% = 100 × (A − B) / BUseful when scale matters. Still centered at 0 → diverging applies. -
Deviation from a baseline —
Δ = value − reference_valueExamples: temperature − freezing point, measurement − target threshold, field − spatial mean. -
Standardized anomaly —
Δ = (value − mean) / stdNow Δ is in "number of standard deviations." Very common in climate and statistics. -
Log-ratio (for multiplicative differences) —
Δ = log(A / B)Symmetric around 0. Handles ratios cleanly and plays nicely with wide dynamic ranges.
Diverging workflow:
- Derive Δ field — compute the difference quantity
- Choose scale — linear or symlog (symlog for wide dynamic ranges near zero)
- Apply diverging colormap centered at 0 — toggle diverging mode
- Optional tolerance band (epsilon) — suppress a dead zone around zero
Using Cyclic Colormaps
Cyclic colormaps encode "wrap-around / periodic" — data where start and end represent the same value (0° ≡ 360°).
Use when:
- Data is periodic with no true endpoints
- There must be no visual discontinuity at the boundary
Examples: angle, phase, orientation, wind direction, time of day (circular).
Properties:
- Ends match seamlessly — color at min == color at max
- No discontinuity at boundaries
- Not suitable for ordered or magnitude data
Using Categorical Colormaps
Categorical colormaps encode "different kinds, not ordered" — discrete labels with no inherent ranking.
Use when:
- Data represents discrete labels with no meaningful ordering
- You need maximum visual distinction between classes
Examples: material IDs, cluster labels, classes, region tags.
Properties:
- Distinct, maximally separated colors
- No gradient or implied ordering between colors
- Not suitable for continuous or magnitude data
Note: In trame-colormaps, any preset can be turned into a categorical colormap via the discrete banding feature. This also serves as a way to apply color-based contours to continuous data — discrete bands act as visual iso-surfaces that segment the color range into distinct regions.
default_presets.json — Active Preset List
A JSON array of colormap names that controls which presets are active by default.
Includes all non-Brewer presets, only the highest-count Brewer variants, and the
core 15 Crameri colormaps. Each preset entry includes a "ColorBlindSafe"
boolean field for filtering.
Configuring Active Presets
On import, the active preset list is loaded from default_presets.json. Use
set_active_presets() to override it at runtime:
from trame_colormaps.core.presets import get_active_presets, set_active_presets
# Get the current active list
presets = get_active_presets()
# Set from a Python list
set_active_presets(["Cool to Warm", "batlow", "vik", "Viridis (matplotlib)"])
# Set from a JSON file
set_active_presets("/path/to/my_presets.json")
Usage
Basic: single colorbar
from trame.app import TrameApp
from trame.dataclasses.colormaps import ColormapConfig
from trame.widgets.colormaps import HorizontalScalarBar
class MyApp(TrameApp):
def __init__(self, server=None):
super().__init__(server)
# ... set up VTK pipeline, mapper, etc. ...
self.colormap = ColormapConfig(
self.server,
mapper=self.mapper,
data_array_fn=self.get_data_array,
).set_data_array("Temperature", self.get_data_array, "point")
# Re-render when the colormap updates the mapper
self.colormap.watch(["mapper_change"], self.render)
self._build_ui()
def get_data_array(self):
ds = self.source.GetOutput()
return ds.GetPointData().GetScalars() if ds else None
def render(self, *_):
self.ctx.view.update()
def _build_ui(self):
with SinglePageLayout(self.server) as self.ui:
with self.ui.content:
# ... 3D view ...
with self.colormap.provide_as("bar"):
HorizontalScalarBar("bar", popup_location="top")
Multiple colorbars
Each ColormapConfig instance is independent. When one control panel opens,
all others close automatically:
self.top = ColormapConfig(server, mapper=mapper, data_array_fn=get_data)
self.top.set_data_array("RTData", get_data, "point")
self.left = ColormapConfig(server, mapper=mapper, data_array_fn=get_data)
self.left.set_data_array("RTData", get_data, "point")
# In UI:
with self.top.provide_as("top"):
HorizontalScalarBar("top", popup_location="bottom")
with self.left.provide_as("left"):
VerticalScalarBar("left", popup_location="right")
Updating color range after data changes
When the underlying data changes (e.g. new data loaded, pipeline update),
call update_color_range() to recompute the range, re-apply transforms,
and regenerate ticks:
self.colormap.update_color_range()
Switching data array at runtime
To color by a different variable without creating a new ColormapConfig:
self.colormap.set_data_array(
"Pressure",
data_array_fn=lambda: get_pressure_array(),
scalar_mode="point", # "cell" (default), "point", or "default"
)
ColormapConfig Fields
ColormapConfig in dataclasses.py is a trame.app.dataclass.StateDataModel
subclass. Fields fall into three groups:
| Field | Type | Default | Role |
|---|---|---|---|
| User-settable (bound to UI, triggers reactive updates) | |||
active_presets |
list[str] |
default_presets.json |
Preset names available in the picker |
preset |
str |
"BuGnYl" |
Active color preset name |
invert |
bool |
False |
Invert the transfer function |
color_blind |
bool |
False |
Filter preset list to color-blind safe |
use_log_scale |
str |
"linear" |
Scale mode: "linear", "log", "symlog" |
discrete_log |
bool |
False |
Enable discrete banding |
n_discrete_colors |
int |
4 |
Color bands between ticks (linear) or per decade (log/symlog) |
n_ticks |
int |
5 |
Number of tick marks on the colorbar |
color_value_min |
str |
"0" |
Manual range min (string for text field) |
color_value_max |
str |
"1" |
Manual range max (string for text field) |
override_range |
bool |
False |
Use manual range instead of data range |
| Derived (computed internally, read by UI) | |||
color_range |
tuple[float, float] |
(0, 1) |
Active min/max color range |
color_value_min_valid |
bool |
True |
Whether color_value_min parses as a valid float |
color_value_max_valid |
bool |
True |
Whether color_value_max parses as a valid float |
n_colors |
int |
255 |
Number of LUT samples |
lut_img_h |
str |
"" |
Base64 PNG data URI of the horizontal colorbar image |
lut_img_v |
str |
"" |
Base64 PNG data URI of the vertical colorbar image |
color_ticks |
list |
[] |
Tick marks: [{position, label, color}, ...] |
effective_color_range |
tuple[float, float] |
(0, 1) |
Actual CTF range after transforms |
luts_normal |
list |
[] |
Sorted preset picker entries (normal) |
luts_inverted |
list |
[] |
Sorted preset picker entries (inverted) |
| UI widget state | |||
menu |
bool |
False |
Whether the control panel popup is open |
search |
str | None |
None |
Preset search filter text |
orientation |
str |
"horizontal" |
Colorbar orientation |
mapper_change |
int |
0 |
Server-only counter incremented on each mapper update |
ColormapConfig Methods
| Method | Purpose |
|---|---|
set_data_array(name, fn, scalar_mode) |
Configure the mapper's scalar mode and color array, recompute range, re-apply preset |
update_color_range() |
Recompute range from data (or validate manual range), re-apply transforms, regenerate ticks |
update_color_preset(name, invert, log_scale, ...) |
Apply a preset with scale/discrete settings — also called automatically by reactive watchers |
ColormapConfig.__init__ parameters
| Parameter | Default | Description |
|---|---|---|
server |
required | Trame server instance (first positional arg) |
mapper |
None |
VTK mapper — the CTF is set as its lookup table |
data_array_fn |
None |
Callable returning the VTK data array for range computation |
Configurable Parameters
core/presets.py — function parameters
| Parameter | Default | Function | Description |
|---|---|---|---|
samples |
255 |
generate_colormaps() |
Horizontal pixels in each colorbar image |
preset_list |
default_presets.json |
set_active_presets() |
List of names or path to JSON file |
core/ticks.py — function parameters
| Parameter | Default | Function | Description |
|---|---|---|---|
n |
5 |
get_nice_ticks() |
Desired number of ticks |
scale |
"linear" |
get_nice_ticks() |
Scale mode: "linear", "log", or "symlog" |
linthresh |
1.0 |
get_nice_ticks() |
Linear threshold for log/symlog scales |
Tick mark behavior
Tick marks are computed identically for discrete and continuous modes:
- Linear: evenly spaced (e.g. 20%, 40%, 60%, 80% for
n_ticks=4) - Log: only powers of 10 (decade marks) that fall within the data range
- Symlog: powers of 10 filtered by visual position spacing — ticks far from zero (where the symlog transform expands the scale) are shown at every decade, while ticks near zero (where the transform compresses values) are adaptively thinned to prevent overlap. Zero is always shown when it falls within the data range and away from edges.
The adaptive spacing uses the symlog-transformed position of each candidate tick: only ticks that are at least 100/n percentage points apart in visual space are kept. This naturally produces a nonlinear stride — larger gaps near zero, smaller gaps at extremes.
core/transforms.py — function parameters
| Parameter | Default | Function | Description |
|---|---|---|---|
n_sub |
1 |
All apply_discrete_*() functions |
Number of color bands per gap (linear) or per decade (log/symlog) |
n_samples |
256 |
apply_log(), apply_symlog(), apply_discrete_symlog() |
Resampling resolution for building continuous CTFs |
Dependencies
| Package | Used in | Purpose |
|---|---|---|
vtk (vtkmodules) |
core/presets.py, core/transforms.py, dataclasses.py |
vtkColorTransferFunction for color sampling, vtkPNGWriter/vtkImageData for colorbar image generation, mapper wiring |
| numpy | core/ticks.py, core/transforms.py, dataclasses.py |
Tick computation, LUT transforms, linthresh calculation |
| trame | dataclasses.py, widgets.py |
StateDataModel for reactive config, Vuetify 3 widgets for UI |
| trame-dataclass | dataclasses.py |
StateDataModel base class, Sync/ServerOnly field types, provide_as scoped slot |
Module Structure
src/trame_colormaps/
├── __init__.py # Package version
├── dataclasses.py # ColormapConfig(StateDataModel) — reactive state, CTF, mapper wiring
├── widgets.py # ColorMapEditor, HorizontalScalarBar, VerticalScalarBar
├── module.py # No-op trame module stub for enable_module
├── core/
│ ├── __init__.py # "Pure VTK/numpy, no trame dependency"
│ ├── presets.py # Preset discovery, COLORBAR_CACHE, lut_to_img()
│ ├── ticks.py # Tick computation (linear, log, symlog)
│ └── transforms.py # LUT transforms (linear, log, symlog, discrete variants)
└── presets/
├── __init__.py # Bundled preset JSON shipping
├── paraview_colormaps.json # 199 ParaView built-in presets
├── crameri_colormaps.json # 60 Crameri scientific colour maps
└── default_presets.json # Active preset list with color-blind-safe flags
src/trame/
├── dataclasses/
│ └── colormaps.py # Re-exports ColormapConfig
└── widgets/
└── colormaps.py # Re-exports widgets, initialize(server)
Layer Separation
| Layer | Modules | Dependencies |
|---|---|---|
| Core (pure VTK/numpy) | core/presets.py, core/ticks.py, core/transforms.py |
VTK, numpy |
| State + Logic (Trame reactive model) | dataclasses.py |
Core + trame |
| Widgets (UI) | widgets.py |
trame (Vuetify 3, HTML) |
The core layer has zero Trame dependency and can be used independently for headless colormap operations.
Widget Structure
HorizontalScalarBar / VerticalScalarBar produce the following DOM tree:
html.Div (top-level — flexbox row/column, bg-blue-grey-darken-2)
├── VMenu (activator="parent" — click anywhere on the bar to open)
│ └── ColorMapEditor → VCard (360px popup)
│ ├── VCardItem: toggle buttons (color-blind, invert, scale, range, discrete)
│ ├── VCardItem: discrete color count input (v-show when discrete)
│ ├── VCardItem: min/max text fields (v-show when override_range)
│ ├── VDivider
│ └── VList: searchable preset list with thumbnail images
├── html.Div (min range label)
├── html.Div (colorbar image container, position:relative)
│ ├── html.Img (LUT image — horizontal or vertical)
│ └── html.Div (tick overlay, position:absolute, pointer-events:none)
│ └── html.Div v-for="tick in <name>.color_ticks"
│ ├── html.Div (tick line)
│ └── html.Span (tick label)
└── html.Div (max range label)
Template bindings use <name>.* via config.provide_as("<name>").
When one control panel opens, all others close automatically.
The popup panel position is controlled by popup_location:
"top" → above bar, "bottom" → below, "left"/"right" for vertical bars.
Examples
| File | Description |
|---|---|
examples/wavelet.py |
4-region layout with horizontal + vertical colorbars around a 3D wavelet visualization |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file trame_colormaps-1.0.1.tar.gz.
File metadata
- Download URL: trame_colormaps-1.0.1.tar.gz
- Upload date:
- Size: 252.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee6a1c167e5c89ac917d33b61c986e2cb2038e9240204bd283ea9955f94ee7a6
|
|
| MD5 |
7381690cc3355af74a53b658ca18d0fd
|
|
| BLAKE2b-256 |
c443fbf5f7e30490f520c0eff3f3d12c02516e5d8627cdaae77f114e062c6324
|
Provenance
The following attestation bundles were made for trame_colormaps-1.0.1.tar.gz:
Publisher:
ci.yml on Kitware/trame-colormaps
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trame_colormaps-1.0.1.tar.gz -
Subject digest:
ee6a1c167e5c89ac917d33b61c986e2cb2038e9240204bd283ea9955f94ee7a6 - Sigstore transparency entry: 1569942710
- Sigstore integration time:
-
Permalink:
Kitware/trame-colormaps@dcc2078c5bb4a55e305357f70438dc2f2a3020b9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Kitware
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@dcc2078c5bb4a55e305357f70438dc2f2a3020b9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file trame_colormaps-1.0.1-py3-none-any.whl.
File metadata
- Download URL: trame_colormaps-1.0.1-py3-none-any.whl
- Upload date:
- Size: 262.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b32a3127e7ffff3c606d87ee4fef0924dfa7c5a79d905916c6087db59812e05d
|
|
| MD5 |
73de083dcbc8feb8d2394ca2183211ea
|
|
| BLAKE2b-256 |
f36a71cb79132764b002d2d7fb211acb1db979cb7558b8124af44ce6914dd074
|
Provenance
The following attestation bundles were made for trame_colormaps-1.0.1-py3-none-any.whl:
Publisher:
ci.yml on Kitware/trame-colormaps
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trame_colormaps-1.0.1-py3-none-any.whl -
Subject digest:
b32a3127e7ffff3c606d87ee4fef0924dfa7c5a79d905916c6087db59812e05d - Sigstore transparency entry: 1569942792
- Sigstore integration time:
-
Permalink:
Kitware/trame-colormaps@dcc2078c5bb4a55e305357f70438dc2f2a3020b9 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Kitware
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@dcc2078c5bb4a55e305357f70438dc2f2a3020b9 -
Trigger Event:
push
-
Statement type: