Skip to content

Commit 3dff0d1

Browse files
authored
Allow to customize legend and graph style in TrendingTask (#2591)
* feat: add extra amplitude graphs * feat: better customization option for trending * chore: add example workflow for better trending customization * chore: add docs about extra trending options * chore: add comments about expected format of the style values * chore: various code-style corrections * feat: legend enabled by default * chore: rollback change to logs and comments wording * feat: allow disabling the legend explicitly * feat: lambda functions reworked as static functions * doc: document overriding option params * chore: further code-style changes * chore: remove legend.enabled + clang-format * doc: update info about custom params * fix: correct wording in docs * chore: fruther code-style changes * fix: set default coords to -1 * chore: comment changes * doc: correct the docs * fix: remove digit qc task changes
1 parent 860ed80 commit 3dff0d1

File tree

6 files changed

+278
-42
lines changed

6 files changed

+278
-42
lines changed

Framework/include/QualityControl/TrendingTask.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class TrendingTask : public PostProcessingInterface
7373
static void formatTimeXAxis(TH1* background);
7474
static void formatRunNumberXAxis(TH1* background);
7575
static std::string deduceGraphLegendOptions(const TrendingTaskConfig::Graph& graphConfig);
76+
static void applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style);
7677

7778
/// returns true only if all datasources were available to update reductor
7879
bool trendValues(const Trigger& t, repository::DatabaseInterface&);

Framework/include/QualityControl/TrendingTaskConfig.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,25 @@ struct TrendingTaskConfig : PostProcessingConfig {
3232
TrendingTaskConfig(std::string name, const boost::property_tree::ptree& config);
3333
~TrendingTaskConfig() = default;
3434

35+
// graph style configuration
36+
// colors as defined by ROOT TColor class:
37+
// https://root.cern/doc/master/classTColor.html
38+
// marker colors and styles are as defined by ROOT TAttMarker class
39+
// https://root.cern/doc/master/classTAttMarker.html
40+
// line styles are as defined by ROOT TAttLine class
41+
// https://root.cern/doc/master/classTAttLine.html
42+
// WARNING: Any parameters in this struct will override colliding parameters in option
43+
struct GraphStyle {
44+
int lineColor = -1;
45+
int lineStyle = -1;
46+
int lineWidth = -1;
47+
int markerColor = -1;
48+
int markerStyle = -1;
49+
float markerSize = -1.f;
50+
int fillColor = -1;
51+
int fillStyle = -1;
52+
};
53+
3554
// this corresponds to one TTree::Draw() call, i.e. one graph or histogram drawing
3655
struct Graph {
3756
std::string name;
@@ -40,6 +59,13 @@ struct TrendingTaskConfig : PostProcessingConfig {
4059
std::string selection;
4160
std::string option; // the list of possible options are documented in TGraphPainter and THistPainter
4261
std::string errors;
62+
GraphStyle style;
63+
};
64+
65+
// legend configuration
66+
struct LegendConfig {
67+
int nColumns{ 1 };
68+
float x1{ -1.f }, y1{ -1.f }, x2{ -1.f }, y2{ -1.f }; // NDC coords
4369
};
4470

4571
// this corresponds to one canvas which can include multiple graphs
@@ -49,6 +75,7 @@ struct TrendingTaskConfig : PostProcessingConfig {
4975
std::string graphAxisLabel;
5076
std::string graphYRange;
5177
int colorPalette = 0;
78+
LegendConfig legend;
5279
std::vector<Graph> graphs;
5380
};
5481

Framework/src/TrendingTask.cxx

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,40 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte
142142
}
143143
}
144144

145+
void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style)
146+
{
147+
if (!graph) {
148+
return;
149+
}
150+
151+
if (style.lineColor >= 0) {
152+
graph->SetLineColor(style.lineColor);
153+
}
154+
if (style.lineStyle >= 0) {
155+
graph->SetLineStyle(style.lineStyle);
156+
}
157+
if (style.lineWidth >= 0) {
158+
graph->SetLineWidth(style.lineWidth);
159+
}
160+
161+
if (style.markerColor >= 0) {
162+
graph->SetMarkerColor(style.markerColor);
163+
}
164+
if (style.markerStyle >= 0) {
165+
graph->SetMarkerStyle(style.markerStyle);
166+
}
167+
if (style.markerSize >= 0.f) {
168+
graph->SetMarkerSize(style.markerSize);
169+
}
170+
171+
if (style.fillColor >= 0) {
172+
graph->SetFillColor(style.fillColor);
173+
}
174+
if (style.fillStyle >= 0) {
175+
graph->SetFillStyle(style.fillStyle);
176+
}
177+
}
178+
145179
void TrendingTask::initialize(Trigger, framework::ServiceRegistryRef services)
146180
{
147181
// removing leftovers from any previous runs
@@ -310,19 +344,31 @@ std::string TrendingTask::deduceGraphLegendOptions(const TrendingTaskConfig::Gra
310344
TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig)
311345
{
312346
auto* c = new TCanvas();
313-
auto* legend = new TLegend(0.3, 0.2);
314347

348+
// Legend
349+
TLegend* legend = nullptr;
350+
if (plotConfig.legend.x1 >= 0 && plotConfig.legend.y1 >= 0 && plotConfig.legend.x2 >= 0 && plotConfig.legend.y2 >= 0) {
351+
legend = new TLegend(plotConfig.legend.x1, plotConfig.legend.y1,
352+
plotConfig.legend.x2, plotConfig.legend.y2,
353+
nullptr, "NDC");
354+
if (plotConfig.legend.nColumns > 0) {
355+
legend->SetNColumns(plotConfig.legend.nColumns);
356+
}
357+
} else {
358+
legend = new TLegend(0.3, 0.2);
359+
}
360+
legend->SetBorderSize(0);
361+
legend->SetFillStyle(0);
362+
legend->SetTextSize(0.03);
363+
legend->SetMargin(0.15);
364+
365+
// Keep palette behavior unless user forces explicit colors via per-graph style
315366
if (plotConfig.colorPalette != 0) {
316-
// this will work just once until we bump ROOT to a version which contains this commit:
317-
// https://github.com/root-project/root/commit/0acdbd5be80494cec98ff60ba9a73cfe70a9a57a
318-
// and enable the commented out line
319-
// perhaps JSROOT >7.7.1 will allow us to retain the palette as well.
320367
gStyle->SetPalette(plotConfig.colorPalette);
321368
// This makes ROOT store the selected palette for each generated plot.
322369
// TColor::DefinedColors(1); // TODO enable when available
323370
} else {
324-
// we set the default palette
325-
gStyle->SetPalette();
371+
gStyle->SetPalette(); // default
326372
}
327373

328374
// regardless whether we draw a graph or a histogram, a histogram is always used by TTree::Draw to draw axes and title
@@ -337,6 +383,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig)
337383
// having "SAME" at the first TTree::Draw() call will not work, we have to add it only in subsequent Draw calls
338384
std::string option = firstGraphInPlot ? graphConfig.option : "SAME " + graphConfig.option;
339385

386+
// Draw main series
340387
mTrend->Draw(graphConfig.varexp.c_str(), graphConfig.selection.c_str(), option.c_str());
341388

342389
// For graphs, we allow to draw errors if they are specified.
@@ -357,11 +404,23 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig)
357404
}
358405
}
359406

407+
// Legend entry and styling for graphs
360408
if (auto graph = dynamic_cast<TGraph*>(c->FindObject("Graph"))) {
409+
if (plotOrder >= 2) {
410+
// Style objects after Draw so we override palette/auto styling when requested
411+
applyStyleToGraph(graph, graphConfig.style);
412+
// Keep errors visually consistent with the main series
413+
if (graphErrors) {
414+
applyStyleToGraph(graphErrors, graphConfig.style);
415+
}
416+
}
361417
graph->SetName(graphConfig.name.c_str());
362418
graph->SetTitle(graphConfig.title.c_str());
363-
legend->AddEntry(graph, graphConfig.title.c_str(), deduceGraphLegendOptions(graphConfig).c_str());
419+
legend->AddEntry(graph, graphConfig.title.c_str(),
420+
deduceGraphLegendOptions(graphConfig).c_str());
364421
}
422+
423+
// Legend entry and styling for histograms
365424
if (auto htemp = dynamic_cast<TH1*>(c->FindObject("htemp"))) {
366425
if (plotOrder == 1) {
367426
htemp->SetName(graphConfig.name.c_str());
@@ -376,7 +435,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig)
376435
// so we have to do it here.
377436
htemp->BufferEmpty();
378437
// we keep the pointer to bg histogram for later postprocessing
379-
if (background == nullptr) {
438+
if (!background) {
380439
background = htemp;
381440
}
382441
}
@@ -430,6 +489,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig)
430489
} else {
431490
delete legend;
432491
}
492+
433493
c->Modified();
434494
c->Update();
435495

Framework/src/TrendingTaskConfig.cxx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,55 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre
3636
for (const auto& [_, graphConfig] : graphsConfig.value()) {
3737
// first we use name of the graph, if absent, we use graph title, if absent, we use plot (object) name.
3838
const auto& name = graphConfig.get<std::string>("name", graphConfig.get<std::string>("title", plotConfig.get<std::string>("name")));
39+
GraphStyle style;
40+
style.lineColor = graphConfig.get<int>("style.lineColor", -1);
41+
style.lineStyle = graphConfig.get<int>("style.lineStyle", -1);
42+
style.lineWidth = graphConfig.get<int>("style.lineWidth", -1);
43+
style.markerColor = graphConfig.get<int>("style.markerColor", -1);
44+
style.markerStyle = graphConfig.get<int>("style.markerStyle", -1);
45+
style.markerSize = graphConfig.get<float>("style.markerSize", -1.f);
46+
style.fillColor = graphConfig.get<int>("style.fillColor", -1);
47+
style.fillStyle = graphConfig.get<int>("style.fillStyle", -1);
48+
3949
graphs.push_back({ name,
4050
graphConfig.get<std::string>("title", ""),
4151
graphConfig.get<std::string>("varexp"),
4252
graphConfig.get<std::string>("selection", ""),
4353
graphConfig.get<std::string>("option", ""),
44-
graphConfig.get<std::string>("graphErrors", "") });
54+
graphConfig.get<std::string>("graphErrors", ""),
55+
style });
4556
}
4657
} else {
58+
GraphStyle style;
59+
style.lineColor = plotConfig.get<int>("style.lineColor", -1);
60+
style.lineStyle = plotConfig.get<int>("style.lineStyle", -1);
61+
style.lineWidth = plotConfig.get<int>("style.lineWidth", -1);
62+
style.markerColor = plotConfig.get<int>("style.markerColor", -1);
63+
style.markerStyle = plotConfig.get<int>("style.markerStyle", -1);
64+
style.markerSize = plotConfig.get<float>("style.markerSize", -1.f);
65+
style.fillColor = plotConfig.get<int>("style.fillColor", -1);
66+
style.fillStyle = plotConfig.get<int>("style.fillStyle", -1);
4767
graphs.push_back({ plotConfig.get<std::string>("name", ""),
4868
plotConfig.get<std::string>("title", ""),
4969
plotConfig.get<std::string>("varexp"),
5070
plotConfig.get<std::string>("selection", ""),
5171
plotConfig.get<std::string>("option", ""),
5272
plotConfig.get<std::string>("graphErrors", "") });
5373
}
74+
75+
LegendConfig leg;
76+
leg.nColumns = plotConfig.get<int>("legend.nColumns", 1);
77+
leg.x1 = plotConfig.get<float>("legend.x1", -1.f);
78+
leg.y1 = plotConfig.get<float>("legend.y1", -1.f);
79+
leg.x2 = plotConfig.get<float>("legend.x2", -1.f);
80+
leg.y2 = plotConfig.get<float>("legend.y2", -1.f);
81+
5482
plots.push_back({ plotConfig.get<std::string>("name"),
5583
plotConfig.get<std::string>("title", ""),
5684
plotConfig.get<std::string>("graphAxisLabel", ""),
5785
plotConfig.get<std::string>("graphYRange", ""),
5886
plotConfig.get<int>("colorPalette", 0),
87+
leg,
5988
graphs });
6089
}
6190

Modules/FIT/FT0/etc/ft0-post-processing.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,33 @@
2222
"url": ""
2323
}
2424
},
25+
"tasks": {
26+
"Digits": {
27+
"active": "true",
28+
"className": "o2::quality_control_modules::ft0::DigitQcTask",
29+
"moduleName": "QcFT0",
30+
"detectorName": "FT0",
31+
"cycleDurationSeconds": "60",
32+
"resetAfterCycles": "1",
33+
"dataSource": {
34+
"type": "direct",
35+
"query": "digits:FT0/DIGITSBC/0;channels:FT0/DIGITSCH/0"
36+
},
37+
"taskParameters": {
38+
"#ChannelIDs": "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215",
39+
"ChannelIDsAmpVsTime": "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215",
40+
"trgThresholdTimeLow": "-100",
41+
"trgThresholdTimeHigh": "100",
42+
"trgModeSide": "A+C",
43+
"trgModeThresholdVar": "Ampl",
44+
"trgThresholdSCenA": "20",
45+
"trgThresholdCenA": "40",
46+
"trgOrGate": "153",
47+
"trgChargeLevelLow": "0",
48+
"trgChargeLevelHigh": "4095"
49+
}
50+
}
51+
},
2552
"checks": {
2653
"ASideInnerMIPCheck": {
2754
"active": "true",
@@ -416,6 +443,7 @@
416443
"className": "o2::quality_control::postprocessing::TrendingTask",
417444
"moduleName": "QcFT0",
418445
"detectorName": "FT0",
446+
"resumeTrend": "true",
419447
"dataSources": [
420448
{
421449
"type": "repository",
@@ -431,6 +459,34 @@
431459
}
432460
],
433461
"plots": [
462+
{
463+
"name": "trend_cycle_duration_ntf_corr",
464+
"title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]",
465+
"legend": { "enabled": true, "x1": 0.70, "y1": 0.70, "x2": 0.93, "y2": 0.90, "nColumns": 1 },
466+
"graphs": [
467+
{
468+
"title": "cycle duration [ns]",
469+
"varexp": "CycleDuration.entries:time",
470+
"selection": "",
471+
"option": "*LP",
472+
"style": { "lineColor": 38, "markerColor": 4, "markerStyle": 20, "lineWidth": 2 }
473+
},
474+
{
475+
"title": "cycle duration [TimeFrames]",
476+
"varexp": "CycleDurationNTF.entries:time",
477+
"selection": "",
478+
"option": "*LP",
479+
"style": { "lineColor": 8, "markerColor": 3, "markerStyle": 30, "lineWidth": 2 }
480+
},
481+
{
482+
"title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]",
483+
"varexp": "CycleDuration.entries/CycleDurationNTF.entries:time",
484+
"selection": "",
485+
"option": "*LP",
486+
"style": { "lineColor": 31, "markerColor": 30, "markerStyle": 23, "lineWidth": 2 }
487+
}
488+
]
489+
},
434490
{
435491
"name": "trend_cycle_duration",
436492
"title": "cycle duration [ns]",

0 commit comments

Comments
 (0)