Can I add buttons on a TGraph?

Dear experts

I wonder if one can add buttons on a TGraph, to be more specific, I have a event display of drift chamber like this:

And dots represents the hits (wire positions) in the chamber. I want to make all hits (red dots) to buttons that when I click that button the waveform of that channel will be showed in a new window.

Any suggestion will be helpful! Even just telling me “It’s impossible”

Hi Siyuan,

Very interesting use case!

I do not think it’s something ROOT supports out of the box. I am adding our experts in the loop @linev @bellenot .


Hi @Crisps,
maybe this part on the TGraphPainter can be useful.
And maybe you could have a look also a these tutorials
Tutorial 1

Thank you for the reply! Which part you mean “this part”? maybe you forgot to add link on it…?

Sorry I meant to put this link

I have read through these two tutorials, and I got what you mean “this part”, now I have one question related to the function in TCanvas::HighlightConnect() : UserFunction(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y)
In my understanding this function will be called when the mouse is pointing to the ith point on the TGraph (or histogram). But what I need is:
I have a map of TGraphs which is channel to waveform (not all channels have waveform), so I use TGraph::SetPoint(channel, x, y) to set the point on the TGraph. The problem is the point that I didn’t set (let’s say I set 2,4,6th point) will be created automatically and result on the TGraph, is there a way to disable auto creation?

I solved the problem above by moving unused points out of window (99999, 99999). If you have better (or decent) solution please teach me!

Now I got another question:
I want to make the HighlightGraph(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y) as a member function in the same class, but when I connected it to canvas:

c->HighlightConnect("HighlightGraph(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y)");

it is never called at runtime even I can see the circle highlight on the TGraph.

then I was thinking maybe class member function is different from function pointer, so I made it a function pointer:

std::function<void(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y)> func = [this](TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y){
                this->HighlightGraph(pad, obj, ihp, y);
c->HighlightConnect("func(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y)");

but it still didn’t work…any help?

And I also tried this:

c->Connect("Highlighted(TVirtualPad*, TObject*, Int_t, Int_t)", "EventDisplay", this, "HighlightGraph(TVirtualPad*, TObject*, Int_t, Int_t)");

but didn’t work :frowning:


TCanvas::HighlightConnect() method is simplified version which only allows to connect global functions, but not objects methods. See docu. You can use TQObject::Connect():

   auto receiver = new ReceiverClass("receiver");

   auto Canvas = new TCanvas("Canvas", "Canvas", 0, 0, 700, 500);

                   "ReceiverClass", receiver, "HighlightHisto(TVirtualPad*,TObject*,Int_t,Int_t)");

I attach modified version of hlGraph1.C tutorial macro:

hlGraph1.C (1.8 KB)

Do you have dictionary for your class?

Thank you for the reply, I have tried this method (my last post) but it didn’t work and give this:

Error in <TQObject::CheckConnectArgs>: slot HighlightGraph(TVirtualPad*,TObject*,int,int) does not exist

But as you pointed my class EventDisplay is no derived from ROOT classes, so there is no dictionary of it. Should I derive it from TNamed and make a dictionary?


When code runs as macro ROOT automatically creates dictionary for all used classes.
If you build and run your application differently - you need to create dictionary for your EventDisplay class. It is not necessary to derive it from TObject.


I tried to add this line in my code:

gInterpreter->GenerateDictionary("EventDisplay", "EventDisplay.hxx");

but got this:
fatal error: ‘EventDisplay.hxx’ file not found

#include “EventDisplay.hxx”

then I modified to:

gInterpreter->GenerateDictionary("EventDisplay", "/Users/siyuan/Physics/comet/crt/tracking/src/EventDisplay.hxx");

but still got the same error…

No, you need to add ROOT_GENERATE_DICTIONARY to your CMakeLists.txt file.

See it in ROOT examples: root/test/periodic/CMakeLists.txt at master · root-project/root · GitHub

Thank you, in that CMakeLists.txt, what is NdbLinkDef.h?
should I write #pragma link C++ class EventDisplay+; in that header file?

Yes, you need to create new LinkDef.h file with minimal content:

#ifdef __CLING__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class EventDisplay+;


You can include your other classes - if it is required.

I tried to make a similar version CMakeLists.txt but got totally lost…
What does these variables refer…?

file(GLOB DBsources RELATIVE ${CMAKE_SOURCE_DIR} Ndb*.cxx)
list(REMOVE_ITEM headers NdbLinkDef.h)

for example I have EventDisplay.hxx, EventDisplay.cxx and EventDisplay_LinkDef.h, which one should fill in where?

Surely the approach you are following now is possible one. However, let me propose another one. I modified an example I have (graphtext3.C) to retrieve which point has been clicked in a TGraph. Once you have it, you are then free to draw whatever you want. In this example, I redraw the graph (as I mentioned, this is an example I already had).

const int Npoints = 3;  // Number of points in the graph
const double x[Npoints] = {1.0, 2.0, 5.0};
const double y[Npoints] = {0.5, 1.0, 2.0};
const char  *c[Npoints] = {"a","b","c"};

void graphtext3() {
   TGraph *gr = new TGraph(Npoints, x, y);
   TExec *ex = new TExec("ex","DrawText();");


void DrawText()
   Int_t i,n;
   Double_t x,y;
   TLatex *t;
   TLine *l;
   double eps = 0.01;

   TGraph *g = (TGraph*)gPad->GetListOfPrimitives()->FindObject("Graph");

   // Get event information
   int event = gPad->GetEvent();
   int px    = gPad->GetEventX();
   int py    = gPad->GetEventY();

   // Get the coordinates
   double xd = gPad->AbsPixeltoX(px);
   double yd = gPad->AbsPixeltoY(py);
   float  xp = gPad->PadtoX(xd);
   float  yp = gPad->PadtoY(yd);
   n = g->GetN();
   for (i=0; i<n; i++) {
      if (TMath::Abs(x-xp)<eps && TMath::Abs(y-yp)<eps) printf("Point %d has been clicked\n",i);

   double ymin = g->GetHistogram()->GetMinimum();
   double ymax = g->GetHistogram()->GetMaximum();
   double dy = (ymax-ymin);
   for (i=0; i<n; i++) {
      t = new TLatex(x, ymin-0.03*dy, Form("%s (%4.2f,%4.2f)",c[i],x,y));
      l = new TLine(x,ymin,x,ymin+0.03*dy);

Thank you @couet and @linev, I tried to make dictionary by adding this command in my CMakeLists.txt:


and then

add_library(lib other_cxx_files ${CMAKE_CURRENT_SOURCE_DIR}/build/G__EventDisplay.cxx)

it compiled successfully but I got runtime error:

Error in <TQObject::CheckConnectArgs>: slot HighlightGraph(TVirtualPad*,TObject*,int,int) does not exist

where I confirmed many time that I have no typo of this function name or parameter.
I also attach part of my EventDisplay.cxx:

void EventDisplay::HighlightGraph(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y){
        c_waveform = (TCanvas*)gROOT->GetListOfCanvases()->FindObject("c_waveform");
                std::cout<<"Make new Canvas for ADC waveform"<<std::endl;
                c_waveform = new TCanvas("c_waveform", "ADC waveform", 505, 0, 600, 400);

        if(ihp == -1){
                delete c_waveform;

        if(waveforms.size() != 0 && waveforms.find(ihp) != waveforms.end()){

void EventDisplay::DrawHits(CDCHitContainer* hits, int event){
        TCanvas* c = new TCanvas();
//      c->HighlightConnect("HighlightGraph(TVirtualPad* pad, TObject* obj, Int_t ihp, Int_t y)");
        c->Connect("Highlighted(TVirtualPad*, TObject*, Int_t, Int_t)", "EventDisplay", this, "HighlightGraph(TVirtualPad*, TObject*, Int_t, Int_t)");

It would be easy if you can provide access to your source code.

May be method is not public?
Or cmake file has other problems

I finally solved it and now it runs fine…Thank you all so much!!!

It’s not fatal but I got seg fault when the Highlight function is called:

 *** Break *** segmentation violation
[/usr/lib/system/libsystem_platform.dylib] _sigtramp (no debug info)
[/Users/siyuan/Physics/root/install/lib/] THistPainter::DistancetoPrimitive(int, int) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TPad::Pick(int, int, TObjLink*&) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TPad::Pick(int, int, TObjLink*&) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TCanvas::Pick(int, int, TObject*) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TCanvas::HandleInput(EEventType, int, int) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TRootContainer::HandleMotion(Event_t*) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TGFrame::HandleEvent(Event_t*) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TGClient::HandleEvent(Event_t*) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TGClient::ProcessOneEvent() (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TGInputHandler::Notify() (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TMacOSXSystem::DispatchOneEvent(bool) (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TSystem::InnerLoop() (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TSystem::Run() (no debug info)
[/Users/siyuan/Physics/root/install/lib/] TApplication::Run(bool) (no debug info)
[/Users/siyuan/Physics/comet/crt/tracking/build/CRTTracking] main (no debug info)
[/usr/lib/dyld] start (no debug info)

but it still works when I moving my mouse to next point, do you know why this seg fault happens…?