6 TF1 *fit_func, Double_t range_low,
7 Double_t range_high, Int_t num_peaks,
8 const TString &info_label)
9 : TGMainFrame(parent, 1400, 900) {
13 info_label_text_ = info_label;
14 range_low_ = range_low;
15 range_high_ = range_high;
16 original_range_low_ = range_low;
17 original_range_high_ = range_high;
18 hist_x_min_ = hist->GetXaxis()->GetXmin();
19 hist_x_max_ = hist->GetXaxis()->GetXmax();
20 num_peaks_ = num_peaks;
21 num_params_ = num_peaks * 10 + 2;
25 needs_redraw_ = kFALSE;
28 if (fit_func_->GetNpar() != num_params_) {
29 std::cerr <<
"InteractiveFitEditor: expected " << num_params_
30 <<
" parameters but TF1 has " << fit_func_->GetNpar()
35 original_params_ =
new Double_t[num_params_];
36 original_bounds_low_ =
new Double_t[num_params_];
37 original_bounds_high_ =
new Double_t[num_params_];
38 original_fixed_ =
new Bool_t[num_params_];
39 current_bounds_low_ =
new Double_t[num_params_];
40 current_bounds_high_ =
new Double_t[num_params_];
42 for (Int_t i = 0; i < num_params_; i++) {
43 original_params_[i] = fit_func_->GetParameter(i);
45 Double_t lo = 0, hi = 0;
46 fit_func_->GetParLimits(i, lo, hi);
49 original_fixed_[i] = (lo >= hi);
51 if (original_fixed_[i]) {
53 GetDefaultBounds(i, lo, hi);
56 original_bounds_low_[i] = lo;
57 original_bounds_high_[i] = hi;
58 current_bounds_low_[i] = lo;
59 current_bounds_high_[i] = hi;
63 sliders_ =
new TGHSlider *[num_params_];
64 value_entries_ =
new TGNumberEntry *[num_params_];
65 fix_checks_ =
new TGCheckButton *[num_params_];
66 lo_bound_entries_ =
new TGNumberEntry *[num_params_];
67 hi_bound_entries_ =
new TGNumberEntry *[num_params_];
73 plus3_line_ =
nullptr;
74 minus3_line_ =
nullptr;
75 chi2_label_ =
nullptr;
77 for (Int_t p = 0; p < 3; p++) {
78 for (Int_t c = 0; c < 4; c++) {
79 comp_graphs_[p][c] =
nullptr;
86 redraw_timer_ =
new TTimer(
this, 50);
87 redraw_timer_->TurnOn();
89 SetWindowName(
"Interactive Fit Editor");
91 Resize(GetDefaultSize());
99 redraw_timer_->TurnOff();
100 delete redraw_timer_;
103 delete[] original_params_;
104 delete[] original_bounds_low_;
105 delete[] original_bounds_high_;
106 delete[] original_fixed_;
107 delete[] current_bounds_low_;
108 delete[] current_bounds_high_;
111 delete[] value_entries_;
112 delete[] fix_checks_;
113 delete[] lo_bound_entries_;
114 delete[] hi_bound_entries_;
124 if (main_pad_ && main_pad_->GetListOfPrimitives()) {
125 main_pad_->GetListOfPrimitives()->Clear(
"nodelete");
127 if (residual_pad_ && residual_pad_->GetListOfPrimitives()) {
128 residual_pad_->GetListOfPrimitives()->Clear(
"nodelete");
130 for (Int_t p = 0; p < 3; p++) {
131 for (Int_t c = 0; c < 4; c++) {
132 delete comp_graphs_[p][c];
142 delete residual_pad_;
148void InteractiveFitEditor::BuildGUI() {
150 TGHorizontalFrame *main_frame =
new TGHorizontalFrame(
this, 1400, 850);
152 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
155 new TRootEmbeddedCanvas(
"FitEditorCanvas", main_frame, 850, 800);
156 main_frame->AddFrame(
158 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
160 TGVerticalFrame *controls =
new TGVerticalFrame(main_frame, 520, 800);
161 main_frame->AddFrame(
162 controls,
new TGLayoutHints(kLHintsExpandY | kLHintsRight, 2, 2, 2, 2));
164 if (info_label_text_.Length() > 0) {
165 TGGroupFrame *info_grp =
new TGGroupFrame(controls,
"Info", kVerticalFrame);
166 controls->AddFrame(info_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
167 TGLabel *info_label =
new TGLabel(info_grp, info_label_text_);
168 info_grp->AddFrame(info_label,
169 new TGLayoutHints(kLHintsCenterX, 4, 4, 4, 4));
172 TGGroupFrame *range_grp =
173 new TGGroupFrame(controls,
"Fit Range", kVerticalFrame);
174 controls->AddFrame(range_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
177 new TGDoubleHSlider(range_grp, 200, kDoubleScaleNo, kRangeSlider);
178 range_slider_->SetRange(hist_x_min_, hist_x_max_);
179 range_slider_->SetPosition(range_low_, range_high_);
180 range_slider_->Associate(
this);
181 range_grp->AddFrame(range_slider_,
182 new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
184 TGHorizontalFrame *range_entries =
new TGHorizontalFrame(range_grp, 200, 28);
185 range_grp->AddFrame(range_entries,
186 new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
188 TGLabel *range_lo_label =
new TGLabel(range_entries,
"Low:");
189 range_entries->AddFrame(range_lo_label,
190 new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
191 range_lo_entry_ =
new TGNumberEntry(
192 range_entries, range_low_, 8, kRangeLoEntry, TGNumberFormat::kNESReal,
193 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
194 range_lo_entry_->GetNumberEntry()->Associate(
this);
195 range_entries->AddFrame(
197 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
199 TGLabel *range_hi_label =
new TGLabel(range_entries,
"High:");
200 range_entries->AddFrame(range_hi_label,
201 new TGLayoutHints(kLHintsCenterY, 8, 2, 2, 2));
202 range_hi_entry_ =
new TGNumberEntry(
203 range_entries, range_high_, 8, kRangeHiEntry, TGNumberFormat::kNESReal,
204 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
205 range_hi_entry_->GetNumberEntry()->Associate(
this);
206 range_entries->AddFrame(
208 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
211 TGTab *tabs =
new TGTab(controls, 510, 700);
213 tabs,
new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
215 for (Int_t p = 0; p < num_peaks_; p++) {
216 TString tab_name = TString::Format(
"Peak %d", p + 1);
217 TGCompositeFrame *tab_frame = tabs->AddTab(tab_name);
218 BuildPeakTab(tab_frame, p);
221 TGCompositeFrame *bkg_tab = tabs->AddTab(
"Background");
222 BuildBackgroundTab(bkg_tab);
224 TGHorizontalFrame *btn_frame =
new TGHorizontalFrame(controls, 510, 40);
226 btn_frame,
new TGLayoutHints(kLHintsExpandX | kLHintsBottom, 2, 2, 5, 5));
228 TGTextButton *refit_btn =
new TGTextButton(btn_frame,
"Refit", kBtnRefit);
229 refit_btn->Associate(
this);
230 TGTextButton *accept_btn =
new TGTextButton(btn_frame,
"Accept", kBtnAccept);
231 accept_btn->Associate(
this);
232 TGTextButton *cancel_btn =
new TGTextButton(btn_frame,
"Cancel", kBtnCancel);
233 cancel_btn->Associate(
this);
234 TGTextButton *reset_btn =
new TGTextButton(btn_frame,
"Reset", kBtnReset);
235 reset_btn->Associate(
this);
237 TGLayoutHints *btn_hints =
new TGLayoutHints(kLHintsExpandX, 4, 4, 2, 2);
239 btn_frame->AddFrame(refit_btn, btn_hints);
240 btn_frame->AddFrame(accept_btn, btn_hints);
241 btn_frame->AddFrame(cancel_btn, btn_hints);
242 btn_frame->AddFrame(reset_btn, btn_hints);
245void InteractiveFitEditor::BuildPeakTab(TGCompositeFrame *parent,
247 Int_t offset = peak_idx * 10;
250 TGGroupFrame *gaus_grp =
new TGGroupFrame(parent,
"Gaussian", kVerticalFrame);
251 parent->AddFrame(gaus_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 1));
252 AddParamRow(gaus_grp, offset + 0,
"Mu");
253 AddParamRow(gaus_grp, offset + 1,
"Sigma");
254 AddParamRow(gaus_grp, offset + 2,
"Amplitude");
257 TGGroupFrame *step_grp =
new TGGroupFrame(parent,
"Step", kVerticalFrame);
258 parent->AddFrame(step_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
259 AddParamRow(step_grp, offset + 3,
"Step Amp");
262 TGGroupFrame *ltail_grp =
263 new TGGroupFrame(parent,
"Low-Side Tails", kVerticalFrame);
264 parent->AddFrame(ltail_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
265 AddParamRow(ltail_grp, offset + 4,
"Exp Amp");
266 AddParamRow(ltail_grp, offset + 5,
"Exp Decay/Sigma");
267 AddParamRow(ltail_grp, offset + 6,
"Lin Amp");
268 AddParamRow(ltail_grp, offset + 7,
"Lin Slope");
271 TGGroupFrame *htail_grp =
272 new TGGroupFrame(parent,
"High-Side Tail", kVerticalFrame);
273 parent->AddFrame(htail_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 3));
274 AddParamRow(htail_grp, offset + 8,
"Exp Amp");
275 AddParamRow(htail_grp, offset + 9,
"Exp Decay/Sigma");
278void InteractiveFitEditor::BuildBackgroundTab(TGCompositeFrame *parent) {
279 TGGroupFrame *bkg_grp =
280 new TGGroupFrame(parent,
"Background", kVerticalFrame);
281 parent->AddFrame(bkg_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
282 AddParamRow(bkg_grp, BkgConstIdx(),
"Constant");
283 AddParamRow(bkg_grp, BkgSlopeIdx(),
"Slope");
286void InteractiveFitEditor::AddParamRow(TGCompositeFrame *parent,
287 Int_t param_idx,
const char *name) {
288 TGHorizontalFrame *row =
new TGHorizontalFrame(parent, 490, 28);
289 parent->AddFrame(row,
new TGLayoutHints(kLHintsExpandX, 1, 1, 1, 1));
291 Double_t val = fit_func_->GetParameter(param_idx);
292 Bool_t fixed = original_fixed_[param_idx];
295 TGLabel *label =
new TGLabel(row, name);
296 row->AddFrame(label,
new TGLayoutHints(kLHintsCenterY, 2, 4, 2, 2));
297 label->Resize(90, 20);
301 new TGHSlider(row, 140, kSlider1, kSliderBase + param_idx);
302 slider->SetRange(0, kSliderRes);
303 slider->SetPosition(ValToSlider(param_idx, val));
304 slider->Associate(
this);
305 row->AddFrame(slider,
306 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
307 sliders_[param_idx] = slider;
310 TGNumberEntry *entry =
new TGNumberEntry(
311 row, val, 8, kEntryBase + param_idx, TGNumberFormat::kNESReal,
312 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
313 entry->GetNumberEntry()->Associate(
this);
314 row->AddFrame(entry,
new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
315 value_entries_[param_idx] = entry;
318 TGCheckButton *fix_cb =
new TGCheckButton(row,
"Fix", kFixBase + param_idx);
319 fix_cb->Associate(
this);
321 fix_cb->SetState(kButtonDown);
323 row->AddFrame(fix_cb,
new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
324 fix_checks_[param_idx] = fix_cb;
327 TGNumberEntry *lo_entry =
new TGNumberEntry(
328 row, current_bounds_low_[param_idx], 6, kLoBoundBase + param_idx,
329 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
330 TGNumberFormat::kNELNoLimits);
331 lo_entry->GetNumberEntry()->Associate(
this);
332 row->AddFrame(lo_entry,
new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
333 lo_bound_entries_[param_idx] = lo_entry;
336 TGNumberEntry *hi_entry =
new TGNumberEntry(
337 row, current_bounds_high_[param_idx], 6, kHiBoundBase + param_idx,
338 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
339 TGNumberFormat::kNELNoLimits);
340 hi_entry->GetNumberEntry()->Associate(
this);
341 row->AddFrame(hi_entry,
new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
342 hi_bound_entries_[param_idx] = hi_entry;
346 slider->SetEnabled(kFALSE);
347 entry->GetNumberEntry()->SetEnabled(kFALSE);
353void InteractiveFitEditor::InitDrawing() {
354 TCanvas *canvas = embedded_canvas_->GetCanvas();
357 main_pad_ =
new TPad(
"editor_main",
"editor_main", 0, 0.3, 1, 1.0);
358 main_pad_->SetBottomMargin(0.04);
359 main_pad_->SetGridx(1);
360 main_pad_->SetGridy(1);
361 main_pad_->SetTopMargin(0.08);
364 residual_pad_ =
new TPad(
"editor_res",
"editor_res", 0, 0, 1, 0.3);
365 residual_pad_->SetTopMargin(0.04);
366 residual_pad_->SetBottomMargin(0.35);
367 residual_pad_->SetGridx(1);
368 residual_pad_->SetGridy(1);
369 residual_pad_->Draw();
374 hist_draw_ =
static_cast<TH1 *
>(hist_->Clone(
"hist_editor_draw"));
375 hist_draw_->SetDirectory(0);
376 Double_t min_disp = 0.9 * range_low_;
377 Double_t max_disp = 1.1 * range_high_;
378 hist_draw_->GetXaxis()->SetRangeUser(min_disp, max_disp);
379 hist_draw_->GetXaxis()->SetLabelSize(0);
380 hist_draw_->GetXaxis()->SetTitleSize(0);
381 hist_draw_->SetLineColor(kViolet);
382 hist_draw_->SetLineWidth(2);
385 fit_func_->SetLineColor(kAzure);
386 fit_func_->SetNpx(1000);
387 fit_func_->Draw(
"same");
390 range_low_, range_high_, 2);
391 bkg_draw_->SetParameter(0, fit_func_->GetParameter(BkgConstIdx()));
392 bkg_draw_->SetParameter(1, fit_func_->GetParameter(BkgSlopeIdx()));
393 bkg_draw_->SetLineColor(kGreen);
394 bkg_draw_->SetNpx(1000);
395 bkg_draw_->Draw(
"same");
397 Int_t comp_colors[4] = {kBlack, kGray, kRed, kOrange};
399 for (Int_t p = 0; p < num_peaks_; p++) {
400 for (Int_t c = 0; c < 4; c++) {
401 comp_graphs_[p][c] =
new TGraph(kNDrawPts);
402 comp_graphs_[p][c]->SetLineColor(comp_colors[c]);
403 comp_graphs_[p][c]->SetLineStyle(PeakStyle(p));
404 comp_graphs_[p][c]->SetLineWidth(2);
405 comp_graphs_[p][c]->Draw(
"L same");
409 chi2_label_ =
new TLatex();
410 chi2_label_->SetNDC();
411 chi2_label_->SetTextSize(0.045);
412 chi2_label_->SetTextAlign(31);
415 main_pad_->SetLogy(kTRUE);
421 Int_t nbins = hist_->GetNbinsX();
422 for (Int_t i = 1; i <= nbins; i++) {
423 Double_t x = hist_->GetBinCenter(i);
424 if (x < range_low_ || x > range_high_)
426 Double_t data = hist_->GetBinContent(i);
427 Double_t error = hist_->GetBinError(i);
428 if (error > 0 && data > 0)
432 if (n_res_points_ < 1)
435 res_graph_ =
new TGraph(n_res_points_);
436 res_graph_->SetMarkerStyle(20);
437 res_graph_->SetMarkerSize(0.8);
438 res_graph_->SetMarkerColor(kAzure);
439 res_graph_->SetLineColor(kAzure);
440 res_graph_->SetTitle(
"");
443 hist_draw_->GetXaxis()->GetBinLowEdge(hist_draw_->GetXaxis()->GetFirst());
445 hist_draw_->GetXaxis()->GetBinUpEdge(hist_draw_->GetXaxis()->GetLast());
446 res_graph_->GetXaxis()->SetLimits(ax_min, ax_max);
447 res_graph_->GetYaxis()->SetTitle(
"#delta/#sigma");
448 res_graph_->GetXaxis()->SetTitle(hist_->GetXaxis()->GetTitle());
449 res_graph_->GetXaxis()->SetTitleSize(0.13);
450 res_graph_->GetYaxis()->SetTitleSize(0.13);
451 res_graph_->GetXaxis()->SetLabelSize(0.12);
452 res_graph_->GetYaxis()->SetLabelSize(0.12);
453 res_graph_->GetXaxis()->SetTitleOffset(1.0);
454 res_graph_->GetYaxis()->SetTitleOffset(0.3);
455 res_graph_->GetYaxis()->SetNdivisions(505);
456 res_graph_->GetXaxis()->SetNdivisions(510);
457 res_graph_->GetYaxis()->CenterTitle(kTRUE);
458 res_graph_->GetYaxis()->SetRangeUser(-5.5, 5.5);
459 res_graph_->Draw(
"AP");
461 zero_line_ =
new TF1(
"zero_editor",
"0", ax_min, ax_max);
462 zero_line_->SetLineColor(kBlack);
463 zero_line_->SetLineStyle(2);
464 zero_line_->SetLineWidth(2);
465 zero_line_->Draw(
"same");
467 plus3_line_ =
new TF1(
"plus3_editor",
"3", ax_min, ax_max);
468 plus3_line_->SetLineColor(kGray + 2);
469 plus3_line_->SetLineStyle(3);
470 plus3_line_->SetLineWidth(2);
471 plus3_line_->Draw(
"same");
473 minus3_line_ =
new TF1(
"minus3_editor",
"-3", ax_min, ax_max);
474 minus3_line_->SetLineColor(kGray + 2);
475 minus3_line_->SetLineStyle(3);
476 minus3_line_->SetLineWidth(2);
477 minus3_line_->Draw(
"same");
483void InteractiveFitEditor::UpdateCanvas() {
488 bkg_draw_->SetParameter(0, fit_func_->GetParameter(BkgConstIdx()));
489 bkg_draw_->SetParameter(1, fit_func_->GetParameter(BkgSlopeIdx()));
492 Double_t chi2_sum = 0;
494 Int_t nbins_chi2 = hist_->GetNbinsX();
495 for (Int_t i = 1; i <= nbins_chi2; i++) {
496 Double_t x = hist_->GetBinCenter(i);
497 if (x < range_low_ || x > range_high_)
499 Double_t data = hist_->GetBinContent(i);
500 Double_t error = hist_->GetBinError(i);
501 if (error > 0 && data > 0) {
502 Double_t fit_val = fit_func_->Eval(x);
503 Double_t residual = (data - fit_val) / error;
504 chi2_sum += residual * residual;
509 for (Int_t i = 0; i < num_params_; i++) {
513 Int_t ndf = ndf_bins - n_free;
515 chi2_label_->SetText(0.85, 0.85,
516 Form(
"#chi^{2}/ndf = %.3f", chi2_sum / ndf));
518 chi2_label_->SetText(0.85, 0.85,
"#chi^{2}/ndf = N/A");
522 zero_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
523 plus3_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
524 minus3_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
527 res_graph_->GetYaxis()->SetRangeUser(-5.5, 5.5);
529 TCanvas *canvas = embedded_canvas_->GetCanvas();
530 main_pad_->Modified();
531 residual_pad_->Modified();
535 needs_redraw_ = kFALSE;
538void InteractiveFitEditor::UpdateCompPoints() {
539 Double_t x_step = (range_high_ - range_low_) / (kNDrawPts - 1);
541 Double_t bkg_const = fit_func_->GetParameter(BkgConstIdx());
542 Double_t bkg_slope = fit_func_->GetParameter(BkgSlopeIdx());
544 for (Int_t p = 0; p < num_peaks_; p++) {
547 Double_t mu = fit_func_->GetParameter(off + 0);
548 Double_t sigma = fit_func_->GetParameter(off + 1);
549 Double_t gaus_amp = fit_func_->GetParameter(off + 2);
551 Double_t step_amp = fit_func_->GetParameter(off + 3) * gaus_amp;
552 Double_t lexp_amp = fit_func_->GetParameter(off + 4) * gaus_amp;
553 Double_t lexp_dec = fit_func_->GetParameter(off + 5);
554 Double_t llin_amp = fit_func_->GetParameter(off + 6) * gaus_amp;
555 Double_t llin_slp = fit_func_->GetParameter(off + 7);
556 Double_t hexp_amp = fit_func_->GetParameter(off + 8) * gaus_amp;
557 Double_t hexp_dec = fit_func_->GetParameter(off + 9);
559 for (Int_t i = 0; i < kNDrawPts; i++) {
560 Double_t x = range_low_ + i * x_step;
561 Double_t x_arr[1] = {x};
563 Double_t bkg_par[2] = {bkg_const, bkg_slope};
566 Double_t gaus_par[3] = {mu, sigma, gaus_amp};
567 comp_graphs_[p][0]->SetPoint(
570 Double_t step_par[3] = {mu, sigma, step_amp};
571 comp_graphs_[p][1]->SetPoint(
574 Double_t lt_par[6] = {mu, sigma, lexp_amp, lexp_dec, llin_amp, llin_slp};
575 comp_graphs_[p][2]->SetPoint(
578 Double_t ht_par[4] = {mu, sigma, hexp_amp, hexp_dec};
579 comp_graphs_[p][3]->SetPoint(
585void InteractiveFitEditor::UpdateResPoints() {
586 Int_t nbins = hist_->GetNbinsX();
588 for (Int_t i = 1; i <= nbins; i++) {
589 Double_t x = hist_->GetBinCenter(i);
590 if (x < range_low_ || x > range_high_)
592 Double_t data = hist_->GetBinContent(i);
593 Double_t error = hist_->GetBinError(i);
594 if (error > 0 && data > 0) {
595 Double_t fit_val = fit_func_->Eval(x);
596 Double_t pull = (data - fit_val) / error;
597 res_graph_->SetPoint(pt, x, pull);
605 Double_t disp_lo = 0.9 * range_low_;
606 Double_t disp_hi = 1.1 * range_high_;
607 res_graph_->GetXaxis()->SetLimits(disp_lo, disp_hi);
612void InteractiveFitEditor::SyncAllWidgets() {
614 for (Int_t i = 0; i < num_params_; i++) {
620void InteractiveFitEditor::SyncWidget(Int_t param_idx) {
621 Double_t val = fit_func_->GetParameter(param_idx);
624 Double_t lo = 0, hi = 0;
625 fit_func_->GetParLimits(param_idx, lo, hi);
626 Bool_t fixed = (lo >= hi);
629 if (!fixed && lo < hi) {
630 current_bounds_low_[param_idx] = lo;
631 current_bounds_high_[param_idx] = hi;
635 Double_t clamped = val;
636 if (clamped < current_bounds_low_[param_idx])
637 clamped = current_bounds_low_[param_idx];
638 if (clamped > current_bounds_high_[param_idx])
639 clamped = current_bounds_high_[param_idx];
641 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, clamped));
642 value_entries_[param_idx]->SetNumber(val);
645 fix_checks_[param_idx]->SetState(kButtonDown);
646 sliders_[param_idx]->SetEnabled(kFALSE);
647 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
649 fix_checks_[param_idx]->SetState(kButtonUp);
650 sliders_[param_idx]->SetEnabled(kTRUE);
651 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
654 lo_bound_entries_[param_idx]->SetNumber(current_bounds_low_[param_idx]);
655 hi_bound_entries_[param_idx]->SetNumber(current_bounds_high_[param_idx]);
660void InteractiveFitEditor::OnSliderMoved(Int_t param_idx) {
663 if (IsFixed(param_idx))
666 Int_t pos = sliders_[param_idx]->GetPosition();
667 Double_t val = SliderToVal(param_idx, pos);
668 fit_func_->SetParameter(param_idx, val);
671 value_entries_[param_idx]->SetNumber(val);
674 needs_redraw_ = kTRUE;
677void InteractiveFitEditor::OnEntryChanged(Int_t param_idx) {
680 if (IsFixed(param_idx))
683 Double_t val = value_entries_[param_idx]->GetNumber();
686 Bool_t bounds_changed = kFALSE;
687 if (val < current_bounds_low_[param_idx]) {
688 current_bounds_low_[param_idx] = val;
689 bounds_changed = kTRUE;
691 if (val > current_bounds_high_[param_idx]) {
692 current_bounds_high_[param_idx] = val;
693 bounds_changed = kTRUE;
696 fit_func_->SetParameter(param_idx, val);
697 fit_func_->SetParLimits(param_idx, current_bounds_low_[param_idx],
698 current_bounds_high_[param_idx]);
701 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, val));
702 if (bounds_changed) {
703 lo_bound_entries_[param_idx]->SetNumber(current_bounds_low_[param_idx]);
704 hi_bound_entries_[param_idx]->SetNumber(current_bounds_high_[param_idx]);
708 needs_redraw_ = kTRUE;
711void InteractiveFitEditor::OnBoundsChanged(Int_t param_idx) {
715 Double_t new_lo = lo_bound_entries_[param_idx]->GetNumber();
716 Double_t new_hi = hi_bound_entries_[param_idx]->GetNumber();
718 if (new_lo >= new_hi)
721 current_bounds_low_[param_idx] = new_lo;
722 current_bounds_high_[param_idx] = new_hi;
725 Double_t val = fit_func_->GetParameter(param_idx);
730 fit_func_->SetParameter(param_idx, val);
732 if (!IsFixed(param_idx)) {
733 fit_func_->SetParLimits(param_idx, new_lo, new_hi);
737 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, val));
738 value_entries_[param_idx]->SetNumber(val);
741 needs_redraw_ = kTRUE;
744void InteractiveFitEditor::OnFixToggled(Int_t param_idx) {
745 Bool_t now_fixed = (fix_checks_[param_idx]->GetState() == kButtonDown);
748 Double_t val = fit_func_->GetParameter(param_idx);
749 fit_func_->FixParameter(param_idx, val);
750 sliders_[param_idx]->SetEnabled(kFALSE);
751 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
754 fit_func_->SetParLimits(param_idx, current_bounds_low_[param_idx],
755 current_bounds_high_[param_idx]);
756 sliders_[param_idx]->SetEnabled(kTRUE);
757 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
760 needs_redraw_ = kTRUE;
763void InteractiveFitEditor::OnRangeChanged() {
767 Double_t new_lo = range_slider_->GetMinPosition();
768 Double_t new_hi = range_slider_->GetMaxPosition();
770 if (new_lo >= new_hi)
774 range_high_ = new_hi;
776 fit_func_->SetRange(range_low_, range_high_);
777 bkg_draw_->SetRange(range_low_, range_high_);
780 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
784 range_lo_entry_->SetNumber(range_low_);
785 range_hi_entry_->SetNumber(range_high_);
788 needs_redraw_ = kTRUE;
793void InteractiveFitEditor::DoRefit() {
795 for (Int_t i = 0; i < num_params_; i++) {
797 fit_func_->SetParLimits(i, current_bounds_low_[i],
798 current_bounds_high_[i]);
802 hist_->Fit(fit_func_,
"LSMRBEN");
805 needs_redraw_ = kTRUE;
809void InteractiveFitEditor::DoAccept() {
814void InteractiveFitEditor::DoCancel() {
816 for (Int_t i = 0; i < num_params_; i++) {
817 fit_func_->SetParameter(i, original_params_[i]);
818 if (original_fixed_[i]) {
819 fit_func_->FixParameter(i, original_params_[i]);
821 fit_func_->SetParLimits(i, original_bounds_low_[i],
822 original_bounds_high_[i]);
826 fit_func_->SetRange(original_range_low_, original_range_high_);
831void InteractiveFitEditor::DoReset() {
833 for (Int_t i = 0; i < num_params_; i++) {
834 fit_func_->SetParameter(i, original_params_[i]);
835 current_bounds_low_[i] = original_bounds_low_[i];
836 current_bounds_high_[i] = original_bounds_high_[i];
837 if (original_fixed_[i]) {
838 fit_func_->FixParameter(i, original_params_[i]);
840 fit_func_->SetParLimits(i, original_bounds_low_[i],
841 original_bounds_high_[i]);
846 range_low_ = original_range_low_;
847 range_high_ = original_range_high_;
848 fit_func_->SetRange(range_low_, range_high_);
849 bkg_draw_->SetRange(range_low_, range_high_);
850 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
852 range_slider_->SetPosition(range_low_, range_high_);
853 range_lo_entry_->SetNumber(range_low_);
854 range_hi_entry_->SetNumber(range_high_);
858 needs_redraw_ = kTRUE;
871 switch (GET_MSG(msg)) {
873 switch (GET_SUBMSG(msg)) {
875 if (parm1 == kBtnRefit)
877 else if (parm1 == kBtnAccept)
879 else if (parm1 == kBtnCancel)
881 else if (parm1 == kBtnReset)
885 case kCM_CHECKBUTTON:
886 if (parm1 >= kFixBase && parm1 < kFixBase + num_params_) {
887 OnFixToggled(parm1 - kFixBase);
894 if (parm1 == kRangeSlider) {
896 }
else if (parm1 >= kSliderBase && parm1 < kSliderBase + num_params_) {
897 OnSliderMoved(parm1 - kSliderBase);
902 if (GET_SUBMSG(msg) == kTE_ENTER || GET_SUBMSG(msg) == kTE_TAB) {
903 if (parm1 == kRangeLoEntry || parm1 == kRangeHiEntry) {
905 Double_t new_lo = range_lo_entry_->GetNumber();
906 Double_t new_hi = range_hi_entry_->GetNumber();
907 if (new_lo < new_hi) {
909 range_slider_->SetPosition(new_lo, new_hi);
913 }
else if (parm1 >= kEntryBase && parm1 < kEntryBase + num_params_) {
914 OnEntryChanged(parm1 - kEntryBase);
915 }
else if (parm1 >= kLoBoundBase && parm1 < kLoBoundBase + num_params_) {
916 OnBoundsChanged(parm1 - kLoBoundBase);
917 }
else if (parm1 >= kHiBoundBase && parm1 < kHiBoundBase + num_params_) {
918 OnBoundsChanged(parm1 - kHiBoundBase);
928 if (timer == redraw_timer_ && needs_redraw_) {
938Int_t InteractiveFitEditor::ValToSlider(Int_t param_idx, Double_t val) {
939 Double_t lo = current_bounds_low_[param_idx];
940 Double_t hi = current_bounds_high_[param_idx];
943 Double_t frac = (val - lo) / (hi - lo);
948 return static_cast<Int_t
>(frac * kSliderRes);
951Double_t InteractiveFitEditor::SliderToVal(Int_t param_idx, Int_t pos) {
952 Double_t lo = current_bounds_low_[param_idx];
953 Double_t hi = current_bounds_high_[param_idx];
954 Double_t frac =
static_cast<Double_t
>(pos) / kSliderRes;
955 return lo + frac * (hi - lo);
958Bool_t InteractiveFitEditor::IsFixed(Int_t param_idx) {
959 return (fix_checks_[param_idx]->GetState() == kButtonDown);
962void InteractiveFitEditor::GetDefaultBounds(Int_t param_idx, Double_t &lo,
964 Double_t peak_height = hist_->GetMaximum();
965 Double_t range_width = range_high_ - range_low_;
967 if (param_idx == BkgConstIdx()) {
972 if (param_idx == BkgSlopeIdx()) {
973 lo = -peak_height / range_width;
974 hi = peak_height / range_width;
978 Int_t local = param_idx % 10;
985 lo = range_width * 0.001;
986 hi = range_width * 0.5;
990 hi = peak_height * 2;
1027Int_t InteractiveFitEditor::PeakStyle(Int_t peak_idx) {
1038 Double_t range_high, Int_t num_peaks,
1039 const TString &info_label) {
1041 std::cerr <<
"InteractiveFitEditor: GUI not available (gClient is null). "
1042 <<
"Make sure you are not in batch mode." << std::endl;
1052 range_high, num_peaks, info_label);
1054 while (!editor->
IsDone()) {
1055 gSystem->ProcessEvents();
1073 gROOT->GetListOfCanvases()->Remove(ecanvas);
1074 ecanvas->SetBatch(kTRUE);
1079 editor->DontCallClose();
1080 editor->UnmapWindow();
1081 gSystem->ProcessEvents();
Stops a recoverable X protocol error from becoming a fatal SIGSEGV.
AUXErrorHandlerSave AUInstallTolerantXErrorHandler()
Install the tolerant X error handler for the duration of an event loop.
void AURestoreXErrorHandler(AUXErrorHandlerSave s)
Put back the handler that was in place before the tolerant one.
Bool_t LaunchInteractiveFitEditor(TH1 *hist, TF1 *fit_func, Double_t range_low, Double_t range_high, Int_t num_peaks, const TString &info_label)
Interactive editor for a TF1 fit from FittingUtils.
virtual ~InteractiveFitEditor()
Destroys the widgets and drawing objects the editor created.
TRootEmbeddedCanvas * GetEmbeddedCanvas()
The canvas holding the fit and residual pads.
virtual Bool_t ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
ROOT GUI message dispatch for every widget in the editor.
virtual void CloseWindow()
Window-manager close. Treated as a cancel, not an accept.
Bool_t IsDone() const
Whether the editor has finished and the loop may exit.
TTimer * GetRedrawTimer()
The coalescing redraw timer, for the driving event loop.
InteractiveFitEditor(const TGWindow *parent, TH1 *hist, TF1 *fit_func, Double_t range_low, Double_t range_high, Int_t num_peaks, const TString &info_label="")
Build the editor around an existing histogram and fit function.
virtual Bool_t HandleTimer(TTimer *timer)
Redraw tick.
Bool_t WasAccepted() const
Whether the user accepted rather than cancelled.
Double_t LinearBackground(Double_t *x, Double_t *par)
Straight-line background.
Double_t Step(Double_t *x, Double_t *par)
Resolution-smeared step shelf below the peak.
Double_t Gaussian(Double_t *x, Double_t *par)
Gaussian peak.
Double_t LowTail(Double_t *x, Double_t *par)
Combined exponential and linear tail below the peak.
Double_t HighTail(Double_t *x, Double_t *par)
Exponential tail above the peak, from pileup.
What AUInstallTolerantXErrorHandler() needs in order to undo itself.