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, …), aPalette. - a style — the visual language (
modern,presentation, …): fonts, density and plot line widths, aStyle.
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_bgis 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 inpalette.py(+PALETTESentry) orms.register_theme(...). Works in every environment immediately. - Add a style — one
Style(...)literal instyle.py(+STYLESentry) orms.register_style(...). A token-only style is pure data; only a structurally different widget language needs its ownassets/qss/<name>.qss. - Add an environment (Plotly, VisPy, Bokeh, …) — a new package with a
matches/_apply/reapplytrio and onems.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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| medusa_style-0.4.0.tar.gz | 4.3 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.