Skip to content

The ROOT look

root_template reproduces a gStyle-like canvas: Helvetica text with sizes given as fractions of the pad height as ROOT does, a full frame with ticks on the bottom and left (or mirrored), tick lengths as a fraction of the plot area, and margins as pad fractions.

import plotly.graph_objects as go
fig = go.Figure(layout=dict(template=php.root_template(800, 600, label_size=0.035, title_size=0.04,
                                                       margins=(0.1, 0.1, 0.1, 0.1), tick_len=0.03)))

The gallery rebuilds two ROOT tutorials, df102_NanoAODDimuonAnalysis and df106_HiggsToFourLeptons, on this template from CERN Open Data and compares them to the reference PNGs on root.cern in the test suite. The lessons from getting those to match are in the README: ROOT's NDC is not Plotly's paper coordinate system, the E draw option means bin-width horizontal bars without caps, and Plotly's power-of-ten tick labels are about a quarter larger than ROOT's, so the rebuild writes them explicitly.

ROOT to Plotly and back

from plotlyhep.convert import from_root, to_root

fig = from_root(canvas)            # a TCanvas or TPad with everything drawn on it
fig = from_root(h, "E1")           # a single TH1 / TH2 / TGraph* / TMultiGraph / THStack / TF1 with a draw option
fig = from_root(uproot_file["h"])  # an object read with uproot, no ROOT installation needed
canvas = to_root(fig)              # back: TH1D / TGraph(Asymm)Errors / TH2D / TLatex / TLine / TLegend on a TCanvas

from_root reads what ROOT drew: the pad margins become the axis domain, so Plotly's paper fractions are ROOT's NDC and every NDC-positioned object (legend, TLatex, the title box, a stats box) lands where ROOT put it. Text sizes are fractions of the pad height as in ROOT, precision-3 fonts stay in pixels, bold and italic font codes carry over, TLatex markup becomes HTML (#it{}, _{}, ^{}, #sqrt{}, Greek letters, accents), marker and line styles map to their Plotly counterparts, colours come from the live colour table (or a snapshot of it for uproot objects) and a TH2 uses the current palette. Draw options are honoured: HIST, E/E1 with or without horizontal bars (X0), P, L, C, B, COL/COLZ, APL, 3 bands, NOSTACK, SAME. Fit functions in a histogram's function list are drawn, Divided canvases become one subplot per pad, and log pads become log axes.

A single object is drawn on a temporary batch canvas first, so ROOT's own axis ranges, title and stats box apply. to_root recognises the traces plotlyhep produces (steps, error bars, heatmaps, the end-aligned axis labels) and returns the canvas with the objects kept alive on it.

The test suite renders one canvas through ROOT's painter and through from_root + kaleido and ratchets the pixel difference, the same way the gallery rebuilds are checked.