25 Writing A Graphical User Interface: The ROOT GUI Classes
25 Writing A Graphical User Interface: The ROOT GUI Classes
Interface
The ROOT GUI classes support an extensive and rich set of widgets with the Windows 95 look and feel. The
widget classes interface to the underlying graphics system via a single abstract class. Concrete versions of this
abstract class have been implemented for X11 and Win32, thereby making the ROOT GUI fully cross-platform.
Originally the GUI classes were based on Hector Peraza's Xclass'95 widget library http://xclass.sourceforge.net/
TVirtualX
The GUI classes interface to the platform dependent low level graphics system via the semi-abstract graphics
base class TVirtualX. Currently concrete implementations exist for X11 and Win32 (MacOS X is fully
supported via Apple’s X11 implementation). Thanks to this single graphics interface, porting the ROOT GUI to a
new platform requires only the implementation of TVirtualX.
We need to say a few words about the parent-children relationship between the widgets before going through
the real code. The widgets' behaviors are based on this relationship. Every parent widget is responsible for
where the children are and it ensures all properties and behavior for them. For example, if you want to hide
several widgets, it will be enough to hide their parent widget. Later you can show the parent and the children will
appear too. Writing your code you have to specify the parent-child relationship. Usually in a child constructor the
address of the parent is passed as an argument. In general frames are parents of simple widgets. In this
example you will see how we organize the parent-children relationship by using frame widgets in addition to the
canvas window and button widgets.
Let’s now go through the code of the example.C.
The first lines include ROOT header files. The header file names are almost always as the class names
(TApplication, TF1, TCanvas), but there are cases when similar classes are grouped together in one
header file: all frames are declared in TGFrame.h, all buttons – in TGButton.h, etc. Our small example is
based on an object of the class MyMainFrame.
new MyMainFrame(gClient->GetRoot(),200,200);
The first parameter gClient->GetRoot() makes the initial connection to the window server. It is a pointer to
the root window of the screen, which is obtained from gClient. The next two parameters initialize the width
and height of the application window in pixels. Let see what MyMainFrame is. The three arguments pass to the
TGMainFrame constructor when we create the fMain object.
The first thing to note is the inclusion of the RQ_OBJECT macro in the class declaration of MyMainFrame. It is
necessary to provide a standalone class signal/slot capability. The signal/slot communication mechanism is
described in a separate chapter. See “Event Processing: Signals and Slots”.
// example.C
#include <TGClient.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TRandom.h>
#include <TGButton.h>
#include <TGFrame.h>
#include <TRootEmbeddedCanvas.h>
#include <RQ_OBJECT.h>
class MyMainFrame {
RQ_OBJECT("MyMainFrame")
private:
TGMainFrame *fMain;
TRootEmbeddedCanvas *fEcanvas;
public:
MyMainFrame(const TGWindow *p,UInt_t w,UInt_t h);
virtual ~MyMainFrame();
void DoDraw();
};
The laying out is always made with respect to the parent-children relationship. There is a special chapter
presenting the different layout managers, but we will quickly introduce the concept here. The layout process will
apply not to the embedded canvas window but to its parent – the main frame. A popular layout manager and the
one used in this case is the vertical layout manager which arranges its widgets vertically in a column.
The next widget we create as a child of the main frame is the horizontal frame hframe:
TGHorizontalFrame *hframe=new TGHorizontalFrame(fMain,200,40);
The first parameter of its constructor is again the address of its parent, fMain. The next ones define the frame
width and height in pixels. The name of the class TGHorizontalFrame gives a hint that a horizontal layout will
apply on its children widgets. The Draw and Exit buttons will be laid out horizontally. Here are their constructors:
TGTextButton *draw = new TGTextButton(hframe,"&Draw");
hframe ->AddFrame(draw, new TGLayoutHints(kLHintsCenterX,5,5,3,4));
TGTextButton *exit = new TGTextButton(hframe,"&Exit",
"gApplication->Terminate(0)");
hframe ->AddFrame(exit,new TGLayoutHints(kLHintsCenterX,5,5,3,4));
They are created as objects of the TGTextButton class that represent the command buttons with a text label.
When you click on a command button it performs the action shown on its label. These buttons are well known
as “push buttons” or just “buttons”. The parent address hframe is passed as first parameter. The second one
defines the button label and normally indicates the action to be taken when the button is clicked. It is possible to
define a hot key for the button at that point using the hot string for its label. A hot string is a string with a “hot”
character underlined. This character we call the button hot key. It shows the assigned keyboard mnemonic for
the button choice. Following our example, this means that you can use Alt+D to click on Draw button and
Alt+E to click on Exit. There is a possibility to specify a command string as third parameter of the button
constructor. We use it to assign the command gApplication->Terminate(0). The application will be
terminated when you click on the Exit button.
We call again AddFrame() to add the buttons to their parent widget giving layout hints for each of them. This
time we would like to have centered buttons with an amount of 5 pixels on the left, 5 on the right, 3 on the top
and 4 on the bottom. You can feel already that the same steps are repeated three times: to create a new widget
with passing a parent address as a parameter, to define layout hints for it and to add it in the parent list. The
next line is something new:
draw->Connect("Clicked()","MyMainFrame",this,"DoDraw()");
Here we connect a signal to a slot. Whenever the draw button is clicked, it emits a signal that something has
happened (it is clicked) to whom might be interesting in the outside world. The widget does not know who will
use this information. On the other side of the program world there is some code which should be executed when
the button is clicked. This code is called a slot. Think about slots as normal C++ functions or class methods.
The line above specifies that the slot MyMainFrame::DoDraw() will be executed when the draw button is
clicked. Our slot draws the graphics of sin(x)/x in randomly chosen interval every time the draw button sends
a signal “I am clicked”. The signal/slot communication mechanism originally featured in Qt by TrollTech(see
http://doc.trolltech.com/3.1/signalsandslots.html). ROOT supports its own version of signals/slots. We will return
to that point in details later. We specified all child widgets of the horizontal frame (the Draw and Exit buttons in
our case). Next, we need to add their parent frame to the main frame:
fMain->AddFrame(hframe,new TGLayoutHints(kLHintsCenterX,2,2,2,2));
The last thing to do is to set the main window title and to make all widgets visible. Commonly in all systems
windows are assigned by name to be identified by users. This name is displayed in the application’s title bar and
can be set by:
fMain->SetWindowName("Simple Example");
A Standalone Version
As usual a standalone program in C++ has to contain a main() function – the starting point for the application
execution. In this case it is better to separate the program code creating a program header file example2a.h
with the MyMainFrame class declaration and example2a.cxx – with the class methods implementation. To
run our simple example as a standalone application we need to create in addition an object of class
TApplication. It will make a correct initialization of the dictionaries if it is not yet done. It will be responsible
for holding everything together and to handle all events in the application. Its environment provides an interface
to the ROOT graphics system and by calling the Run() method the event loop starts and the application
program is waiting for the user action. The application exits only if the top level window is not closed. Two
header files are used in addition: TApplication.h – for the class TApplication and TGClient.h that is
used to make initial connection to the graphics system. The class TApplication must be instantiated only
once in any given application. The original list of argument options can be retrieved via the Argc() and
Argv() methods.
Note: to have signals/slots working we need to create a dictionary for the class MyMainFrame, i.e. we create
the file ex2aLinkDef.h containing the line:
#pragma link C++ class MyMainFrame;
We compile the example:
rootcint -f ex2aDict.cxx -c example2a.h ex2aLinkDef.h
g++ `root-config --cflags --glibs` -o example2a example2a.cxx ex2aDict.cxx
example2a.h
#include <TQObject.h>
#include <RQ_OBJECT.h>
class TGWindow;
class TGMainFrame;
class TRootEmbeddedCanvas;
class MyMainFrame {
RQ_OBJECT("MyMainFrame")
private:
TGMainFrame *fMain;
TRootEmbeddedCanvas *fEcanvas;
public:
MyMainFrame(const TGWindow *p,UInt_t w,UInt_t h);
virtual ~MyMainFrame();
void DoDraw();
};
Any custom widget can be created by sub classing existing widgets. To achieve a better understanding of the
widgets’ properties they are separated by their type and their inheritance. As all of them inherit from TGObject
and most from TGWidget, these base classes are described first.
TGObject
TGObject is the base class for all ROOT GUI classes. It inherits from TObject. The two data members of this
class contain important information about X11/Win32 window identifier and the connection to the host’s graphics
system. Every GUI element, which derives from TGObject has access to the TGClient via the data member
fClient of TGObject. TGClient creates the connection with the host’s graphics system and sets up the
complete graphics system for all widgets.
TGWidget
The widgets base class TGWidget is typically used as a mix-in class via multiple inheritances. Its properties are
available for all deriving widgets: TGButton, TGComboBox, TGTab, TGColorPalette, TGColorPick,
TGDoubleSlider, TGListTree, TGNumberEntry, TGScrollBar, TGShutterItem, TGTextEntry,
TGSlider, TGListBox, TGView.
This class has four data members keeping information about the widget id – important for event processing, the
window which handles the widget’s events, the widget status flags and the assigned command (if there is any).
The general properties of TGWidget are specified by SetFlags(Int_t flags) and ClearFlags(Int_t
flags) methods. The status flags are: kWidgetWantFocus, kWidgetHasFocus, and kWidgetIsEnabled.
The method Associate(const TGWindow* w) – sets the window which handles the widget events.
SetCommand(const char* command) – sets the command to be executed. The command string can be
gathering via GetCommand() method. For example, the third parameter in TGTextButton constructor can be
omitted and set later in your program, i.e. instead of:
TGTextButton *exit = new TGTextButton(hframe,"&Exit",
"gApplication->Terminate()");
You will have the following the two lines:
TGWindow
TGWindow is a ROOT GUI window base class. It inherits from TGObject and TGFrame derives from it. The
application does not use it directly. It creates and registers a new window within the system. This window has
common characteristics: existing parent, location, size in height and width (it has a default minimum size 1, 1
under which it cannot shrink), border with particular view, state, specific attributes. If there are no specified
arguments their values will be taken from the parent. It receives events from the window system and can paint a
representation of itself on the screen.
Frames
Most of the frame classes are mainly created for arranging widgets in a window. The class TGFrame is a
subclass of TGWindow providing additional window characteristics and overriding some methods of TGWindow.
It is a base class for the simple widgets as buttons, labels, etc. Its only purpose is to draw a frame around
widgets that do not have a frame of their own. The main groups of TGFrame member functions are:
• Window’s functions: DoRedraw(), DeleteWindow(), Activate(), etc.
• Geometry functions: Move(), Resize(), SetSize(), etc.
• Graphics handlers: ChangeBackground(), ChangeOptions(), etc.
• Mouse and keyboard functions: HandleButton(), HandleFocusChange(), HandleKey(),
HandleMotion(), etc.
• Event handlers: HandleEvent(), ProcessEvent(), GetSender(), SendMessage(),
ProcessMessage(), GetLastClick(), etc.
Figure 25-2 The GUI classes hierarchy
Ones of TGFrame member functions provide direct functionality; others – will be overridden by TGFrame
subclasses to ensure particular widget’s functionality. There are two constructors provided in TGFrame class.
One creates a frame using an externally created window:
TGFrame(TGClient *c,Window_t id,const TGWindow *parent = 0);
It is a composite frame with a border and a title. The title explains the purpose of the group and should be a
noun or noun phrase. Here is an example taken from guitest.C:
groupFrame = new TGGroupFrame(tf,"Options",kVerticalFrame);
groupFrame->SetTitlePos(TGGroupFrame::kLeft);
The second line sets the title position on the left. You can change it to be centered or right aligned if you use
TGGroupFrame::kCenter or TGGroupFrame::kRight as a parameter.
Be conservative in the use of borders because of the potential for clutter. Do not place them around single entry
fields, single combo boxes, list boxes and groups of command buttons. The design of these widgets provides
them with a border. The picture above provides kind of borders to avoid.
Layout Management
The layout process is an integral part of any GUI. When you create a simple message window, laying out its few
buttons and text widgets is quite simple. However, this process becomes increasingly difficult if you have to
implement large GUI’s with many widgets that should behave properly when the GUI is resized or uses a
different font type or size. Layout management is the process of determining the size and position of every
widget in a container.
A layout manager is an object that performs layout management for the widgets within a container. You already
know that when adding a component (child widget) to a container (parent widget) you can provide alignment
The base “container” class is TGCmpositeFrame. You can easily change the layout manager using the
SetLayoutManager(TGLayoutManager *l) method. Setting the proper layout manager for each container
is the first step you have to do. The container uses that layout manager to position and size the components
before they are painted. ROOT currently provides the layout managers shown on the picture above.
The next important step is to provide hints about every widget in the container, i.e. to provide positions and right
amount of space between the components. The TGLayoutHints objects set hints by specifying the white
space in pixels around every widget.
Let’s see an example with five buttons. First you put them in a container, assign them desired properties, and
then you lay them out according to the layout manager. This process can be repeated: you go back and add,
remove or change some of the widgets and lay them out again.
Once created, you can consider these widgets as elementary objects even though they are compound ones.
The pictures above present four different layouts of five buttons. The first one shows laid out vertically buttons.
Almost everywhere you can find this vertical orientation. Looking at dialogs you see that often they consist of
number of rows laid out below each other. Some of the rows could have an internal vertical structure as well.
The second picture shows the same buttons laid out horizontally – the next common orientation. The other two
show different layouts based on mixed use of the vertical and horizontal orientation. You might recognize their
pattern: two (third picture) and three (last picture) rows that are vertically laid out.
As we already explained the layout process is always applying to a container. It will be enough to define the
container frame with vertical or horizontal layout to have buttons as in the first and second pictures.
To design them in several rows we need to use additional frames as invisible containers: two horizontal frames,
children of a vertical parent frame; or one horizontal frame laid out vertically with the Draw and Exit buttons. For
widgets in a group it is obvious to use a vertical layout.
The layout hints data member of TGLayoutHints is the bit wise OR between the hints:
Hints Description
kLHintsNoHints no specified layout hints, the default ones will be used
kLHintsLeft specifies the frame position to the left of the container frame after other frames with
the same hint into the list
kLHintsCenterX specifies the frame position centered horizontally (with vertical containers only)
kLHintsRight specifies the frame position to the right of the container frame before any other laid
out frames with the same hint into the list
kLHintsTop specifies the frame position to the top of the container frame, below any laid out
frames with the same hint
kLHintsCenterY specifies the frame position centered vertically (with horizontal containers only)
kLHintsBottom specifies the frame position to the bottom of the container frame, above any laid out
frames with the same hint
The signals/slot communication mechanism is an advanced object communication concept; it largely replaces
the concept of callback functions to handle actions in GUI’s. Signals and slots are just like any object-oriented
methods implemented in C++. The objects are the instances of classes that don’t know anything about each
other. They interact and allow method calls of other object’s methods. The idea is simple: any object can send
out (emit) a signal in certain situations saying that something happened. This is all it does to communicate and it
does not know whether anything is interested in this information. On the other side there might be an object
waiting for that signal and ready to react to it. This object disposes of special instruments to listen to the sent out
signals. To have a communication we need a message transmission between the objects. In this simple
example we use signals and slots. The code of the method TGButton::Clicked() is:
virtual void Clicked() { Emit("Clicked()"); } // *SIGNAL*
I.e. any button emits the signal Clicked() any time someone clicks on it. As you can see this method is virtual
and could be overridden if you need to. In our simple example we call the Connect() method to connect the
Clicked() signal of Draw button with MyMainFrame::DoDraw():
draw->Connect("Clicked()","MyMainFrame",this,"DoDraw()");
In the same way we can connect to the signal Clicked() of the Exit button with the system call
gApplication->Terminate(0). We declare a new slot DoExit(), implement it to invoke the termination
call and associate this slot with the signal Clicked() of the Exit button.
The code of example.C can be changed as follows:
To benefit from this mechanism your classes must inherit from TQObject or otherwise the class definition must
start with RQ_OBJECT(“ClassName”) macro. This macro allows the signals/slots communication mechanism
to be applied between compiled and interpreted classes in an interactive ROOT session without having the
class derive from TQObject. Every signal method declaration is followed by a comment “*SIGNAL*”. Only
instances of a class that defines a signal or instances of its subclasses can emit the signal. The ROOT
implementation of a popular example presenting signals and slots is the next. Let’s have a minimal class
declaration:
class MyClass {
private:
Int_t fValue;
public:
MyClass() { fValue=0; }
Int_t GetValue() const { return fValue; }
Void SetValue(Int_t);
};
It will become the following as interpreted:
class MyClass {
RQ_OBJECT("MyClass")
private:
Int_t fValue;
public:
MyClass() { fValue=0; }
Int_t GetValue() const { return fValue; }
Void SetValue(Int_t); // *SIGNAL*
};
Both class declarations have the same data member and public methods to access the value. By placing the
RQ_OBJECT(“MyClass”) macro inside the MyClass body (MyClass is not inherited from TQObject) we
allow this class to use the signal/slot communication. Any instance of this class can tell the outside world that
the state of its data member has changed by emitting a signal SetValue(Int_t). A possible implementation
of MyClass::SetValue() can be:
void MyClass::SetValue(Int_t v) {
if (v != fValue) {
fValue = v;
Emit("SetValue(Int_t)",v);
}
}
class A {
RQ_OBJECT("A")
private:
Int_t fValue;
public:
A():fValue(0) { }
~A() { }
void SetValue(Int_t value); // *SIGNAL*
void PrintValue() const { printf("value=%d\n",fValue); }
};
void A::SetValue(Int_t value) { // Set new value
// Emit signal "SetValue(Int_t)" with a single parameter
if(value!=fValue) {
fValue=value;
Emit("SetValue(Int_t)",fValue);
}
}
// Main program
#ifdef STANDALONE
int main(int argc, char **argv) {
A* a = new A();
A* b = new A();
a->Connect("SetValue(Int_t)","A",b,"SetValue(Int_t)");
printf("\n******* Test of SetValue(Int_t) signal *******\n");
b->SetValue(10);
printf("\n\t***** b before ******\n");
Widgets in Detail
Buttons
Buttons are a popular group of widgets designed to provide specific interfaces for user interaction. TGButton is
an abstract class defining the general button behavior: width, height, state, its group, tool tip text, etc.
There are two main groups of buttons: command buttons with a text or graphics inside that indicate the action to
be accomplished and option buttons well known as radio and check buttons that select or change properties.
The first group is presented in ROOT by TGPictureButton and TGTextButton classes. They yield an action
as soon as they are clicked. It can be opening/closing a dialog box or invoking a specific function in an
application. Remember the Draw button from the example. The radio and check buttons from the second group
are used to select an option. There is a visual difference between these two groups: the text buttons appear
“pressed in” only while they are clicked, while the radio and check buttons change their appearance when they
are selected and keep that appearance afterwards.
A text button is represented by the class TGTextButton. We already used its constructor in the example. The
button label indicates the action to be taken when the button is selected or pressed. The text can be a hot string
defining a hot key (known as shortcut key also) for this selection. The hot key is an underlined character in a
button label that shows the assigned keyboard mnemonic for its choice. A button that prompts more information
for users has the label generally followed by ellipsis (…).
As we saw the hot strings "&Draw" and "&Exit" define the text labels “Draw” and “Exit” and keyboard
mnemonics Alt+D, Alt+E for their selection. The letter D and E appear underlined on the screen. All text
buttons should have a unique shortcut key with the exception of OK and Cancel.
These buttons are usually placed within a window to provide fast access to frequently used or critical
commands. They help in situations where a command is not available through the menu bar. You already know
that a command string can be passed in the text button via the constructor:
TGTextButton(const TGWindow *p,const char *s,const char *cmd,
Int_t id,GContext_t norm,FontStruct_t font,UInt_totions);
A button label can be changed by SetText(new_label). There are important guidelines to be followed
about a button label. The text has to provide a meaningful description of the performed action. The single-word
label should be used whenever possible, only two-three words for clarity, if necessary. Do not number labels.
Always follow all platform presentation and usage guidelines for standard button functions. Let’s remember a
few standard names and definitions of well known buttons:
OK - any changed information in a window is accepted and the window is closed;
Cancel – closes window without implementing submitted changes;
Reset – resets defaults and cancels any changed information that has not be submitted;
Apply – any changed information is accepted and again displayed in the window that remains open;
Close – closes the window;
Help – opens online Help.
Picture buttons are usually rectangular in shape with an icon or graphics label. These buttons may appear alone
or placed in a group at the window’s top or side. They are most frequently used to quickly access commands,
many of which are normally accessed through the tool bar. For example, the picture buttons below can be used
to provide different styles of a histogram drawing.
A columnar orientation is the preferred manner of radio buttons presentation. If the vertical space on the window
is limited, they can be oriented horizontally. Selection choices should be organized logically in groups. Here is
the example that produces the image above:
br = new TGButtonGroup(p,"Coordinate system",kVerticalFrame);
fR[0] = new TGRadioButton(bg,new TGHotString("&Pixel"));
fR[1] = new TGRadioButton(bg,new TGHotString("&NDC "));
fR[2] = new TGRadioButton(bg,new TGHotString("&User "));
fR[1]->SetState(kButtonDown);
br->Show();
It is enough to change kVerticalFrame to kHorizontalFrame in TGButtonGroup constructor and you will
have radio buttons aligned horizontally:
The class TGButtonGroup will help you to organize button widgets in a group. There is no need to call
AddFrame() since the buttons are added automatically with a default layout hint to their parent by
TGButtonGroup::Show() as shown in the previous example. The buttons in the group have assigned
identifiers. Any button in a group emits a Clicked() signal with this identifier when it is clicked. This giving an
ideal solution to connect several Clicked() signals to one slot.
To have the check button “Event Status” and to set it as selected we need to write:
TGCheckButton *estat = new TGCheckButton(p, "Event Status",1);
estat->SetState(kButtonDown);
Check boxes show the selected choices and any number of them can be selected, including none. Their proper
usage is for setting attributes, properties or values; also for data or choices that are discrete, small and fixed in
number, not easily remembered. With check boxes all alternatives are visible: it is easy to access and compare
choices because they can all be seen together. Each option acts as a switch and can be either “on” or “off”. It is
never changed in contents. Checkboxes differ from radio buttons in that they permit selection of more than one
alternative. Each box can be switched on or off independently. These buttons can be used alone or grouped in
sets. It is good practice to provide default settings for check boxes whenever it is possible.
Text Entries
A TGTextEntry is a one-line text input widget. It contains text that is entered or modified through the
keyboard. This text may be displayed in different way according to the set echo mode. Users can control them
by SetEchoMode(), GetEchoMode() methods.
• kNormal - display characters as they are entered (default);
• kNoEcho - do not display anything;
• kPassword - display asterisks instead of the characters actually entered.
There ate different text alignment modes defined by TGWidget::ETextJustification. They are valid until
text fits the frame width of the text entry field.
• kTextLeft - left-side text alignment
• kTextRight - right-side text alignment
• kTextCenterX - center text alignment along x direction
• kTextTop - top-side text alignment
• kTextBottom - bottom-side text alignment
• kTextCenterY - center text alignment along y direction
Number Entries
The TGNumberEntry class present number entry widgets. A number entry is a single-line field followed by two
small, vertically arranged up-down buttons. Its purpose is to make a selection by either scrolling through a small
set of meaningful predefined choices or typing numbers. The TGNumberFormat class contains enum types to
specify the numeric format. The number entry widget is based on TGTextEntry. It allows only numerical
input. The widget supports numerous formats including integers, hex numbers, real numbers, fixed fraction real
and time/date formats. It also allows to restrict input values to non-negative or positive numbers and to specify
explicit limits.
Menus
Menus provide a list of commands or options helping the user to select and to perform a task. The menu system
classes are TGMenuBar, TGMenuTitle, TGPopupMenu, and TGMenuEntry.
The TGMenuBar class implements a menu bar widget. It is used to specify and provide access to common and
frequently used application actions described in menu titles, implemented by TGMenuTitle class. The menu
bar is the highest-level of the menu system and it is a starting point for all interactions. Also, it is always visible
and allows using the keyboard equivalents. The geometry of the menu bar is automatically set to the parent
widget, i.e. the menu bar automatically resizes itself so that it has the same width as its parent (typically
TGMainFrame).
The menu bar is as a container for its menus – objects of the type TGPopupMenu. Popup menus can appear in
a menu bar. They can be a sub-menu of another popup menu (cascading menus) or can be standalone (as a
context menu). They are made of one or more menu items choices. When displayed, the menu items are
arranged in a vertical list. Usually they correspond to actions (e.g. Open). These items can be labeled with text,
graphics or a combination of both. Each of them should have a character defined as its unique key for access.
Grouped logically by their functionality, they are separated visually by menu separators in groups. For example,
The File menu is a common menu title for tasks that apply to a file, as Open, Save, Close, Print…
// a popup menu
fMenuFile = new TGPopupMenu(gClient->GetRoot());
// adding separator
fMenuFile->AddSeparator();
// menu bar
fMenuBar = new TGMenuBar(fMain,100,20,kHorizontalFrame);
Toolbar
A toolbar (TGToolBar) is a composite frame that contains TGPictureButton objects. It provides an easy
and fast access to most frequently used commands or options across multiple application screens. Also, it
invokes easily a sub application within an application. All its functions can be obtained by application menus. It
is located horizontally at the top of the main window just below the menu bar. All other subtask and sub-feature
bars are positioned along sides of window.
// toolbar icon files
const char *xpms[] = {
"x_pic.xpm",
"y_pic.xpm",
"z_pic.xpm",
0
};
// toolbar tool tip text
if (strlen(xpms[i]) == 0) {
separator = 5;
continue;
}
tb->AddButton(fMain,&t[i],separator);
separator = 0;
}
// adding the tool bar to the main frame
fMain->AddFrame(tb, new TGLayoutHints(kLHintsTop | kLHintsExpandX));
First we need to complete a ToolBarData_t structure for each tool bar button before adding it to the tool bar.
This structure contains:
• the icon file name “filename.xpm”
• the tool tip text – a short help message explaining the button purpose
• the Boolean variable defining the button behavior when is clicked
• kFALSE – do not stay down
• kTRUE – to stay down
• the button ID
• the button pointer (TGButton *) – should be NULL
We create an array *xpms[] containing the icon file names that will be used for a picture button creation. If you
write only the file names here ROOT will search these files in $ROOTSYS/icons directory. If the icon files are
not there, you should provide the full path name also. The array *tips[] contains the tool tip texts for buttons.
The integer variable separator is used to set the distance between two groups of toolbar buttons. It defines the
amount of pixels to the left for each button.
We create a tool bar object and add the buttons using the AddButton method. The variable separator helps us
to define no space between the buttons in a group (0), and 5 pixels extra-space before and after. All buttons
added via this method will be deleted by the toolbar. On return the TGButton field of the ToolBarData_t
structure is filled in (if the icon pixmap was valid). The first parameter is the window to which the button
messages will be sent. Lastly, we create an object of class TGHorizontal3DLine – a horizontal 3D line. It will
separate the toolbar from the menu bar because the layout hints we define as kLHintsTop |
kLHintsExpandX.
It is user friendly to allow the possibility for the tool bar to be turned on or off (via a menu). If you use a single
tool bar, it should fill the complete width of its parent. When using more than one, you should also think about
setting the bar size to the end of the most right button. This way other bars can be displayed in the same row
below the menu bar.
List Boxes
The purpose of a list box is to display a collection of items from which single or multiple selection can be made.
It is always visible, having a scroll bar when the displayed area is not enough to show all items. The choices
may be mutually exclusive (a list box with single selection) or not mutually exclusive (a list box with multiple
selection).
The proper usage of the list boxes is for selecting values, or objects, or setting attributes. You have to create
them to display 4 to 8 choices at one time (3 is a required minimum in case of lack of screen space). The list
should contain not more than 40 items accessible by scrolling view (vertical scroll bar). If more are required, you
should provide a method for using search criteria or scoping the options. The best list boxes use is for textual
data or choices. They should be wide enough to display fully all items. When it is not possible, break the long
items with ellipsis and provide tool tip that displays the full item text.
The list box widget is represented by TGListBox, TGLBContainer, TGLBEntry and TGTextLBEntry
classes. Currently entries are simple text strings (TGTextLBEntry). A TGListBox looks a lot like a
TGCanvas. It has a TGViewPort containing a TGLBContainer which contains the entries and it also has a
vertical scrollbar which becomes visible if there are more items than fit in the visible part of the container. The
TGListBox is user callable. The other classes are service classes of the list box. Here is a sample code
showing how to create a list box with ten entries:
// list box widget containing 10 entries
int fFirstEntry = 0, fLastEntry = 10;
char tmp[20];
TGListBox *fListBox = new TGListBox(parent, 90);
for (i = fFirstEntry; i < fLastEntry; i++) {
sprintf(tmp, "Entry %i", i+1);
fListBox->AddEntry(tmp, i);
}
fListBox->Resize(150, 80);
parent->AddFrame(fListBox,new TGLayoutHints(kLHintsTop | kLHintsLeft,5,5,5,5));
We create the list box widget passing the parent window pointer and giving an ID number. Next we add entries
with specified string and ID to the list box. Before adding the list box to its parent widget, it should be resized via
Resize(width, height) method. The list box width and height are in pixels. The default entry layout hints
are kLHintsExpandX | kLHintsTop. If you want to add entries using different ones, call the method:
TGListBox::AddEntry(TGLBEntry *lbe, TGLayoutHints *lhints);
It adds the specified TGLBEntry and TGLayoutHints to the list box. There are several methods providing a
flexible entry manipulation: you can insert, add or remove list box items dynamically. The list box entry IDs are
used in these methods and also in event processing routines. In our example the integer variables
fFirstEntry and fLastEntry contain the information about the first and last entry IDs. You can add or
remove a list box entry using them in the following way:
// adding an entry
fLastEntry++;
sprintf(tmp, "Entry %i", fLastEntry);
fListBox->AddEntry(tmp, fLastEntry);
fListBox->MapSubwindows();
fListBox->Layout();
. . .
// removing an entry
if (fFirstEntry < fLastEntry) {
fListBox->RemoveEntry(fFirstEntry);
fListBox->Layout();
fFirstEntry++;
}
A single-selection list box is used for selecting only one item in a list.
A multiple-selection list box permits selection of more than one item. The selected choices should be visible –
you have several choices to do this:
Combo Boxes
A combo box is as single-selection list box that shows only the currently selected entry and a prompt button
displayed as a downward arrow. The prompt button provides a visual cue that a list box is hidden. Its main
advantage is consuming of quite a bit of screen space. When the user clicks on it, a list pops up, from which a
new choice can be made. After a new item is chosen the combo box folds again showing the new selection.
The combo box widget is represented by the user callable class TGComboBox. The class TGComboBoxPopup is
a service class. The combo box constructor is very similar to the list box one. The first parameter is a parent
widget pointer again, the second – an integer value that will be used as combo box ID. The method used for
adding entries is very similar to the list box method we used before. The method Select(entryID) sets the
current combo box entry.
char tmp[20];
// combo box layout hints
fLcombo = new TGLayoutHints(kLHintsTop | kLHintsLeft,5,5,5,5);
// combo box widget
TGComboBox *fCombo = new TGComboBox(parent,100);
for (i = 0; i < 10; i++) {
sprintf(tmp, "Entry%i", i+1);
fCombo->AddEntry(tmp, i+1);
}
fCombo->Resize(150, 20);
// Entry3 is selected as current
fCombo->Select(2);
parent->AddFrame(fCombo, fLcombo);
You have the same flexibility to add, insert or remove entries. As with list boxes you can retrieve the information
for currently selected item via GetSelected or GetSelectedEntry methods. The first one returns the entry
ID, the second – the current entry pointer (TGLBEntry *).
Sliders
A slider is a scale with an indicator (slider) that you can drag to choose a value from a predefined range. It may
be oriented horizontally or vertically. In both cases it provides an excellent indication of where a value exists
within a range of values.
The class TGHSlider represents the horizontal slider; TGVSlider – the vertical one. Both inherit from the
base class TGSlider that creates the main slider parameters: the range of values within a value can be
selected; the indicator type; the tick mark scale. Using its methods SetRange, SetPosition and SetScale
you can set these parameters. To retrieve the set slider value you can call GetPosition method.
Next sample code creates a horizontal slider hslider with a tick mark of type kSlider1. Its width is 150
pixels, and its scale is placed down (kScaleDownRight). The last parameter in the TGHSlider constructor is
the slider ID. It will be used for event processing. The methods SetRange and SetPosition set the range
and the current tick mark position of the slider.
hslider = new TGHSlider(parent,150,kSlider1 | kScaleDownRight,sID);
hslider->SetRange(0,50);
Double Slider
Double slider widgets allow easy selection of a min and a max value out of a range. They can be either
horizontal or vertical oriented. There is a choice of different types of tick marks: kDoubleScaleNo,
kScaleDownRight, kDoubleScaleBoth.
To change the min value you should press the left mouse button near to the left (TGDoubleHSlider) or
bottom (TGDoubleHSlider) edge of the slider. Alternatively, to change the max value you need to press the
mouse near to the right (TGDoubleHSlider) or top (TGDoubleHSlider) edge of the slider. To change both
values simultaneously you should press the left mouse button near to the center of the slider.
TGDoubleSlider is an abstract base class that creates the main slider parameters. The concrete class to use
for a vertical double slider is TGDoubleVSlider and TGDoubleHSlider for a horizontal one. The double
slider constructors are similar to those of the other sliders. If you set kDoubleScaleNo as a scale parameter
no scale will be drawn. Here is an example:
vDslider = new TGDoubleVSlider(p,100,kDoubleScaleNo,dsliderID);
vDslider->SetRange(-10,10);
Triple Slider
The new TGTripleHSlider and TGTripleVSlider classes inherit from the double slider widgets and allow
easy selection of a range and a pointer value. The pointer position can be constrained into the selected range or
can be relative to it.
To change the slider range value press the left mouse button near to the left/right (top/bottom) edges of the
slider. To change both values simultaneously press the mouse button near to the slider center. To change
pointer value press the mouse on the pointer and drag it to the desired position.
fSlider = new TGTripleHSlider(parent, 100, kDoubleScaleBoth, kSLD_ID,
kHorizontalFrame);
parent->AddFrame(fSlider, new TGLayoutHints(kLHintsExpandX, 5, 5, 5, 5));
fSlider->SetConstrained(kTRUE);
fSlider->SetRange(rmin, rmax);
fSlider->SetPosition(pmin, pmax);
fSlider ->SetPointerPosition(pvalue);
Progress Bars
A progress bar is a widget that shows that an operation is in progress and how much time is left. It is a long
rectangular bar, initially empty, that fills with a color as a process is being performed. The filled-in area indicates
the percentage of the process that has been completed. You should use this widget for waits exceeding one
minute. For a very time consuming operation it is better to break the operation into subtasks and provide a
progress bar for each of them.
A progress bar may be oriented horizontally or vertically. The horizontally oriented progress bar fills with a color
from left to right; the vertically oriented – from bottom to top. A percent complete message provides an
Static Widgets
The classes TGLabel and TGIcon show some information - text or graphics. The line below creates a label
object. The syntax is very simple: you specify the parent widget and a string object holding the desired text.
TGLabel *label = new TGLabel(parentWidget, "Label’s string");
Next sample creates an icon object. First we create an object of type TGPicture. The TGPicture objects are
never created directly by the application code. We call TGClient telling it the pixmap’s file name to create a
TGPicture object and, in turn, it will return a pointer to the created object. If the pixmap file cannot be found
the returned pointer will be NULL. As usual, the first parameter of a TGIcon constructor is the parent frame. The
second one is the TGPicture object holding the pixmap we want to show. Last two parameters define the
width and height of pixmap in pixels. In the end we add the created icon object to its parent.
// icon widget
const TGPicture *ipic =(TGPicture *)gClient->GetPicture("leaf.xpm");
TGIcon *icon = new TGIcon(parent,ipic,40,40);
parent->AddFrame(icon,new TGLayoutHints(kLHintsLeft | kLHintsBottom,1,15,1,1));
The TGPicture objects are cached by TGClient in order to keep the resource usage low and to improve the
efficiency of the client-server windowing systems. TGClient will check whether a pixmap with the same name
was already loaded before to register a new picture object. If it finds it, it will return a pointer to the existing
object. Also, it will increase the usage counter for the object.
All TGPicture objects are managed by the class TGPicturePool. TGClient creates an object of this type
upon initialization. Normally your application program does not deal directly with this class because all
manipulations go through TGClient class.
Once you have finished with using of the TGPicture object, you should call the method
TGClient::FreePicture(const TGPicture *pic) to free it. The usage counter of the picture object will
be decreased and when it reaches zero – the TGPicture object will be deleted.
Status Bar
The status bar widget is used to display some information about the current application state: what is being
viewed in the window, a descriptive message about selected objects, or other no interactive information. It may
also be used to explain highlighted menu and tool bar items.
Splitters
A window can be split into two parts (panes) by using a horizontal or a vertical splitter. A horizontal splitter
resizes the frames above and below of it; a vertical splitter resizes the frames left and right of it.
This widget is represented by TGSplitter, TGHSplitter, and TGVSplitter classes. Currently there is no
special graphics representation for splitter widgets; only the cursor changes when crossing a splitter.
There is nothing special to create a splitter – two lines of code only:
TGHSplitter *hsplitter = new TGHSplitter(fVf);
hsplitter->SetFrame(fH1,kTRUE);
You call a horizontal TGHSplitter or a vertical TGVSplitter splitter constructor and after you set the frame
to be resized via SetFrame method. In spite of that, there are rules to be followed when you create a splitter in
your application.
For a horizontal splitter they are:
• the parent of a horizontal splitter must inherit from TGCompoziteFrame and must have a vertical
layout
• the above resized frame must have kFixedHeight option set
• use layout hints kLHintsTop | kLHintsExpandX when adding the above resized frame to its
parent
• use layout hints kLHintsBottom | kLHintsExpandX | kLHintsExpandY when adding the
bottom resized frame to its parent
• set the above frame to be resized using SetFrame method; the second parameter should be
kTRUE
You can see these rules in the code below:
// Create horizontal splitter
fVf = new TGVerticalFrame(fMain,10,10);
fH1 = new TGHorizontalFrame(fVf,10,10, kFixedHeight);
fH2 = new TGHorizontalFrame(fVf,10,10);
fFtop = new TGCompositeFrame(fH1,10,10, kSunkenFrame);
fFbottom = new TGCompositeFrame(fH2,10,10,kSunkenFrame);
fLtop = new TGLabel(fFtop,"Top Frame");
fLbottom = new TGLabel(fFbottom,"Bottom Frame");
Embedded Canvas
This class creates a TGCanvas in which a well known ROOT TCanvas is embedded. A pointer to the TCanvas
can be obtained via the GetCanvas() member function.
fEc1 = new TRootEmbeddedCanvas("ec1",fParent,100,100);
fParent ->AddFrame(fEc1, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));
fEc2 = new TRootEmbeddedCanvas("ec2",fParent,100,100);
fParent ->AddFrame(fEc2, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY));
fEc1->GetCanvas()->SetBorderMode(0);
fEc2->GetCanvas()->SetBorderMode(0);
fEc1->GetCanvas()->SetBit(kNoContextMenu);
fEc1->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)",
"MyClass", this,
"HandleMyCanvases(Int_t,Int_t,Int_t,TObject*)");
Object Editors
Every object editor follows a simple naming convention: to have as a name the object class name concatenated
with ‘Editor’ (e.g. for TGraph objects the object editor is TGraphEditor). Thanks to the signals/slots
communication mechanism and to the method DistanceToPrimitive() that computes a ‘‘distance’’ to an
object from the mouse position, it was possible to implement a signal method of the canvas that says which is
the selected object and to which pad it belongs. Having this information the graphics editor loads the
corresponding object editor and the user interface is ready for use. This way after a click on ‘axis’—the axis
editor is active; a click on a ‘pad’ activates the pad editor, etc.
The algorithm in use is simple and is based on the object-oriented relationship and communication. When the
user activates the editor, according to the selected object <obj> in the canvas it looks for a class name
<obj>Editor. For that reason, the correct naming is very important. If a class with this name is found, the
editor verifies that this class derives from the base editor class TGedFrame. If all checks are satisfied, the editor
makes an instance of the object editor. Then, it scans all object base classes searching the corresponding
object editors. When it finds one, it makes an instance of the base class editor too.
Once the object editor is in place, it sets the user interface elements according to the object’s status. After that,
it is ready to interact with the object following the user actions.
The graphics editor gives an intuitive way to edit objects in a canvas with immediate feedback. Complexity of
some object editors is reduced by hiding GUI elements and revealing them only on users’ requests.
An object in the canvas is selected by clicking on it with the left mouse button. Its name is displayed on the top
of the editor frame in red color. If the editor frame needs more space than the canvas window, a vertical scroll
bar appears for easy navigation.
Figure 25-4 Histogram, pad and axis editors
A script file from the browser can be dropped to a TGTextView or TGTextEdit widget in TGTextEditor.
On Linux, it is possible to drag objects between ROOT and an external application. For example to drag a
macro file from the ROOT browser to the Kate editor. On Windows, drag and drop works only within a single
ROOT application (for the time being), but works also from Windows Explorer to TCanvas ot to TGTextEdit.