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.1.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.1-py3-none-any.whl (4.3 MB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: medusa_style-0.3.1.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.1.tar.gz
Algorithm Hash digest
SHA256 91eca0fa6f42fb07229f30e1dc42d72fddc666014d586fc5f6feb0e893f3d277
MD5 f0fa57ff46d49694062f979b59ff9b96
BLAKE2b-256 fb93ff198c067ebb8dca18dc3e181eeeb6d019ba8c5054688b3847ae901a2c59

See more details on using hashes here.

Provenance

The following attestation bundles were made for medusa_style-0.3.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: medusa_style-0.3.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6940625fa3ac84aff4a60739f063c248967ddb631d358572e23466d54774241d
MD5 a75726932d73adc31a4102b233614e40
BLAKE2b-256 2bba3297461ef6b467be847306cb6ea55a35efe5b90037b542c5bf32a2ada3a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for medusa_style-0.3.1-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