Skip to main content

Single source of truth for MEDUSA styling: one Style x Theme look for PySide6 QSS + Matplotlib, organized by rendering environment.

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

medusa_style-0.3.0.tar.gz (4.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

medusa_style-0.3.0-py3-none-any.whl (4.3 MB view details)

Uploaded Python 3

File details

Details for the file medusa_style-0.3.0.tar.gz.

File metadata

  • Download URL: medusa_style-0.3.0.tar.gz
  • Upload date:
  • Size: 4.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for medusa_style-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a7ee646bd954679f84aa05c20db4ea2595910481fd5cd9ee8d13d20466c30a06
MD5 b1e9134ec4323f0dd8ec868ac0311372
BLAKE2b-256 cc128391a7004e52ee65b8abb2a2d339f301b2b451531111b4a3bc05dabf5382

See more details on using hashes here.

Provenance

The following attestation bundles were made for medusa_style-0.3.0.tar.gz:

Publisher: publish.yml on medusabci/medusa-style

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file medusa_style-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: medusa_style-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 4.3 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for medusa_style-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e67eee6e8ee54c9b974ba8d1d0fa7d0225d621c6a32fe66a7db89eb65a0a5e84
MD5 4cad487951e23cfb24ab64e5a3652f59
BLAKE2b-256 330be340486584beabd7e9385036faedf2b1b8e54342977f1559ea9225b2c45e

See more details on using hashes here.

Provenance

The following attestation bundles were made for medusa_style-0.3.0-py3-none-any.whl:

Publisher: publish.yml on medusabci/medusa-style

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page