Skip to main content

medusa-style

Single source of truth (SSOT) for MEDUSA styling. One look themes BOTH PySide6 desktop apps (via generated QSS) and Matplotlib scientific plots (built on Matplotlib's bundled seaborn baseline) — so a microvolt is the same color in the dark acquisition app and on a white-paper export, and an embedded plot is seamless with the panel that hosts it.

The look has two independent axes:

  • a theme — the color scheme (dark, light, …), a Palette.
  • a style — the visual language (modern, presentation, …): fonts, density and plot line widths, a Style.

Any style renders in any theme. The code is organized by rendering environment (qt/, mpl/, …), so adding a backend never touches the others.

Install

pip install git+https://github.com/medusabci/medusa-style.git

That's all — PySide6 and Matplotlib are both core dependencies (every MEDUSA component needs both). The only extra is [dev] for building/testing the package itself. The seaborn library is not required: the seaborn base styles ship inside Matplotlib.

The whole API

import medusa_style as ms

ms.apply(app)               # paint a Qt application (widgets + seamless plots)
ms.apply(widget)            # scope the theme to one Qt widget
ms.apply()                  # theme every Matplotlib figure you draw
ms.apply(fig)               # restyle one figure in place
ms.apply(ax)                # restyle one axes in place
ms.use_theme("light")       # switch COLORS — restyles everything already styled
ms.use_style("presentation")# switch VISUAL LANGUAGE — restyles everything
ms.current_theme()          # the active Palette
ms.current_style()          # the active Style
ms.themes()                 # ['dark', 'light']          — for a theme picker
ms.styles()                 # ['modern', 'presentation'] — for a style picker
ms.customize(colors=..., style=...)   # one-line house theme / style
ms.on_change(cb)            # react to switches — cb(palette, style)
ms.categorical_color(7)     # SSOT categorical color #7

apply() dispatches on what you give it: nothing → global Matplotlib, a QApplication/QWidget → Qt, a Figure/Axes → Matplotlib. You never choose a backend. Whatever you pass is remembered, so a later use_theme()/use_style() repaints it with no loop on your side.

Desktop app (PySide6)

import sys
import medusa_style as ms
from medusa_style.qt import application, PlotPanel
from PySide6.QtWidgets import QMainWindow

app = application(sys.argv)        # Hi-DPI handled before construction; themed
ms.apply()                         # theme Matplotlib figures too

win = QMainWindow()
panel = PlotPanel(toolbar=True)    # seamless embed — no objectName, no restyle call
panel.figure.add_subplot(111).plot(range(10), color=ms.categorical_color(0))
win.setCentralWidget(panel)
win.show()

# View menu -> app AND embedded figure restyle, one line each:
view = win.menuBar().addMenu("View")
view.addAction("Light", lambda: ms.use_theme("light"))
view.addAction("Presentation", lambda: ms.use_style("presentation"))
sys.exit(app.exec())

PlotPanel is the borderless Matplotlib↔Qt seam as a drop-in widget: its background equals the plot's, and it tracks itself for live theme/style switches. For a frame you already own, use medusa_style.qt.embed_figure(frame, fig).

Notebook / headless (Qt never imported)

import medusa_style as ms
ms.apply()                  # theme Matplotlib globally
fig = make_figure()
ms.apply(fig)               # ...or restyle one figure
ms.use_theme("dark")        # restyles tracked figures in place

Importing medusa_style pulls in neither PySide6 nor Matplotlib until you actually style something.

Themes and styles

Two built-in themes (color schemes), both tuned for long medical/scientific sessions:

  • dark (default) — deep, cool, near-black canvas for dim-lab real-time use.
  • light — a calm cool-gray elevation ramp for reports/paper export (plot_bg is pure white).

Two built-in styles (visual languages):

  • modern (default) — the clean flat MEDUSA look (assets/qss/base.qss).
  • presentation — larger type, thicker plot lines and roomier controls for projectors and slides (token-only; reuses base.qss).

The data-visualization encodings — a 16-color colorblind-aware categorical cycle and the medusa_sequential (PSD) / medusa_diverging (topomap) colormaps — are theme- and style-independent, so results stay comparable across every look.

Customize

Zero config by default; override in one line. colors derive a new theme, style derives a new style:

ms.customize(
    colors={"accent_primary": "#FF6600"},                # -> a registered theme
    style={"ui_size_body": 15, "plot_line_width": 1.4},  # -> a registered style
)                            # activates + live-restyles; returns the created value(s)

Architecture & extensibility

medusa_style/
  api.py        the public verbs
  dispatch.py   target detection + environment routing (stdlib-only)
  appearance.py the active (theme, style) + live switching + observer
  palette.py    the color SSOT (Palette, DARK, LIGHT) + encodings
  style.py      the visual languages (Style, MODERN, PRESENTATION)
  resources.py  zip-safe asset loading
  qt/           the Qt environment (QSS render, PlotPanel, icons, fonts)
  mpl/          the Matplotlib environment (rcParams, colormaps, figure styling)
  assets/       qss/ (structural templates), icons/, fonts/, brand/
  • Add a theme — one Palette(...) literal in palette.py (+ PALETTES entry) or ms.register_theme(...). Works in every environment immediately.
  • Add a style — one Style(...) literal in style.py (+ STYLES entry) or ms.register_style(...). A token-only style is pure data; only a structurally different widget language needs its own assets/qss/<name>.qss.
  • Add an environment (Plotly, VisPy, Bokeh, …) — a new package with a matches/_apply/reapply trio and one ms.register_environment(...). No existing file changes.

Power-user namespaces

The tiny top-level surface is enough for almost everything; reach into these only when you need the knobs:

Namespace What's there
medusa_style.qt application(), PlotPanel, embed_figure(), style_app(), stylesheet(), prepare_high_dpi(), icon(), pixmap(), load_fonts()
medusa_style.mpl style_figure(), style_axes(), rcparams(), build_rcparams(), mplstyle_text(), register_colormaps(), sequential_cmap(), diverging_cmap()
medusa_style.palette the raw SSOT colors: Palette, DARK, LIGHT, CATEGORICAL_CYCLE, WCAG utils
medusa_style.style the visual languages: Style, MODERN, PRESENTATION

medusa_style.qt imports PySide6 (never Matplotlib); medusa_style.mpl imports Matplotlib (never Qt). import medusa_style loads neither until you style something, so a headless figure-export job never pulls in Qt.

Icons, fonts & brand assets

from medusa_style import qt

button.setIcon(qt.icon("save_as"))        # theme-agnostic action icon
button.setIcon(qt.icon("delete_forever", color="error"))  # ...or a semantic color
splash = qt.pixmap("medusa_splash")       # brand images (splash/login/about)
win.setWindowIcon(qt.app_icon())          # the MEDUSA app icon
path = qt.asset_path("fonts/Roboto-Regular.ttf")   # any bundled file, real path

qt.icon(name) icons are theme-agnostic. The color is not baked in: each icon recolors to the active theme on every repaint and changes with Qt's state automatically — a disabled widget dims its icon, and a theme switch repaints it live, with no re-fetch. Pass color= (a palette role like "error"/"accent_primary", or a "#RRGGBB" literal) to force a specific color; it still dims when disabled.

qt.application() does the common setup for you: it registers the bundled fonts (Roboto family, Abel, Dense, Womby) and sets the MEDUSA window icon app-wide (both opt-out via application(set_icon=False, load_bundled_fonts=False)).

Packaging

All runtime data lives under medusa_style/assets/ — qss/ (the structural QSS templates), icons/, fonts/ and brand/. They are declared in pyproject.toml so the wheel always includes them, while the editable .psd design sources stay in the repo-level design_source/ directory (outside the package, never shipped):

[tool.hatch.build.targets.wheel]
packages  = ["medusa_style"]
artifacts = [
    "medusa_style/assets/**/*.qss",
    "medusa_style/assets/**/*.svg",
    "medusa_style/assets/**/*.png",
    "medusa_style/assets/**/*.ico",
    "medusa_style/assets/**/*.ttf",
    "medusa_style/assets/**/*.otf",
    "medusa_style/py.typed",
]

At runtime the assets are loaded zip-safely via medusa_style.resources (which wraps importlib.resources), so it works even from inside a zipped wheel. Matplotlib .mplstyle files are generated on demand (medusa_style.mpl.write_mplstyle(...)) rather than shipped, so they can never drift from the SSOT.

Metadata

Release files for medusa-style 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for medusa-style 0.4.0
File Size Uploaded Provenance
medusa_style-0.4.0.tar.gz 4.3 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for medusa-style 0.4.0
File Interpreter ABI Platform
medusa_style-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 8.5 MB

Release files / medusa_style-0.4.0.tar.gz

Download URL medusa_style-0.4.0.tar.gz
Size 4.3 MB
Tags Source
SHA-256 checksum
How to use checksums
9a71f7d6c86329ea6ae1af8c2953f2b987168d64643d1335bbeb1b7953aa1bc8
BLAKE2b-256 checksum
How to use checksums
ab99cc2c02c82f3114fb25ff8c111d15850a66b2cb9a5f8a7f9952e66fcd3728
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / medusa_style-0.4.0-py3-none-any.whl

Download URL medusa_style-0.4.0-py3-none-any.whl
Size 4.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
a618be615278bda47b1c8c17cb6ad72b5ba1b3dfe6b8d5cc649e5979098be6d5
BLAKE2b-256 checksum
How to use checksums
1a4b6ed8014ce39875fdd8f3d661a58cdf6d130bd7df41425877447daad0aa16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Security for medusa-style 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for medusa_style-0.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page