Argus - GUI extension of ROME


  • Introduction.
  • How to design own projects.
  • Introduction.

    AGRUS is a GUI generator based on ROOT. It helps you write any sort of graphical user interface. Although it is used in the ROME environment as the GUI extension of ROME it is not limited to ROME. It can be used as a completely separated (stand alone) GUI application. The way how ROME and ARGUS are executed can be steered with the configuration file. Three modes are supported. First, the framework can run as a stand alone ROME application. This will be a pure analyzer with no GUI extension. This is the default mode of ROME. Second is a mixed mode. Therefore ARGUS acts as the GUI extension of ROME. The framework will run as an analyzer with a GUI displaying the ROME objects. The third mode is a stand alone ARGUS application. In this mode you will have no analyzer running. Therefore no tasks will be executed. The GUI may accesses objects of a ROME application running in a separate process. But it can also run completely independent of ROME.

    How to design own projects.

    How to create graphical objects.

    Each tab in ROME is a class, and Tabs can have several graphical objects in it. A Tab can be created with adding an entry in a project definition xml file.

    Xmonitor.xml
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <ROMEFrameworkDefinition>
      <Experiment>
        <ExperimentName>X</ExperimentName>
        <ExperimentShortCut>X</ExperimentShortCut>
        <ProgramName>monitor</ProgramName>
      </Experiment>
      <Tabs>
        <Tab>
          <TabName>X</TabName>
          <TabTitle>XXX</TabTitle>
        </Tab>
      </Tabs>
    </ROMEFrameworkDefinition>
    

    From this definition file, romebuilder will generates header files and a source file under include/tabs and src/tabs. You will see this window when you run the created executable.

    You can put any ROOT graphical objects such as TGTextButton, TRootEmbeddedCanvas, TGListBox and so on with editing these header and source file. Your change in XTX.h and XTX.cpp will be preserved even if you run romebuilder again, while XTX_Base.h will be over written.

    For example, following change of XTX.h and XTX.cpp will add a canvas in the tab.

    XTX.h
    #include <include/generated/XTX_Base.h>
    #include <TCanvas.h>
    #include <TRootEmbeddedCanvas.h>
    
    class XTX : public XTX_Base
    {
    protected:
       TRootEmbeddedCanvas *fCanvas;
    
    public:
       XTX():XTX_Base(){
          fCanvas = 0;
       }
    
       ~XTX(){
    
          if(fCanvas)
             delete fCanvas;
       }
    
       void Init();
       ClassDef(XTX,1)
    };
    

    XTX.cpp
    #include "include/tabs/XTX.h"
    
    ClassImp(XTX)
    
    void XTX::Init()
    {
    
       fCanvas = new TRootEmbeddedCanvas("XCanvas", this
                     , ((UInt_t)200*gAnalyzer->GetWindowScale())
                     , ((UInt_t)200*gAnalyzer->GetWindowScale()));
    
       AddFrame(fCanvas, new TGLayoutHints( kLHintsExpandX |
       kLHintsExpandY, 10, 10, 4, 4));
    }
    

    How to draw histograms.

    ROME applications can obtain histograms from other ROME application over network. For this purpose, NetFolder can be used, Following example shows how to obtain a histogram and draw. Framework is in charge of connection to server, so what you need to do is just to call FindObjectAny function in your code.

    Xmonitor.xml
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <ROMEFrameworkDefinition>
      <Experiment>
        <ExperimentName>X</ExperimentName>
        <ExperimentShortCut>X</ExperimentShortCut>
        <ProgramName>monitor</ProgramName>
      </Experiment>
      <Tabs>
        <Tab>
          <TabName>X</TabName>
          <TabTitle>XXX</TabTitle>
        </Tab>
      </Tabs>
      <NetFolders>
        <NetFolder>
          <NetFolderName>mynetfolder</NetFolderName>
        </NetFolder>
      </NetFolders>
    </ROMEFrameworkDefinition>
    

    XTX.h
    #include <include/generated/XTX_Base.h>
    #include <TCanvas.h>
    #include <TRootEmbeddedCanvas.h>
    #include <TH1.h>
    
    class XTX : public XTX_Base
    {
    protected:
       TRootEmbeddedCanvas *fCanvas;
       TH1F *fHisto;
    
    public:
       XTX():XTX_Base(){
          fCanvas = 0;
          fHisto = 0;
       }
    
       ~XTX(){
          if(fCanvas)
             delete fCanvas;
    
          if(fHisto)
             delete fHisto;
       }
    
       void Init();
       void DrawHisto();
       ClassDef(XTX,1)
    };
    

    XTX.cpp
    #include "include/tabs/XTX.h"
    
    ClassImp(XTX)
    
    void XTX::Init()
    {
       fCanvas = new TRootEmbeddedCanvas("XCanvas", this
                     , ((UInt_t)200*gAnalyzer->GetWindowScale())
                     , ((UInt_t)200*gAnalyzer->GetWindowScale()));
       AddFrame(fCanvas, new TGLayoutHints( kLHintsExpandX |
       kLHintsExpandY, 10, 10, 4, 4));
       DrawHisto();
    }
    
    
    void XTX::DrawHisto(){
       if(!gAnalyzer->GetNetFolder("mynetfoler")){
          return;
       }
       fCanvas->GetCanvas()->cd();
       
       if(fHisto)
          delete fHisto;
       fHisto =
          (TH1F*)gAnalyzer->GetNetFolder("mynetfoler")->FindObjectAny("hAdc0_001");
       if (!fHisto)
          cout<<"Histo hAdc0_001 not available."<<endl;
       else
          fHisto->Draw();
       
       fCanvas->GetCanvas()->Modified();
       fCanvas->GetCanvas()->Update();  
       return;
    }
    

    How to create buttons.

    In this example, when the button is pressed. the message (kC_COMMAND, kCM_BUTTON and ButtonID) will be sent to the function ProcessMessage. You can call any function from ProcessMessage.

    In addition to ProcessMessage, ROME provide a special function ProcessMessageThread. If you rename ProcessMessage to ProcessMessageThread in XTX.h and XTX.cpp, All functions called from ProcessMessageThread will be executed in new thread. It means you can do other operation even if the called function is not finished.

    It is possible to use both ProcessMessage and ProcessMessageThread. In this case ProcessMessageThread needs to be called from ProcessMessage. There is an example project in rome/argus/examples/processMessage. Please note that multi thread application might not safe (especially on multi processor computer). For instance, usually Xlib is not thread safe.

    XTX.h
    #include <include/generated/XTX_Base.h>
    #include <TGButton.h>
    
    class XTX : public XTX_Base
    {
    protected:
    
       TGTextButton  *fButton;
       enum CommandIdentifiers {
          ButtonID
       };
    
    public:
       XTX():XTX_Base(){
          fButton = 0;
       }
    
       ~XTX(){
          delete fButton;
       }
    
       void Init();
    
       Bool_t ProcessMessage(Long_t msg, Long_t param1, Long_t param2);
       
       ClassDef(XTX,1)
    };
    

    XTX.cpp
    #include "include/tabs/XTX.h"
    
    ClassImp(XTX)
    
    void XTX::Init()
    {
    
       fButton = new TGTextButton(this, "Button", ButtonID);
       fButton->Associate(this);
       AddFrame(fButton, new TGLayoutHints( kLHintsCenterX |
                             kLHintsCenterY, 10, 10, 4, 4));
    }
    
    
    Bool_t XTX::ProcessMessage(Long_t msg, Long_t param1, Long_t param2)
    {
       switch (GET_MSG(msg)) {
       case kC_COMMAND:    
          switch (GET_SUBMSG(msg)) {
          case kCM_BUTTON:
             switch (param1){
             case ButtonID:
                cout<<"Button is clicked."<<endl;
                break;
             }
             break;
          }
          break;    
       }
       return true;
    }
    

    How to create menus.

    Menus can be added with adding entries in a project definition XML file. Menus belong to a tab, and they appear when the tab is front. Every menu item has MenuItemID. This number will be sent to a special function MenuClicked. You can implement any function in MenuClicked. Please note that MenuItemID needs to be between 0 and maxNumberOfTabMenuItems. maxNumberOfTabMenuItems is defined in rome/builder/include/ROMEBuilder.h, and the default value is 20.


    Xmonitor.xml
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <ROMEFrameworkDefinition>
      <Experiment>
        <ExperimentName>X</ExperimentName>
        <ExperimentShortCut>X</ExperimentShortCut>
        <ProgramName>monitor</ProgramName>
      </Experiment>
      <Tabs>
        <Tab>
          <TabName>X</TabName>
          <TabTitle>XXX</TabTitle>
    
          <Menus>
            <Menu>
              <MenuTitle>Y</MenuTitle>
              <MenuItems>
                <MenuItem>
                  <MenuItemTitle>YYY</MenuItemTitle>
                  <MenuItemID>1</MenuItemID>
                </MenuItem>
              </MenuItems>
            </Menu>
          </Menus>
        </Tab>
      </Tabs>
    </ROMEFrameworkDefinition>
    

    XTX.h
    #include <include/generated/XTX_Base.h>
    
    class XTX : public XTX_Base
    {
    protected:
    
    public:
       XTX():XTX_Base(){
       }
    
       ~XTX(){
       }
    
       void Init();
    
       void MenuClicked(Long_t param);
       
       ClassDef(XTX,1)
    };
    

    XTX.cpp
    #include "include/tabs/XTX.h"
    
    ClassImp(XTX)
    
    void XTX::Init()
    {
    }
    
    
    void XTX::MenuClicked(Long_t param){
       cout<<"Menu is clicked.("<<param<<")"<<endl;
    }
    

    How to show message in status bar.

    gWindow->GetStatusBar() returns a pointer of TGStatusBar. You can split status bar and set text. Please note that the first part may be used by framework.

    XTX.cpp
    #include "include/monitor/XWindow.h"
    
    ClassImp(XTX)
    
    void XTX::Init()
    {
    
       gWindow->GetStatusBar()->SetText("status bar");
    }
    

    How to execute functions in background.

    There are two ways to execute functions in background. One is executing functions in a new thread, and the other is using TTimer.

    With creating thread, you can execute functions in parallel with the main thread. It uses effectively (maybe less) CPU power than TTimer. On the other hand, you have to carefully write code, because two threads can access the same memory space really at the same time. It is known that Xlib is not thread safe.

    If the calculation time of the functions is rather short or you want to use safer way. You may use TTimer. It executes functions periodically within the main thread.


    R.Sawada