#!/usr/bin/env python2.7
from __future__ import print_function
import ROOT
import re
from MethodProxy import *
from Helpers import DissectProperties
def ExtendMethods(cls):
# Add dedicated methods (+ their associated properties) for each axis of the frame.
# The axis name is added to the beginning of the property name, e.g. 'xlabelfont'
# will configure the labelfont of the pad frame's x-axis only, while 'labelfont'
# will do so for all axes (see setter method below).
# (See: https://www.reddit.com/r/learnpython/comments/50yk7s/)
cls._frameproperties = []
for prop in [
"BinLabels",
"LabelFont",
"LabelSize",
"LabelColor",
"LabelOffset",
"LabelOption",
"TitleFont",
"TitleSize",
"TitleColor",
"TitleOffset",
"Ndivisions",
]:
cls._frameproperties.append(prop.lower())
for coord in "xyz":
cls._frameproperties.append("{}{}".format(coord, prop.lower()))
setattr(
cls,
"Set{}{}".format(coord.upper(), prop),
lambda cls, v, bound_coord=coord, bound_prop=prop: getattr(
cls, "Set{}".format(bound_prop)
)(v, axes=bound_coord),
)
coordprop = "{}{}".format(coord.upper(), prop)
if coordprop.lower() in cls._properties:
continue
cls._properties.append(coordprop.lower())
cls._methods.append("Set{}".format(coordprop))
return cls
[docs]@ExtendMethods
@PreloadProperties
class Pad(MethodProxy, ROOT.TPad):
# Properties not meant to be changed via keyword arguments:
_ignore_properties = [
"attfillps",
"attlineps",
"attmarkerps",
"atttextps",
"bboxcenter",
"bboxcenterx",
"bboxcentery",
"bboxx1",
"bboxx2",
"bboxy1",
"bboxy2",
"canvas",
"canvassize",
"frame",
"maxpickdistance",
"name",
"ndivisions",
"number",
"pad",
"phi",
"theta",
"vertical",
"view",
"viewer3d",
"xfile",
"xstat",
"yfile",
"ystat",
]
[docs] def __init__(self, name="undefined", *args, **kwargs):
MethodProxy.__init__(self)
self._frame = None
self._drawframe = False
self._xmin = self._ymin = 1e-2
self._xmax = self._ymax = 1.0
self._xunits, self._yunits = None, None
self._ypadding = 0.25 # frame is 25% higher than max. bin content
self._drawlegend = False
self._legendxshift = 0.0
self._legendyshift = 0.0
self._labeloption = {}
if len(args) > 0:
self._xposlow, self._yposlow = args[0:2]
self._xposup, self._yposup = args[2:4]
ROOT.TPad.__init__(self, name, "", *args)
else:
self._xposlow, self._yposlow, self._xposup, self._yposup = (
0.0,
0.0,
1.0,
1.0,
)
ROOT.TPad.__init__(self)
self.SetName(name)
self.SetTitle(";;")
properties = DissectProperties(kwargs, [{"Frame": self._frameproperties}, Pad])
self.Draw()
self.DeclareProperty("padposition", properties["Pad"].pop("padposition"))
self.DeclareProperties(**properties["Pad"])
self.Draw()
self.cd()
if self._drawframe:
self.DrawFrame(**properties["Frame"])
[docs] def SetPadPosition(self, xlow, ylow, xup, yup):
self._xposlow, self._yposlow = xlow, ylow
self._xposup, self._yposup = xup, yup
self.SetPad(xlow, ylow, xup, yup)
[docs] def GetPadPosition(self):
return (self._xposlow, self._yposlow, self._xposup, self._yposup)
[docs] def GetPadWidth(self):
return self._xposup - self._xposlow
[docs] def GetPadHeight(self):
return self._yposup - self._yposlow
[docs] def GetXMin(self):
return self._xmin
[docs] def GetXMax(self):
return self._xmax
[docs] def GetYMin(self):
return self._ymin
[docs] def GetYMax(self):
return self._ymax
[docs] def SetXMin(self, xmin):
if self.GetLogx() and xmin <= 0:
logger.error("Cannot set 'xmin' to {} on log-axis!".format(xmin))
raise ValueError
self._xmin = xmin
[docs] def SetXMax(self, xmax):
if self.GetLogx() and xmax <= 0:
logger.error("Cannot set 'xmax' to {} on log-axis!".format(xmax))
raise ValueError
self._xmax = xmax
[docs] def SetYMin(self, ymin):
if self.GetLogy() and ymin <= 0:
logger.error("Cannot set 'ymin' to {} on log-axis!".format(ymin))
raise ValueError
self._ymin = ymin
[docs] def SetYMax(self, ymax):
if self.GetLogy() and ymax <= 0:
logger.error("Cannot set 'ymax' to {} on log-axis!".format(ymax))
raise ValueError
self._ymax = ymax
[docs] def SetLogx(self, boolean):
if boolean and self.GetXMin() * self.GetXMax() <= 0:
logger.error(
"Cannot set 'logx' to '{}' on axis with negative"
" values!".format(boolean)
)
raise ValueError
super(Pad, self).SetLogx(boolean)
[docs] def SetLogy(self, boolean):
if boolean and self.GetYMin() * self.GetYMax() <= 0:
logger.error(
"Cannot set 'logy' to '{}' on axis with negative"
" values!".format(boolean)
)
raise ValueError
super(Pad, self).SetLogy(boolean)
[docs] def SetXTitle(self, xtitle):
ytitle = self.GetTitle().split(";")[2]
prev_xtitle = self.GetTitle().split(";")[1]
xunits = re.search("\[(.*?)\]$", prev_xtitle)
if xunits:
xtitle += " {}".format(xunits.group())
self.SetTitle(";{};{}".format(xtitle, ytitle))
[docs] def GetXTitle(self):
return self.GetTitle().split(";")[1]
[docs] def SetYTitle(self, ytitle):
xtitle = self.GetTitle().split(";")[1]
prev_ytitle = self.GetTitle().split(";")[2]
yunits = re.search("\[(.*?)\]$", prev_ytitle)
if yunits:
ytitle += " {}".format(yunits.group())
self.SetTitle(";{};{}".format(xtitle, ytitle))
[docs] def GetYTitle(self):
return self.GetTitle().split(";")[2]
[docs] def SetXUnits(self, xunits):
assert isinstance(xunits, str)
self._xunits = xunits
xtitle = self.GetXTitle()
if re.search("\[(.*?)\]$", xtitle):
re.sub("\[(.*?)\]$", "[{}]".format(self._xunits), xtitle)
else:
xtitle += " [{}]".format(self._xunits)
self.SetXTitle(xtitle)
[docs] def GetXUnits(self):
return self._xunits
[docs] def SetYUnits(self, yunits):
assert isinstance(yunits, str)
self._yunits = yunits
ytitle = self.GetYTitle()
if re.search("\[(.*?)\]$", ytitle):
re.sub("\[(.*?)\]$", "[{}]".format(self._yunits), ytitle)
else:
ytitle += " [{}]".format(self._yunits)
self.SetYTitle(ytitle)
[docs] def GetYUnits(self):
return self._yunits
[docs] def SetFrame(self, xmin, ymin, xmax, ymax):
self._xmin = xmin
self._ymin = ymin
self._xmax = xmax
self._ymax = ymax
[docs] def GetFrame(self):
return (self._xmin, self._ymin, self._xmax, self._ymax)
[docs] def SetDrawFrame(self, boolean):
self._drawframe = boolean
[docs] def GetDrawFrame(self):
return self._drawframe
[docs] def DrawFrame(self, *args, **kwargs):
if len(args) == 4:
for i, prop in enumerate(["xmin", "ymin", "xmax", "ymax"]):
self.DeclareProperty(prop, args[i])
elif len(args) != 0:
logger.error(
"DrawFrame() takes exactly 0 or 4 arguments"
" ({} given)".format(len(args))
)
raise TypeError
assert self._xmin < self._xmax
assert self._ymin < self._ymax
self._frame = super(Pad, self).DrawFrame(
self._xmin, self._ymin, self._xmax, self._ymax, self.GetTitle()
)
self.DeclareProperties(**kwargs)
[docs] def SetLabelColor(self, color, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetLabelColor(color)
[docs] def SetLabelFont(self, font, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetLabelFont(font)
[docs] def SetLabelSize(self, size, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetLabelSize(size)
[docs] def SetLabelOffset(self, offset, axes="xyz"):
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
scale = 700.0 / canvaswidth
axis.SetLabelOffset(offset * scale)
[docs] def SetTitleColor(self, color, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetTitleColor(color)
[docs] def SetTitleFont(self, font, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetTitleFont(font)
[docs] def SetTitleSize(self, size, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetTitleSize(size)
[docs] def SetTitleOffset(self, offset, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
canvasheight = self.GetCanvas().GetWh() * self.GetCanvas().GetAbsHNDC()
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
padheight = self.GetWh() * self.GetAbsHNDC()
padwidth = self.GetWw() * self.GetAbsWNDC()
scale = {
"x": (canvasheight / canvaswidth) * (padwidth / padheight),
"y": (canvasheight / canvaswidth),
}.get(coord.lower(), 1.0)
axis.SetTitleOffset(offset * scale)
[docs] def SetNdivisions(self, ndiv, optim=True, axes="xyz"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
axis.SetNdivisions(ndiv, optim)
[docs] def SetTopMargin(self, margin):
canvasheight = self.GetCanvas().GetWh() * self.GetCanvas().GetAbsHNDC()
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
padheight = self.GetWh() * self.GetAbsHNDC()
scale = canvaswidth / canvasheight
scale *= canvasheight / padheight
scale *= 700.0 / canvaswidth
super(Pad, self).SetTopMargin(margin * scale)
[docs] def SetBottomMargin(self, margin):
canvasheight = self.GetCanvas().GetWh() * self.GetCanvas().GetAbsHNDC()
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
padheight = self.GetWh() * self.GetAbsHNDC()
scale = canvaswidth / canvasheight
scale *= canvasheight / padheight
scale *= 700.0 / canvaswidth
super(Pad, self).SetBottomMargin(margin * scale)
[docs] def SetLeftMargin(self, margin):
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
scale = 700.0 / canvaswidth
super(Pad, self).SetLeftMargin(margin * scale)
[docs] def SetRightMargin(self, margin):
canvaswidth = self.GetCanvas().GetWw() * self.GetCanvas().GetAbsWNDC()
scale = 700.0 / canvaswidth
super(Pad, self).SetRightMargin(margin * scale)
[docs] def SetDrawLegend(self, boolean):
self._drawlegend = boolean
[docs] def SetLegendXShift(self, value):
self._legendxshift = value
[docs] def GetLegendXShift(self):
return self._legendxshift
[docs] def SetLegendYShift(self, value):
self._legendyshift = value
[docs] def GetLegendYShift(self):
return self._legendyshift
[docs] def GetDrawLegend(self):
return self._drawlegend
[docs] def SetYPadding(self, value):
self._ypadding = value
[docs] def GetYPadding(self):
return self._ypadding
[docs] def SetBinLabels(self, listoflabels, axes="x"):
for coord in axes:
axis = {
"x": self._frame.GetXaxis(),
"y": self._frame.GetYaxis(),
"z": self._frame.GetZaxis(),
}.get(coord.lower(), None)
for i, label in enumerate(listoflabels):
axis.SetBinLabel(
(i * axis.GetNbins() / len(listoflabels)) + 1, str(label)
)
self._frame.LabelsOption(self._labeloption.get(coord, "h"))
[docs] def SetLabelOption(self, option, axes="xyz"):
for coord in axes:
self._labeloption[coord] = option
try:
self._frame.LabelsOption(option)
except:
pass