Root cern GUI & TThread: send data to another thread

Dear professionals,

please help me with this problem.
I write GUI for online monitor.
I want to have two threads: one is responsible for GUI and another for data acquisition.
I have analogous program in Qt and it works perfectly, but my new program should be in root cern and I have some difficulties.

It is my simple compiled example which reproduce the problem:
main.cpp

#include <TApplication.h>
#include <TGClient.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TRandom.h>
#include <TGButton.h>
#include <TRootEmbeddedCanvas.h>

//my
#include "mymainframe.h"

int main(int argc, char **argv)
{
    printf("You are in master Thread \n");
    TApplication theApp("App",&argc,argv);

    Worker *worker = new Worker();
    MyMainFrame *my_mframe = new MyMainFrame();

    worker->Connect("SendValue(Long_t)","MyMainFrame",my_mframe,"Draw_Graphs(Long_t)");

    theApp.Run();
    return 0;
}

mymainframe.h

#ifndef MYMAINFRAME_H
#define MYMAINFRAME_H

#include "worker.h"

class MyMainFrame
{
    RQ_OBJECT("MyMainFrame")
public:
    MyMainFrame();
    void Draw_Graphs(Long_t cnt);
private:
    Long_t counter;
};

#endif // MYMAINFRAME_H

worker.h

#ifndef WORKER_H
#define WORKER_H

#include "TThread.h"
#include <Riostream.h>

//root cern GUI
#include <TGClient.h>
#include <TGButton.h>
#include <TRootEmbeddedCanvas.h>
#include "TGFrame.h"
#include "TGTab.h"
#include <TQObject.h>
#include <RQ_OBJECT.h>

class Worker
{
   RQ_OBJECT("Worker")
private:
   static std::vector<Int_t> fValue_vec;
   static Long_t counter;

   static TThread *h1;
public:
   Worker();
   void  SendValue(Long_t); // *SIGNAL*

   static void *handle(void *ptr);
};

#endif // WORKER_H

linkdef.h

#pragma link C++ class Worker;
#pragma link C++ class MyMainFrame;

mymainframe.cpp

#include "mymainframe.h"

MyMainFrame::MyMainFrame()
{

}

void MyMainFrame::Draw_Graphs(Long_t cnt)
{
    //dummy
    printf("You are in master Thread: event %d: %d, %d, %d \n", cnt);
    //printf("You are in master Thread: at event %d values are is: %d, %d, %d \n", counter, vec[0], vec[1], vec[2]);
}

worker.cpp

#include "worker.h"

std::vector<Int_t> Worker::fValue_vec(0);
Long_t Worker::counter = 0;
TThread *Worker::h1 = new TThread("h1", Worker::handle);

Worker::Worker()
{
    h1->Run();
}

void  Worker::SendValue(Long_t cnt)
{
    Emit("SendValue(Long_t)",cnt);
}

void *Worker::handle(void *ptr)
{
   vector<Int_t> vec(3);

   //Long_t counter = 0;

   while(1)
   {
      //some work to get data (read board or file)
      gSystem->Sleep(1000);//dummy
      vec[0] = counter + 10;
      vec[1] = counter + 20;
      vec[2] = counter + 30;

      //I want to send new data to master thread.
      //How?
      //
      SendValue(counter);// see the next error:
      //worker.cpp: In static member function ‘static void* Worker::handle(void*)’:
      //worker.cpp:34:24: error: cannot call member function ‘void Worker::SendValue(Long_t)’ without object SendValue(counter);

      //this code does not help too
      //Emit("SendValue(Long_t)",counter);

      printf("You are in slave Thread: at event %d values are is: %d, %d, %d \n", counter, vec[0], vec[1], vec[2]);

      counter++;


   }

   return 0;
}

To launch:

rootcint -f mainDic.cpp -c mymainframe.h worker.h linkdef.h;
g++ -o main mymainframe.cpp worker.cpp main.cpp mainDic.cpp `root-config --cflags --glibs`;
./main

So, my problem how to send data from slave thread to master thread(GUI).
As I know I can get some values when thread will be finished, but in my case, I think, it will waste too many resources to open/close threads.

In Qt I just send signal from one thread to another without any problems, because signals and slots are thread-safe.
I think, should be simple solution.

Thank you in advance.

Hi,

Here is your modified code:
worker.h:

#ifndef WORKER_H
#define WORKER_H

#include "TThread.h"
#include <Riostream.h>

//root cern GUI
#include <TGClient.h>
#include <TGButton.h>
#include <TRootEmbeddedCanvas.h>
#include "TGFrame.h"
#include "TGTab.h"
#include <TQObject.h>
#include <RQ_OBJECT.h>

class Worker
{
   RQ_OBJECT("Worker")
private:
   std::vector<Int_t> fValue_vec;
   Long_t counter;

   TThread *h1;
public:
   Worker();
   void SendValue(Long_t); // *SIGNAL*

   void *handle(void *ptr);
};

#endif // WORKER_H

And worker.cpp:

#include "worker.h"

Worker::Worker() : counter(0), h1(0)
{
   h1 = new TThread("h1", (void(*)(void *))&Worker::handle, (void*) this);
   h1->Run();
}

void  Worker::SendValue(Long_t cnt)
{
   Emit("SendValue(Long_t)",cnt);
}

void *Worker::handle(void *ptr)
{
   std::vector<Int_t> vec(3);
   while(1) {
      //some work to get data (read board or file)
      gSystem->Sleep(1000);//dummy
      vec[0] = counter + 10;
      vec[1] = counter + 20;
      vec[2] = counter + 30;
      SendValue(counter);
      printf("You are in slave Thread: at event %d values are is: %d, %d, %d \n", counter, vec[0], vec[1], vec[2]);
      counter++;
   }
   return 0;
}

But be aware that any GUI event must be handled in the main thread

Cheers, Bertrand.

1 Like

Dear Bertrand,
Thanks!
It works!!!
Was my problem in static members only?
Can I use this technique for static members (just interesting)?

Well, maybe, but then you cannot call non-static function from static ones…

Dear Bertrand,
When I try to draw canvas in void MyMainFrame::Draw_Graphs(Long_t cnt) my program stop.
I think, this happened because void MyMainFrame::Draw_Graphs(Long_t cnt) in slave thread, not master.
And this is incorrect.
I checked this using method

//to show thread_id for linux systems
#include <sys/types.h>
#include <sys/syscall.h>

//for linux
    pid_t this_id = syscall(__NR_gettid);
    printf("You are in Thread(%d) \n", this_id);

So, how to sent data in another thread and save void MyMainFrame::Draw_Graphs(Long_t cnt) in master thread?

You can create the worker thread from MyMainFrame and pass the pointer to the MyMainFrame as argument. Something like:

void MyMainFrame::StartTread()
{
   h1 = new TThread("Consumer", (void(*)(void *))&Worker::handle, (void*) this);
   h1->Run();
}

void* Worker::handle(void* ptr)
{
   MyMainFrame* p = (MyMainFrame *)ptr;
   /// do whatever you want...
   // and use TThread::Lock()/TThread::Unlock() when needed
   return 0;
}

Cheers, Bertrand.

Dear Bertrand,
Should I use only TThread::Lock()/TThread::Unlock() without Emit?
Do you offer to write something like this:

void* Worker::handle(void* ptr)
{
   MyMainFrame* p = (MyMainFrame *)ptr;

  GetData();

p->h1->Lock();
p->Draw_Graphs(DataStr data_str);
p->h1->UnLock();


   return 0;
}

?

Please, could you give small working example?

My code: archive.tar.gz (1.7 KB)
(I have some problems with compilation)

Sorry, I found my mistake.
It is working project: archive.tar.gz (2.0 KB)

But I still have problem: code between

p->h1->Lock();

and

p->h1->UnLock();

in the slave thread.

Use TThread::Lock() and TThread::Unlock() (they are static methods).
And here is a (hopefully working) example: demo.tar.gz (2.1 KB)

Dear Bertrand,
Thanks for working example.
Well, you program works, but could you explain what is mean of this construction

TThread::Lock();
//...
TThread::UnLock();

?

Code inside this block will be in slave thread again.
So, your code works because you create canvas in the slave thread.

      TCanvas *aCanvas = p->fEcan->GetCanvas();
      aCanvas->Modified();
      aCanvas->Update();

In this solution I should divide GUI code in two threads.
It will works, but I think, that it is better to realize GUI only in one thread.

Do you know another solution?

As I said, those are static methods (see for example the TThread::Lock() documentation). And you can also look at the ROOT tutorials.

Well, there are always several solution to a given problem, but GUI and threads in ROOT is a bit tricky, and sorry, but I don’t have any pre-cooked solution…

Cheers, Bertrand.

Dear Bertrand,
Thank you for your help!

1 Like

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