-
Notifications
You must be signed in to change notification settings - Fork 2
MatplotlibThemes
Drop-in matplotlib look in pure .NET — no Python runtime required.
Two themes that mimic Python matplotlib's iconic visual style: the legendary pre-2.0 classic look and the modern v2.0+ default. Use them when you want your .NET charts to feel at home next to plots from a Jupyter notebook, a research paper, or pyplot.plot(...).
Matplotlib is the dominant scientific plotting library in the Python world, and its aesthetic is instantly recognizable in academic papers, ML blog posts, textbooks, and notebooks. People who move between Python and .NET know what a matplotlib chart should look like — and they expect that style to be available without dragging in a Python runtime.
These themes give you exactly that: the same colors, the same fonts, the same defaults — rendered entirely by MatPlotLibNet.
The look that appeared in scientific papers from matplotlib's release until 2017. White background, the iconic bgrcmyk 7-color cycle, DejaVu Sans 12pt, and grid hidden by default (matplotlib has always shipped with plt.grid(False); users opt in).
using MatPlotLibNet;
using MatPlotLibNet.Styling;
double[] x = [0, 1, 2, 3, 4, 5];
double[] y1 = [0, 1, 4, 9, 16, 25];
double[] y2 = [0, 2, 4, 6, 8, 10];
Plt.Create()
.WithTheme(Theme.MatplotlibClassic)
.WithTitle("Classic matplotlib look")
.Plot(x, y1, s => s.Label = "Quadratic")
.Plot(x, y2, s => s.Label = "Linear")
.WithLegend()
.Save("classic.svg");| # | Hex | Name |
|---|---|---|
| 0 | #0000FF |
blue |
| 1 | #008000 |
green |
| 2 | #FF0000 |
red |
| 3 | #00BFBF |
cyan |
| 4 | #BF00BF |
magenta |
| 5 | #BFBF00 |
yellow |
| 6 | #000000 |
black |
| Property | Value |
|---|---|
| Background |
#FFFFFF (white) |
| Axes background |
#FFFFFF (white) |
| Foreground text |
#000000 (pure black) |
| Font family | DejaVu Sans, Bitstream Vera Sans, sans-serif |
| Font size | 12.0pt |
| Grid | hidden by default (Visible = false) |
| Cycle length | 7 colors |
The look every Jupyter notebook ships with today. White background, soft-black #262626 text (matplotlib v2 deliberately softened pure black), the modern tab10 10-color cycle, DejaVu Sans 10pt, and grid hidden by default.
Plt.Create()
.WithTheme(Theme.MatplotlibV2)
.WithTitle("Modern matplotlib look")
.Plot(x, y1, s => s.Label = "Quadratic")
.Plot(x, y2, s => s.Label = "Linear")
.WithLegend()
.Save("v2.svg");| # | Hex | Name |
|---|---|---|
| 0 | #1f77b4 |
blue |
| 1 | #ff7f0e |
orange |
| 2 | #2ca02c |
green |
| 3 | #d62728 |
red |
| 4 | #9467bd |
purple |
| 5 | #8c564b |
brown |
| 6 | #e377c2 |
pink |
| 7 | #7f7f7f |
gray |
| 8 | #bcbd22 |
olive |
| 9 | #17becf |
cyan |
| Property | Value |
|---|---|
| Background |
#FFFFFF (white) |
| Axes background |
#FFFFFF (white) |
| Foreground text |
#262626 (soft black) |
| Font family | DejaVu Sans, sans-serif |
| Font size | 10.0pt |
| Grid | hidden by default (Visible = false) |
| Cycle length | 10 colors |
Both matplotlib themes ship with the grid off to stay faithful to matplotlib's plt.grid(False) default. To turn it on for a single plot, use the standard grid API:
Plt.Create()
.WithTheme(Theme.MatplotlibV2)
.AddSubPlot(1, 1, 1, ax => ax
.Plot(x, y)
.ShowGrid(true)) // simple toggle
.Save("v2_with_grid.svg");Or override the theme grid via ThemeBuilder:
var v2WithGrid = Theme.CreateFrom(Theme.MatplotlibV2)
.WithGrid(g => g with { Visible = true, Color = Color.FromHex("#B0B0B0") })
.Build();| Aspect | MatplotlibClassic |
MatplotlibV2 |
Theme.Default |
|---|---|---|---|
| Era mimicked | matplotlib < 2.0 | matplotlib 2.0+ | none specifically |
| Cycle |
bgrcmyk (7) |
tab10 (10) |
tab10 (10) |
| Foreground text | pure black | soft black #262626
|
pure black |
| Font size | 12pt | 10pt | 13pt (default) |
| Grid default | off | off | on |
| Use case | retro / classic | modern Jupyter look | MatPlotLibNet's own default |
Theme.Default already happens to use the tab10 colors, but MatplotlibV2 is the deliberate choice when you want to be the matplotlib look — different font stack, soft-black text, and the matplotlib-style hidden-by-default grid.
Phase P made the six community themes first-class. Before Phase P they rendered near-identically to
Theme.Default; each now carries its own palette, font, size, and grid defaults, so dropping one in visibly changes the chart without any other API calls.
| Theme | Palette | Font | Size | Grid |
|---|---|---|---|---|
Theme.Grayscale |
grayscale cycle (no hues) | default | default | default |
Theme.Paper |
default | serif | 11pt | off |
Theme.Presentation |
default | bold | 16pt | default |
Theme.Poster |
default | bold | 20pt | on, 1.5 linewidth |
Theme.GitHub |
GitHub brand palette | default | default | #E1E4E8 |
Theme.Minimal |
default | default | 11pt | off |
Use them the same way as the matplotlib themes — Plt.Create().WithTheme(Theme.Paper)…. The Phase P rework is covered by byte-distinct-output contract tests so a future regression (a theme collapsing back to Default) turns CI red.
Since v1.1.3 every chart that has a matplotlib equivalent is verified against a pinned matplotlib (3.10.8) reference PNG under both themes. The fidelity test suite runs 146 tests = 73 fixtures × 2 themes:
# regenerate reference PNGs (developers only — not run in CI)
pip install -r tools/mpl_reference/requirements.txt
python tools/mpl_reference/generate.py --all --style both
# run the dual-theme fidelity tests
dotnet run --project Tst/MatPlotLibNet.Fidelity/MatPlotLibNet.Fidelity.Tests.csprojEach test renders one MatPlotLibNet figure, exports to PNG via the SkiaSharp backend, and compares against the matplotlib reference using three independent metrics: RMS pixel error, block SSIM (structural similarity), and MaxColorDeltaE (CIE ΔE*76 across the 5 dominant colours). Failures emit a 3-panel side-by-side diff PNG (reference | actual | abs-difference heatmap) under bin/Debug/net10.0/fidelity-failures/{theme}_{name}.diff.png.
Coverage: 73 fixtures = 12 core (line, scatter, bar, hist, pie, box, violin, heatmap, contour, polar, candlestick, errorbar) + 45 Phase 5 (XY, grid, field, polar, categorical, distribution, 3D, financial, special) + 15 Phase 6 (pandas_ta technical indicators) + 1 composition (multi-subplot suptitle + mathtext labels + mathtext legend).
The v2 fixtures are generated via plt.style.context('default') — that is the modern matplotlib default style (tab10 cycle, DejaVu Sans 10pt, soft-black #262626 foreground), the exact look Theme.MatplotlibV2 reproduces.
Both themes are built by an internal MatplotlibThemeFactory that centralizes the shared font stack and grid defaults via a single Build(...) helper, so the two themes only differ in the values they actually disagree on (color cycle, font size, foreground text). The font stack is captured as a record struct (MatplotlibFontStack) — named fields with value-equality semantics, not a positional tuple.
- Styling — themes, colormaps, PropCycler, custom themes
-
Accessibility —
Theme.ColorBlindSafe,Theme.HighContrast - Source