GUI - Use external function in a widget


ROOT Version: 6.18/04
Platform: Ubuntu 18.04
Compiler: gcc 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)


Dear co-rooters,

I’m building a GUI that will unpack some signals and then draw them. What I would like to do is connect a widget (i.e. a button) with external functions.

I have divided my code to practically 3 parts: gui (.cxx, .h) in which a class MainFrame is defined, unpacker (.cxx, .h) which will unpack the signals and main (.cxx, .h) which will execute any functions.

I compiled everything and I haven’t encountered any issue either in the generation of the dictionary

rootcling -f exDict.cxx -c -p main.h gui.h unpacker.h exLinkDef.h

or in the compilation using

g++ -o SignalViewer main.cxx gui.cxx unpacker.cxx exDict.cxx `root-config --cflags --glibs`

However when I run the executable I get the following error

Error in TQObject::CheckConnectArgs: slot MainFrame::test_unpack() does not exist

This is because the gui cannot see any external function, but I’m not sure how to solve it. Any ideas would be more than welcome!

Thanks in advance.

I uploaded a .zip file with the code but I am also pasting it here.
My code is (I tried to make as simple as possible)

main.cxx

//File includes
#include "main.h"
//#include "../include/gui.h"
//#include "exDict.h"

using namespace std;

int main(int argc, char *argv[]){

	// Initialise
	init_unpack();

	TApplication theApp("App",&argc,argv);
	start_gui();
	theApp.Run();
	return 0;

}

main.h

#ifndef MAIN_H
#define MAIN_H

#include "gui.h"
#include "unpacker.h"

#endif

unpacker.cxx


#include <iostream>
#include "unpacker.h"

using namespace std;

int init_unpack(){
cout << "***** Initialising unpacking *****" << endl;
return 0;
}

int test_unpack(){

std::cout << "***** Executing test_unpack() *****" << std::endl;
return 0;

}

unpacker.h

#ifndef UNPACKER_H
#define UNPACKER_H

using namespace std;

int init_unpack();
int test_unpack();

#endif

gui.cxx


#include "gui.h"
#include "unpacker.h"
//#include "unpacker.cxx"

MainFrame::MainFrame(const TGWindow *p, UInt_t width, UInt_t height):TGMainFrame(p, width, height, kMainFrame|kHorizontalFrame)
{

   //---- Left Frame - for buttons and user values
   fr_left  = new TGVerticalFrame(this, 0.3*width, height, kChildFrame|kFixedSize); 
   
   // The 1st grouped frame - runs
   fr_runs = new TGGroupFrame(fr_left, "Run", kChildFrame | kVerticalFrame|kFixedSize);   
   box_run = new TGNumberEntry(fr_runs, (Double_t) 0, 10, -1, (TGNumberFormat::EStyle) 5);  
   fr_runs->AddFrame(box_run, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 2, 2, 2, 2) );
   fr_left->AddFrame(fr_runs, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
   
   // The 2nd grouped frame - Drawing the signal
   fr_draw = new TGGroupFrame(fr_left, "Draw", kChildFrame | kHorizontalFrame|kFixedSize);   
   TGTextButton *but_draw_histo = new TGTextButton(fr_draw, "&Draw");
   but_draw_histo->Connect("Clicked()", "MainFrame", this, "test_unpack()");   
   fr_draw->AddFrame(but_draw_histo, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 2, 2, 2, 2) );
   fr_left->AddFrame(fr_draw, new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
  
   // Test button to see the structure
   TGTextButton *test = new TGTextButton(fr_left,"&Test");
   test->Connect("Clicked()", "MainFrame", this, "draw_test()");
   fr_left->AddFrame(test, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY, 10, 10, 10, 10) );
   
   // Exit button to exit the application
   TGTextButton *exit = new TGTextButton(fr_left, "&Exit", "gApplication->Terminate(0)");
   fr_left->AddFrame(exit, new TGLayoutHints(kLHintsCenterX, 5, 5, 3, 4) );                                        
   AddFrame(fr_left , new TGLayoutHints(kLHintsLeft | kLHintsTop, 10,10,10,10));
  
   
   //---- Right frame - for the canvas
   TGVerticalFrame *fr_right = new TGVerticalFrame(this, 0.7*width, height, kChildFrame);   
   emb_canvas = new TRootEmbeddedCanvas("canvas", fr_right, 130, 180);
   fr_right->AddFrame(emb_canvas, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 10,10,10,10));   
   AddFrame(fr_right , new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 10,10,10,10));

    // Set a name to the main frame
   SetWindowName("Signal viewer");
   
   // Map all subwindows of main frame
   MapSubwindows();
   Layout();
   
   // Initialize the layout algorithm
   Resize(GetDefaultSize());
   
   // Map main frame
   MapWindow();

}

////////////////////////////////////////////////////////////////////////////////
/// Handles resize events for the mainframe.

Bool_t MainFrame::HandleConfigureNotify(Event_t *event)
{
    // set the size minus the margins set in the LayoutManager:
   // AddFrame(fr_left , new TGLayoutHints(kLHintsLeft | kLHintsTop, 10,10,10,10));
   UInt_t w = event->fWidth-20;
   UInt_t h = event->fHeight-20;
   
   fr_runs->Resize(0.25*w, 0.15*h);
   fr_runs->Layout();
   
   fr_draw->Resize(0.25*w, 0.15*h);
   fr_draw->Layout();
   
   fr_left->Resize(0.25*w, h);
   fr_left->Layout();
   
   return TGMainFrame::HandleConfigureNotify(event);
}

void MainFrame::draw_test() {
   // Draws function graphics in randomly chosen interval
   TF1 *f1 = new TF1("f1","sin(x)/x",0,gRandom->Rndm()*10);
   f1->SetLineWidth(3);
   f1->Draw();
   TCanvas *fCanvas = emb_canvas->GetCanvas();
   fCanvas->cd();
   fCanvas->Update();
}

MainFrame::~MainFrame() {
   // Clean up used widgets: frames, buttons, layout hints
   Cleanup();
}

void start_gui(){
   // Popup the gui
   std::cout << "Starting the gui" << std::endl;
   new MainFrame(gClient->GetRoot(), 800, 500);
}

//int test_unpack();

gui.h


#ifndef ___GUI_H
#define ___GUI_H

//ROOT Includes
#include "TFile.h"
#include <TGClient.h>
#include <TCanvas.h>
#include <TF1.h>
#include "TH1F.h"
#include "TPaveText.h"
#include <TRandom.h>
#include <TGButton.h>
#include <TGFrame.h>
#include <TGNumberEntry.h>
#include <TRootEmbeddedCanvas.h>
#include <TApplication.h>
#include <TGClient.h>
#include <RQ_OBJECT.h>
#include "unpacker.h"
//#include "exLinkDef.h"

using namespace std;

class MainFrame : public TGMainFrame {
private:
   TRootEmbeddedCanvas *emb_canvas;
   TGVerticalFrame *fr_left;
   TGGroupFrame *fr_runs, *fr_draw;
   TGNumberEntry *box_run;
public:
   MainFrame(const TGWindow *p, UInt_t width, UInt_t height);
   Bool_t HandleConfigureNotify(Event_t *);
   virtual ~MainFrame();
   void draw_test();

ClassDef (MainFrame,0);// Remove for ROOT6 and rootcling
};

void start_gui();

//int test_unpack();

#endif

signal_viewer_forum.zip (4.8 KB)

I’ll try, but you need at least to create a dictionary for the test_unpack() function:

#pragma link C++ function test_unpack();

and connect to a free function (test_unpack() is not a method of MainFrame):

   but_draw_histo->Connect("Clicked()", 0, 0, "test_unpack()");   

So only managed to make it working by creating a class to be able to use use signal/slot in this case. For example:

class Unpacker
{
public:
   int init_unpack();
   int test_unpack();
};

And in exLinkDef.h, add:

#pragma link C++ class Unpacker+;

And then in gui.cxx:

   but_draw_histo->Connect("Clicked()", "Unpacker", gUnpacker, "test_unpack()");   

But I’m pretty sure using free functions should work. Maybe @pcanal or @Axel know if it should work. Otherwise I might open a bug report…

There’s no need for function dictionaries in ROOT 6 anymore!

I bet the issue is that cling doesn’t know about the free function. Is the library loaded, is the header included into cling? For the member function it’s easy: MainFrame::test_unpack() will trigger cling to look at MainFrame, it finds the dictionary and automatically #includes the header. For a free function that’s not the case.

HTH!

Axel.

Thanks @Axel, but there is no MainFrame::test_unpack(), only test_unpack() and it is all in the same executable, there is no library involved.
E.g. Using a free function int test_unpack() gives:

Error in <TQObject::CheckConnectArgs>: slot test_unpack() does not exist

But using a class method int Unpacker::test_unpack() works…

So here is the solution (thanks @Axel):

  • No need to add #pragma link C++ function test_unpack(); in your exLinkDef.h file.
  • Calling rootcling -f exDict.cxx gui.h exLinkDef.h is sufficient
  • Add gInterpreter->Declare("int test_unpack();"); for example in main.cxx, or in the MainFrame constructor, before calling but_draw_histo->Connect
  • Use but_draw_histo->Connect("Clicked()", 0, 0, "test_unpack()");

The trick is to declare the slot function (test_unpack()) in the in the interpreter with gInterpreter->Declare("int test_unpack();");. That’s it!

Thanks a lot for your help!
I’ve been trying to see why your solution didn’t work, but I was accidentally on a system with ROOT5!
Your solution works great in ROOT6 and in fact it’s so intuitive!
Thanks a lot.

*Is there a way to make it work with ROOT5? Just curious, because I spend sometime there and I have the bug now!

I don’t know, I’ll try and let you know

Thanks a lot!
I’ve went through the TCint and TInterpreter but, no luck!

I think it’s what you first suggested!
Creating a separate dictionary works.

#pragma link C++ function test_unpack()+;

Oh, OK, sorry I thought you tried it and it didn’t work…

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.