Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TQt6GedEditor.cxx
Go to the documentation of this file.
1// Author: Sergey Linev, GSI 27/08/2026
2
3/*************************************************************************
4 * Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "TQt6GedEditor.h"
12
13#include "TCanvas.h"
14#include "TROOT.h"
15#include "TColor.h"
16#include "TQt6Canvas.h"
17#include "QCanvasWidget.h"
18
19#include "TClass.h"
20#include "TBaseClass.h"
21#include "TMethod.h"
22#include "TMethodCall.h"
23
24#include "TAttMarker.h"
25#include "TAttLine.h"
26#include "TAttFill.h"
27#include "TAttText.h"
28
29#include <QDialog>
30#include <QVBoxLayout>
31#include <QHBoxLayout>
32#include <QFormLayout>
33#include <QComboBox>
34#include <QSpinBox>
35#include <QPushButton>
36#include <QLabel>
37#include <QColorDialog>
38#include <QDoubleSpinBox>
39#include <QGroupBox>
40
41#include <memory>
42
43using namespace ROOT::Experimental;
44
45/** \class TQt6GedEditor
46 \ingroup qt6canvas
47 \brief TVirtualPadEditor ABI implementation for Qt6
48*/
49
50TQt6GedEditor::TQt6GedEditor(TCanvas *c)
51{
52 SetCanvas(c);
53}
54
59
60
62{
63 if (fCanvas == newcan) return;
64
67
68 if (!newcan) return;
69
70 // SetWindowName(Form("%s_Editor", fCanvas->GetName()));
72 if (!fPad) fPad = fCanvas;
74}
75
76
77////////////////////////////////////////////////////////////////////////////////
78/// Connect this editor to the Selected signal of canvas 'c'.
79
81{
82 c->Connect("Selected(TVirtualPad*,TObject*,Int_t)", "ROOT::Experimental::TQt6GedEditor", this,
83 "SetModel(TVirtualPad*,TObject*,Int_t)");
84}
85
86////////////////////////////////////////////////////////////////////////////////
87/// Disconnect this editor from the Selected signal of fCanvas.
88
90{
91 if (fCanvas)
92 Disconnect(fCanvas, "Selected(TVirtualPad*,TObject*,Int_t)", this, "SetModel(TVirtualPad*,TObject*,Int_t)");
93}
94
95////////////////////////////////////////////////////////////////////////////////
96/// Activate object editors according to the selected object.
97
99{
100 if (event != kButton1Down)
101 return;
102
103 auto prev = fModel;
104
105 fPad = pad;
106 fModel = obj ? obj : pad;
107
108 if (fModel != prev)
110}
111
113{
114 if (gPad)
115 SetCanvas(gPad->GetCanvas());
116
117 if (fCanvas && fGlobal)
119
120 if (!gROOT->GetListOfCleanups()->FindObject(this))
121 gROOT->GetListOfCleanups()->Add(this);
122
123
124 fDialog = new QDialog;
125 fDialog->setWindowTitle("Edit Attributes");
126 fDialog->setModal(false);
127
128 auto imp = dynamic_cast<TQt6Canvas *>(fCanvas->GetCanvasImp());
129 if (imp) {
130 auto widget = imp->GetCanvasWidget();
131 fDialog->resize(200, widget->height());
132 QPoint pos = widget->mapToGlobal(QPoint(0, 0));
133 fDialog->move(pos.x() - fDialog->width(), pos.y());
134 }
135
136 auto mainLayout = new QVBoxLayout(fDialog);
137 fFormLayout = new QFormLayout();
138
139 mainLayout->addLayout(fFormLayout);
140
141
142 // --- Dialog Buttons (OK / Cancel) ---
144 QPushButton *okButton = new QPushButton("OK");
145 QPushButton *cancelButton = new QPushButton("Cancel");
146 buttonLayout->addStretch();
147 buttonLayout->addWidget(okButton);
148 buttonLayout->addWidget(cancelButton);
149 mainLayout->addLayout(buttonLayout);
150
151 QObject::connect(okButton, &QPushButton::clicked, fDialog, &QDialog::accept);
152 QObject::connect(cancelButton, &QPushButton::clicked, fDialog, &QDialog::reject);
153
154 fDialog->setAttribute(Qt::WA_DeleteOnClose);
155
156 QObject::connect(fDialog, &QDialog::finished, [this]([[maybe_unused]] int result) {
157 fDialog = nullptr;
158 fFormLayout = nullptr;
159 });
160
161 if (fModel)
163
164 fDialog->show();
165}
166
168{
171 layout->setContentsMargins(0, 5, 0, 5);
172
174 leftLine->setFrameShape(QFrame::HLine);
175
176 QLabel *label = new QLabel(lbl, fDialog);
177
179 rightLine->setFrameShape(QFrame::HLine);
180
181 layout->addWidget(leftLine, 1); // Stretch factor 1
182 layout->addWidget(label, 0); // Fits content tightly
183 layout->addWidget(rightLine, 4); // Stret
184
185 f->addRow(container);
186}
187
188void TQt6GedEditor::AddColorElements(int colindx, QFormLayout *layout, std::function<void(int)> callback)
189{
190 TColor *rootColor = gROOT->GetColor(colindx);
191 QColor initialColor = Qt::black;
192 int initialAlpha255 = 255; // Default fully opaque
193
194 if (rootColor) {
195 initialColor = QColor(rootColor->GetRed() * 255, rootColor->GetGreen() * 255, rootColor->GetBlue() * 255);
196 initialAlpha255 = static_cast<int>(rootColor->GetAlpha() * 255);
197 }
199
200 auto colorButton = new QPushButton(fDialog);
201 colorButton->setFixedWidth(80);
202
203 QSlider *alphaSlider = new QSlider(Qt::Horizontal);
204 alphaSlider->setRange(0, 255);
205 alphaSlider->setValue(initialAlpha255);
206
207 // instance will be deleted when last lambda is removed
208 auto selectedColor = std::make_shared<QColor>(initialColor);
209
210 auto updateColorElements = [colorButton, selectedColor, callback]() {
211 QString qss = QString("background-color: rgba(%1, %2, %3, %4); border: 1px solid gray;")
212 .arg(selectedColor->red())
213 .arg(selectedColor->green())
214 .arg(selectedColor->blue())
215 .arg(selectedColor->alpha() / 255.0);
216 colorButton->setStyleSheet(qss);
218 selectedColor->green(),
219 selectedColor->blue(),
220 selectedColor->alpha() / 255.0);
221 callback(newColorIdx);
222 };
223
225
226 QObject::connect(colorButton, &QPushButton::clicked, [selectedColor, updateColorElements]() {
227 QColor col = QColorDialog::getColor(*selectedColor, nullptr, "Select Color");
228 if (col.isValid()) {
229 selectedColor->setRed(col.red());
230 selectedColor->setGreen(col.green());
231 selectedColor->setBlue(col.blue());
233 }
234 });
235
236 // --- Slider Shift Connection ---
237 QObject::connect(alphaSlider, &QSlider::valueChanged, [selectedColor, updateColorElements](int value) {
238 selectedColor->setAlpha(value);
240 });
241
242 layout->addRow("Color:", colorButton);
243
244 layout->addRow("Opacity:", alphaSlider);
245}
246
248{
249 auto pad = fPad;
250 if (!pad)
251 pad = fCanvas;
252 if (pad)
253 pad->ModifiedUpdate();
254}
255
257{
258 AddHLine(fFormLayout, "TAttLine");
259
260 AddColorElements(attline->GetLineColor(), fFormLayout, [attline, this](int colindx) {
261 attline->SetLineColor(colindx);
262 ModifiedPad();
263 });
264
266 styleCombo->addItem("None (0)", 0);
267 styleCombo->addItem("Solid (1)", 1);
268 styleCombo->addItem("Dashed (2)", 2);
269 styleCombo->addItem("Dotted (3)", 3);
270 styleCombo->addItem("Dash-Dot (4)", 4);
271 styleCombo->addItem("Dash-Dot (5)", 5);
272 styleCombo->addItem("Dash-Dot-Dot-Dot (6)", 6);
273 styleCombo->addItem("Dashed medium (7)", 7);
274 styleCombo->addItem("Dash-Dot-Dot (8)", 8);
275 styleCombo->addItem("Dashed long (9)", 9);
276 styleCombo->addItem("Dash-Dot long (10)", 10);
277
278 // Find and set current style
279 int currentStyle = attline->GetLineStyle();
280 int styleIdx = styleCombo->findData(currentStyle);
281 if (styleIdx != -1)
282 styleCombo->setCurrentIndex(styleIdx);
283 else
284 styleCombo->addItem(QString("Custom (%1)").arg(currentStyle), currentStyle);
285
286 QObject::connect(styleCombo, &QComboBox::currentIndexChanged, [this, attline, styleCombo](int) {
287 attline->SetLineStyle(styleCombo->currentData().toInt());
288 ModifiedPad();
289 });
290
291 fFormLayout->addRow("Style:", styleCombo);
292
293 QSpinBox *widthSpin = new QSpinBox();
294 widthSpin->setRange(1, 20);
295 widthSpin->setValue(attline->GetLineWidth());
296
297 QObject::connect(widthSpin, &QSpinBox::valueChanged, [this, attline](int v) {
298 attline->SetLineWidth(v);
299 ModifiedPad();
300 });
301
302 fFormLayout->addRow("Width:", widthSpin);
303}
304
306{
307 AddHLine(fFormLayout, "TAttFill");
308
309 AddColorElements(attfill->GetFillColor(), fFormLayout, [attfill, this](int colindx) {
310 attfill->SetFillColor(colindx);
311 ModifiedPad();
312 });
313
315 styleCombo->addItem("None (0)", 0);
316 styleCombo->addItem("Solid (1001)", 1001);
317 for (int s = 3001; s <= 3025; ++s)
318 styleCombo->addItem(QString("Style %1").arg(s), s);
319 for (int s = 3144; s <= 3944; s += 100)
320 styleCombo->addItem(QString("Style %1").arg(s), s);
321 for (int s = 3305; s <= 3395; s += 10)
322 styleCombo->addItem(QString("Style %1").arg(s), s);
323 for (int s = 3350; s <= 3359; s += 1)
324 styleCombo->addItem(QString("Style %1").arg(s), s);
325 for (int s = 3409; s <= 3490; s += 9)
326 styleCombo->addItem(QString("Style %1").arg(s), s);
327 for (int s = 3609; s <= 3690; s += 9)
328 styleCombo->addItem(QString("Style %1").arg(s), s);
329
330 // Find and set current style
331 int currentStyle = attfill->GetFillStyle();
332 int styleIdx = styleCombo->findData(currentStyle);
333 if (styleIdx != -1)
334 styleCombo->setCurrentIndex(styleIdx);
335 else
336 styleCombo->addItem(QString("Style %1").arg(currentStyle), currentStyle);
337
338 QObject::connect(styleCombo, &QComboBox::currentIndexChanged, [this, attfill, styleCombo](int) {
339 attfill->SetFillStyle(styleCombo->currentData().toInt());
340 ModifiedPad();
341 });
342
343 fFormLayout->addRow("Style:", styleCombo);
344}
345
346
348protected:
349 QString textFromValue(double value) const override {
350 if (value == 0) return "Default";
351 return QDoubleSpinBox::textFromValue(value);
352 }
353
354 double valueFromText(const QString &text) const override {
355 if (text == "Default") return 0.;
356 return QDoubleSpinBox::valueFromText(text);
357 }
358};
359
360class CustomSpinBox : public QSpinBox {
361protected:
362 QString textFromValue(int value) const override {
363 if (value == 0) return "Default";
364 return QSpinBox::textFromValue(value);
365 }
366
367 int valueFromText(const QString &text) const override {
368 if (text == "Default") return 0;
369 return QSpinBox::valueFromText(text);
370 }
371};
372
373
375{
376 AddHLine(fFormLayout, "TAttText");
377
378 AddColorElements(atttext->GetTextColor(), fFormLayout, [atttext, this](int colindx) {
379 atttext->SetTextColor(colindx);
380 ModifiedPad();
381 });
382
384 fontCombo->addItem("1. Times italic", 1);
385 fontCombo->addItem("2. Times bold", 2);
386 fontCombo->addItem("3. Times bold italic", 3);
387 fontCombo->addItem("4. Helvetica", 4);
388 fontCombo->addItem("5. Helvetica italic", 5);
389 fontCombo->addItem("6. Helvetica bold", 6);
390 fontCombo->addItem("7. Helvetica bold italic", 7);
391 fontCombo->addItem("8. Courier", 8);
392 fontCombo->addItem("9. Courier italic", 9);
393 fontCombo->addItem("10. Courier bold", 10);
394 fontCombo->addItem("11. Courier bold italic", 11);
395 fontCombo->addItem("12. Symbol", 12);
396 fontCombo->addItem("13. Times", 13);
397 fontCombo->addItem("14. Wingdings", 14);
398 fontCombo->addItem("15. Symbol italic", 15);
399
400 // Find and set current style
401 int currentPrec = atttext->GetTextFont() % 10;
402 int currentFont = atttext->GetTextFont() / 10;
403 int styleIdx = fontCombo->findData(currentFont);
404 if (styleIdx >= 0)
405 fontCombo->setCurrentIndex(styleIdx);
406 else
407 fontCombo->addItem(QString("Font %1").arg(currentFont), currentFont);
408
409 QObject::connect(fontCombo, &QComboBox::currentIndexChanged, [this, atttext, fontCombo, currentPrec](int) {
410 atttext->SetTextFont(fontCombo->currentData().toInt() * 10 + currentPrec);
411 ModifiedPad();
412 });
413
414 fFormLayout->addRow("Font:", fontCombo);
415
416 // QDoubleSpinBox* floatSpinBox = nullptr;
417 //QSpinBox *intSpinBox = nullptr;
418
419 if (currentPrec == 2) {
421 floatSpinBox->setRange(0.0, 1.0); // Set your minimum and maximum limits
422 floatSpinBox->setSingleStep(0.01); // Set step size to 00.1
423 floatSpinBox->setDecimals(3); // Force it to show exactly 3 decimal places
424 floatSpinBox->setValue(atttext->GetTextSize());
425
426 QObject::connect(floatSpinBox, &QDoubleSpinBox::valueChanged, [this, atttext](double v) {
427 atttext->SetTextSize(v);
428 ModifiedPad();
429 });
430 fFormLayout->addRow("Size:", floatSpinBox);
431
432 } else {
433 auto intSpinBox = new CustomSpinBox();
434 intSpinBox->setRange(0, 128);
435 intSpinBox->setValue(atttext->GetTextSize());
436
437 QObject::connect(intSpinBox, &QSpinBox::valueChanged, [this, atttext](int v) {
438 atttext->SetTextSize(v);
439 ModifiedPad();
440 });
441
442 fFormLayout->addRow("Size:", intSpinBox);
443 }
444
446 alignCombo->addItem("11. Left Bottom", 11);
447 alignCombo->addItem("12. Left Center", 12);
448 alignCombo->addItem("13. Left Top", 13);
449 alignCombo->addItem("21. Middle Bottom", 21);
450 alignCombo->addItem("22. Middle Center", 22);
451 alignCombo->addItem("23. Middle Top", 23);
452 alignCombo->addItem("31. Right Bottom", 31);
453 alignCombo->addItem("32. Right Center", 32);
454 alignCombo->addItem("33. Right Top", 33);
455
456 // Find and set current style
457 int alignIdx = alignCombo->findData(atttext->GetTextAlign());
458 if (alignIdx < 0)
459 alignIdx = alignCombo->findData(11);
460 alignCombo->setCurrentIndex(alignIdx);
461
462 QObject::connect(alignCombo, &QComboBox::currentIndexChanged, [this, atttext, alignCombo](int) {
463 atttext->SetTextAlign(alignCombo->currentData().toInt());
464 ModifiedPad();
465 });
466
467 fFormLayout->addRow("Align:", alignCombo);
468}
469
470
471
473{
474 AddHLine(fFormLayout, "TAttMarker");
475
476 AddColorElements(attmarker->GetMarkerColor(), fFormLayout, [attmarker, this](int colindx) {
477 attmarker->SetMarkerColor(colindx);
478 ModifiedPad();
479 });
480
482 for (int s = 1; s <= 49; ++s)
483 styleCombo->addItem(QString("Style %1").arg(s), s);
484
485 // Find and set current style
486 int currentStyle = attmarker->GetMarkerStyle();
487 int styleIdx = styleCombo->findData(currentStyle);
488 if (styleIdx != -1)
489 styleCombo->setCurrentIndex(styleIdx);
490 else
491 styleCombo->addItem(QString("Style %1").arg(currentStyle), currentStyle);
492
493 QObject::connect(styleCombo, &QComboBox::currentIndexChanged, [this, attmarker, styleCombo](int) {
494 attmarker->SetMarkerStyle(styleCombo->currentData().toInt());
495 ModifiedPad();
496 });
497
498 fFormLayout->addRow("Style:", styleCombo);
499
501 floatSpinBox->setRange(0.0, 100.0); // Set your minimum and maximum limits
502 floatSpinBox->setSingleStep(1); // Set step size to 1
503 floatSpinBox->setDecimals(1); // Force it to show exactly 1 decimal place (e.g., 1.5)
504 floatSpinBox->setValue(attmarker->GetMarkerSize());
505 fFormLayout->addRow("Size:", floatSpinBox);
506
507 QObject::connect(floatSpinBox, &QDoubleSpinBox::valueChanged, [this, attmarker](double v) {
508 attmarker->SetMarkerSize(v);
509 ModifiedPad();
510 });
511}
512
514{
515 TString method_name = TString::Format("Add%s", cl->GetName());
516
517 auto mtd = IsA()->GetMethodAny(method_name.Data());
518
519 if (mtd) {
520 void *obj = fModel->IsA()->DynamicCast(cl, fModel);
521 auto sarg = TString::Format("(%s*)0x%zx", cl->GetName(), (size_t)obj);
522 TMethodCall call(IsA(), method_name, sarg.Data());
523 call.Execute(this);
524 }
525
526 auto lst = cl->GetListOfBases();
527
528 TIter iter(lst);
529 while (auto base = (TBaseClass*) iter())
530 FillGed(base->GetClassPointer());
531}
532
533
535{
536 if (!fFormLayout || !fModel)
537 return;
538
539 // first delete all elements
540 while (fFormLayout->count() > 0) {
541 // Always take from index 0 or use a reverse loop
542 auto item = fFormLayout->takeAt(0);
543
544 if (QWidget *widget = item->widget())
545 widget->deleteLater(); // Safely deletes the widget
546
547 delete item; // Deletes the layout item wrapper
548 }
549
550 FillGed(fModel->IsA());
551
552// auto attmarker = dynamic_cast<TAttMarker *>(fModel);
553// if (attmarker)
554// AddTAttMarker(attmarker);
555}
556
558{
559 gROOT->GetListOfCleanups()->Remove(this);
560 if (fDialog) {
561 fDialog->close();
562 fDialog = nullptr;
563 fFormLayout = nullptr;
564 }
565}
566
568{
569 if (obj == fModel) {
571 } else if (obj == fPad) {
573 } else if (obj == fCanvas)
574 Hide();
575}
@ kButton1Down
Definition Buttons.h:17
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
cudaEvent_t event
short Color_t
Color number (short)
Definition RtypesCore.h:100
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char text
#define gROOT
Definition TROOT.h:417
#define gPad
QString textFromValue(double value) const override
double valueFromText(const QString &text) const override
QString textFromValue(int value) const override
int valueFromText(const QString &text) const override
void ConnectToCanvas(TCanvas *c)
Connect this editor to the Selected signal of canvas 'c'.
void DisconnectFromCanvas()
Disconnect this editor from the Selected signal of fCanvas.
TClass * IsA() const override
void AddHLine(QFormLayout *f, const char *lbl)
void AddColorElements(int colindx, QFormLayout *layout, std::function< void(int)> callback)
void RecursiveRemove(TObject *obj) override
Recursively remove this object from a list.
virtual void SetModel(TVirtualPad *pad, TObject *obj, Int_t event)
Activate object editors according to the selected object.
Fill Area Attributes class.
Definition TAttFill.h:21
Line Attributes class.
Definition TAttLine.h:21
Marker Attributes class.
Definition TAttMarker.h:22
Text Attributes class.
Definition TAttText.h:21
Each class (see TClass) has a linked list of its base class(es).
Definition TBaseClass.h:33
The Canvas class.
Definition TCanvas.h:23
TObject * GetClickSelected() const
Definition TCanvas.h:146
TVirtualPad * GetClickSelectedPad() const
Definition TCanvas.h:151
TCanvasImp * GetCanvasImp() const override
Definition TCanvas.h:162
TVirtualPad * GetSelectedPad() const override
Definition TCanvas.h:150
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3699
TMethod * GetMethodAny(const char *method)
Return pointer to method without looking at parameters.
Definition TClass.cxx:4495
The color creation and management class.
Definition TColor.h:22
static Int_t GetColor(const char *hexcolor)
Static method returning color number for color specified by hex color string of form: "#rrggbb",...
Definition TColor.cxx:1939
Method or function calling interface.
Definition TMethodCall.h:37
void Execute(const char *, const char *, int *=nullptr) override
Execute method on this object with the given parameter string, e.g.
Definition TMethodCall.h:64
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
virtual TClass * IsA() const
Definition TObject.h:248
Bool_t Disconnect(const char *signal=nullptr, void *receiver=nullptr, const char *slot=nullptr)
Disconnects signal of this object from slot of receiver.
Basic string class.
Definition TString.h:137
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2460
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
Namespace for ROOT features in testing.
Definition TROOT.h:100