Blog Archives

Radio Buttons, Combo Boxes and Event Handling

Welcome back! Today we will walk through a slightly bigger example to understand the way event handling works in YUI. We will also learn use two new widgets that we see in almost all the applications we use : Radio Buttons and Combo Boxes.

We will create a program that contains a combo box, two radio buttons, a label and a push button. The radio buttons are called immediate and event1. If immediate is selected, the label reflects the selected value of the combo box without clicking the push button. If event1 radio button is selected, the push button has to be clicked for the label to reflect the value of the selected element of the combo box. Simple concept, right?

Before we proceed, please note that all the examples found in this blog are available at : https://gitorious.org/libyui. So feel free to compile and see the output for yourself! (ofcourse, only on openSUSE at the moment.)

Also, I will be assuming that you know the basics of YUI and that you have read my previous post : http://nbprashanth.wordpress.com/2011/04/22/libyui-an-introduction/. If not, it is highly recommended that you read that first as we will build on that foundation.

So, lets start with the example.


#include "YUI.h"
#include "YWidgetFactory.h"
#include "YDialog.h"
#include "YLayoutBox.h"
#include "YComboBox.h"
#include "YEvent.h"
#include "YLabel.h"
#include "YPushButton.h"
#include "YRadioButton.h"
#include "YRadioButtonGroup.h"

int main( int argc, char **argv )
{
YDialog * dialog = YUI::widgetFactory()->createPopupDialog();

YLayoutBox * vbox = YUI::widgetFactory()->createVBox( dialog );
YLayoutBox * radiobox = YUI::widgetFactory()->createHBox( vbox );

YRadioButtonGroup *radiogrp = YUI::widgetFactory()->createRadioButtonGroup(radiobox);
YRadioButton *immediate = YUI::widgetFactory()->createRadioButton(radiobox,"Immediate", false);
YRadioButton *event1 = YUI::widgetFactory()->createRadioButton(radiobox,"Not Immediate", true);
immediate->setNotify(true);
event1->setNotify(true);

radiogrp->addRadioButton(immediate);
radiogrp->addRadioButton(event1);

YLayoutBox * hbox = YUI::widgetFactory()->createHBox( vbox );
YComboBox *cbox = YUI::widgetFactory()->createComboBox(hbox,"Choose Item",false);
YLabel *label = YUI::widgetFactory()->createLabel(hbox,"",false,false);

YLayoutBox * hbtnbox = YUI::widgetFactory()->createHBox( vbox );
YPushButton *button = YUI::widgetFactory()->createPushButton(hbtnbox,"&Update");

cbox->addItem("English",true);
cbox->addItem("Spanish",false);
cbox->addItem("French",false);
cbox->addItem("Hindi",false);
cbox->addItem("Chinese",false);
cbox->addItem("Russian",false);

do
{
YEvent *event = dialog->waitForEvent();

if(event)
{
if(event->eventType() == YEvent::CancelEvent)
break;

if(event->widget() == cbox || event->widget() == button)
{
label->setText(cbox->selectedItem()->label());
}

if(event->eventType() == YEvent::WidgetEvent && (event->widget() == immediate || event->widget() == event1))
{
radiogrp->uncheckOtherButtons((YRadioButton*)event->widget());

if(event->widget()==immediate)
cbox->setNotify(true);
else
cbox->setNotify(false);
}

}
}while(1);

dialog->destroy();
}

Lets go through the code a few lines at a time now.


YDialog * dialog = YUI::widgetFactory()->createPopupDialog();

As in hello world example, the line above is used to create the window, which would contain all our widgets (buttons, radio buttons etc).


YLayoutBox * vbox = YUI::widgetFactory()->createVBox( dialog );
YLayoutBox * radiobox = YUI::widgetFactory()->createHBox( vbox );

The next two lines, are used to set the layout. We add a VBox layout (used to arrange the widgets in a vertical stack) to the window and add a HBox layout (used to arrange the widgets in a horizontal stack) to the vbox layout created before. The layouts, as such do not show up on the screen. They are used as containers to place widgets on the screen in an easy way. It is always preferred to place components/widgets in such containers as they take care of events like window resizing.


YRadioButtonGroup *radiogrp = YUI::widgetFactory()->createRadioButtonGroup(radiobox);
YRadioButton *immediate = YUI::widgetFactory()->createRadioButton(radiobox,"Immediate", false);
YRadioButton *event1 = YUI::widgetFactory()->createRadioButton(radiobox,"Not Immediate", true);

The next three lines are used to create a radio button group, radiogrp and two radio buttons immediate and event1. Radio buttons are used when the user has to select exactly one of the given choices. Hence, it becomes necessary to group these radio buttons. The radio button group helps us do just that.

Please note that just by adding radio buttons to a radio button group, the other radio buttons are not deselected by default. This is handled in the event loop.

While creating the radio buttons, the first parameter is the parent widget, inside which the radio button is placed. The second parameter is the label value or the value of the text displayed next to the radio button. And finally, the last value represents the default selected state. In our example, the immediate radio button is disabled by default whereas the event1 radiobutton is enabled by default.


immediate->setNotify(true);
event1->setNotify(true);

In the last two lines, we call the set notify property of both the radio buttons and set them to true. If this is not done, an event is not generated by the radio button itself and we will have to manually check using some other strategy, like using a push button.

Let’s take a minute to understand this. There are two ways in which we can handle events. The first is when any change to the widget generates an event or by using another widget to generate an event and use it to check for any change. In the second case, the most commonly used widget would be the push button. You would probably have noticed google instant, where the search page gets updated as you change the search phrase. While not similar in technology (they use ajax), that is the an example for the first type of event notification. If you remember google in the olden days, where we had to press the search button after we change the search phrase everytime. That is an example for the second type. Let’s call the first type of events as “immediate” and the second type as “event-driven”.

So, since we want any clicks/changes to the radio button to reflect on it’s own without any button presses, we set their setNotify methods to true! Easy right? Let’s move on now.


radiogrp->addRadioButton(immediate);
radiogrp->addRadioButton(event1);

In the first two lines, we add the radio button to the radio button group we created. This is done because immediate mode and event driven modes are opposite and cannot be enabled at the same time. Hence, we group them so that only one can be enabled at a time. We disable the other radio buttons in the event loop and is not done automatically. This is a very important point to note as it is not very intuitive.


YLayoutBox * hbox = YUI::widgetFactory()->createHBox( vbox );
YComboBox *cbox = YUI::widgetFactory()->createComboBox(hbox,"Choose Item",false);
YLabel *label = YUI::widgetFactory()->createLabel(hbox,"",false,false);

The next set of lines define a HBox for the combo box and the label widget. The combo box is simply another name for the drop down box which we use to select an item from a list of items. In our case, while creating the combo box, we use three parameters. The first, is obviously the parent widget. the second, is a label that is used to describe the combo box. In our case it is “choose item”(Maybe “Choose language” is more appropriate?). The third parameter is a boolean value that is used to set the editable property of the combo box. If set to true, the user is allowed to enter a value that is not a part of the list. He can type it out as if the combo box was a text field or choose from the list. Since we want the user to select only from the list, we set this to false.


YLayoutBox * hbtnbox = YUI::widgetFactory()->createHBox( vbox );
YPushButton *button = YUI::widgetFactory()->createPushButton(hbtnbox,"&Update");

The next two lines are used to create a HBox and add a push button to it. Since we have seen the parameters used to create the push button in the last post, i am not elaborating it here again.


cbox->addItem("English",true);
cbox->addItem("Spanish",false);
cbox->addItem("French",false);
cbox->addItem("Hindi",false);
cbox->addItem("Chinese",false);
cbox->addItem("Russian",false);

The last set of lines are used to add elements to the combo box. The second boolean parameter is the default selected element. If “Spanish” was set to true, the first and default element in the combo box would be Spanish.

Please note that there are more efficient ways to add large number of items to a combo box. In this example, we neglect that as we have very few items.

A point to be noted here is that when layouts are used, please pay attention to their parent widget parameters. Giving incorrect parameters can cause skewed layouts. Also, like in our case, when we nest 3 HBox elements in a VBox, the Hbox elements are stacked one over another with the first HBox on the top. But the elements are arranged linearly within each HBox. I hope this distinction is very clear.

And that’s the end of the user interface! Take a break and lets move to the event handling part.


do
{
YEvent *event = dialog->waitForEvent();

if(event)
{
if(event->eventType() == YEvent::CancelEvent)
break;

if(event->widget() == cbox || event->widget() == button)
{
label->setText(cbox->selectedItem()->label());
}

if(event->eventType() == YEvent::WidgetEvent && (event->widget() == immediate || event->widget() == event1))
{
radiogrp->uncheckOtherButtons((YRadioButton*)event->widget());

if(event->widget()==immediate)
cbox->setNotify(true);
else
cbox->setNotify(false);
}

}
}while(1);

These set of lines control all the events and actions of our program. It is very important to understand the working of the event loop in order to program effectively in YUI. Let’s go line by line.

As you can see, the entire set of code is placed in an infinite do while loop. The loop need not be do while. It can be a while loop or a for loop (your pick). The reason for the loop being infinite is obvious. We want to keep polling for events as long as the program is running.

The first line in the loop is is used to create an instance of YEvent which is equated to dialog->waitForEvent(). Here, dialog is the main window. We use this as it is the top level container for out program i.e, all widgets are under this. The dialog->waitForEvent() function is used to poll for events. With no parameters, it waits indefinitely for an event to occur. In some cases, we would like to set a time out and carry on with the loop in case no event occurs. In such a case, we can use an over loaded method which takes an integer which is the timeout in milli seconds. Thus, dialog->waitForEvent(5) waits for 5 ms for an event to occur. If an event occurs, the event is stored in the YEvent variable and the the loop continues. If no event occurs, then the program execution continues after the timeout, 5ms.

It is important to note that the waitForEvent is an infinite loop which is truncated by the timeout time or by an event.

In the next line, we check if event is not empty. This is not needed if a timeout has not been specified since this line is executed only when an event has occurred.

The main functions associated with YEvent variables are :
1. eventType() – Returns the event type. The types are defined in the YEvent class.
2. widget() – Returns the widget that raised the event.

The above two functions are frequently used and is handy to know about their function before we move further.


if(event->eventType() == YEvent::CancelEvent)
break;

In the above line, we check if the event is of the type “CancelEvent”. This event is triggered when the user closes the window. In such a case, we want to break out of the loop and destroy the widgets. Hence, we issue the break command.


if(event->widget() == cbox || event->widget() == button)
{
label->setText(cbox->selectedItem()->label());
}

In the above set of lines, we check if either the combo box or the button has raised an event using the widget() function. If true, we change the label text by using the setText() function as shown above. cbox->selectedItem()->label() is used to retrieve the text of the currently selected element.


if(event->eventType() == YEvent::WidgetEvent && (event->widget() == immediate || event->widget() == event1))
{
radiogrp->uncheckOtherButtons((YRadioButton*)event->widget());

if(event->widget()==immediate)
cbox->setNotify(true);
else
cbox->setNotify(false);
}

After the last two blocks of code, i guess this is easier to understand.Here we check for the event type “WidgetEvent”. Radio buttons, when setNotify is set to true, raise such events in case of a change. Also, we check if the widget that raised this event is either immediate or event1, our two radio buttons. This is to ensure that the event is being raised by specific widgets though in our case, it may be trivial and not necessary.

In the next line, we unset the other radio buttons in the same group. Again, note that we do this explicitly!! The uncheckOtherButtons takes a radio button as a parameter and unchecks all other radio buttons that are a part of the same group. Note that the uncheckOtherButtons function belongs to the RadioButtonGroup class and not the RadioButton class.

In the last few lines, we toggle the setNotify of the combo box depending on the radio button selected.


dialog->destroy();

This is the last line of code and is placed outside the event loop. It is used to destroy all widgets and the main window as well. Execution of this command truncates the program.

And that’s it! Phew… long post.

I hope this post helped a few of you out there to understand a the basic functioning of the YUI library. Now, you are ready to start writing a few programs of your own! In case you need some help, feel free to contact me through the comments section below.

libYUI – An Introduction

Time for some code!

Like all languages, let us start with the customary Hello World Example. In this post, we will walk through a small example, compile it and run in on all three frameworks (Qt, Gtk & ncurses). Ofcourse, at the moment these programs can be compiled only on SUSE/openSUSE systems. The Ubuntu port of YUI will be available soon.

Let’s take a look at the program now :

//File Name : HelloWorld.cc

#include "YUI.h"
#include "YWidgetFactory.h"
#include "YDialog.h"
#include "YLayoutBox.h"
#include "YEvent.h"

int main( int argc, char **argv )
{
YDialog * dialog = YUI::widgetFactory()->createPopupDialog();
YLayoutBox * vbox = YUI::widgetFactory()->createVBox( dialog );
YUI::widgetFactory()->createLabel ( vbox, "Hello, World!" );
YUI::widgetFactory()->createPushButton( vbox, "&OK" );

dialog->waitForEvent();
dialog->destroy();
}

First, let’s look at the headers.

#include "YUI.h"
#include "YWidgetFactory.h"
#include "YDialog.h"
#include "YLayoutBox.h"
#include "YEvent.h"

The YUI.h file is must for all YUI programs. The other header files are added here as they represent the components that are used in the program. For example, YDialog.h is used here as we use a popup dialog box as the main window.


YDialog * dialog = YUI::widgetFactory()->createPopupDialog();

All widgets are created using the widgetFactory. In the above line, we create a popup dialog and use it as the main window.

YLayoutBox * vbox = YUI::widgetFactory()->createVBox( dialog );

We create a VBox (Vertical Layout Box) which is a part of YLayout class. The parameter dialog, represents the parent widget.
This means that the VBox is a widget nested inside the main window. In the previous line, we do not specify any arguments as the main window has no parent.

YUI::widgetFactory()->createLabel ( vbox, "Hello, World!" );

Now, we add a label to the VBox with the text as “Hello, World!”. Here, obviously, vbox is the parent widget and “Hello, World!” is the text to be displayed by the label.

YUI::widgetFactory()->createPushButton( vbox, "&OK" );

This line is similar to the above. We create a button and set VBox as it’s parent and OK as the button label. The ‘&’ symbol is used to specify keyboard shortcuts. In this case, we get OK as the button label with ‘O’ underlined. This means that the button can be activated by the keyboard shortcut Alt+ (in this case Alt+O).

The VBox arranges all the widgets added to it in a vertical stack, one below another. In our example, this means that the label is placed above the button. If you want to place the widgets side-by-side, you could use a HBox as shown below :

YLayoutBox * hbox = YUI::widgetFactory()->createHBox( dialog );

That’s all the code required to generate the UI. Now, lets take a look at the last two lines of code :

dialog->waitForEvent();
dialog->destroy();

The first line is used to start the main event loop. The event loop can be thought of as an infinite loop where YUI checks for events. An event could be anything from a mouse click to a window close. As soon an an event is detected, the control is passed on from the waitForEvent() function.

In bigger programs, the event loop is used inside an infinite loop. We will take a look at such scenario’s in the upcoming posts. Since this is just an introductory post, let’s keep things simple!

The last line is more or less self explanatory. It is used to destroy the main window (popup dialog) that we created. This, is used to close the window and end the program.

To compile the program, you need g++ compiler and the yui library (libyui.so). Both the packages can be obtained on openSUSE using a simple zypper command.

sudo zypper install gcc-g++ yast2-libyui-devel

To compile, we use :

g++ -I/usr/include/YaST2/yui -lyui HelloWorld.cc -o HelloWorld

Here, we use the -I and -l flags to specify the location of the include directory and the location of the library respectively. Since we are using the YUI framework, -I has the value /usr/include/YaST2/yui by default (unless you changed the location manually). The -l, on the other hand takes the name of the library as an argument. “lib” and “.so” are added as prefix and suffix to the name of the library specified. So, in our case here, it looks for libyui.so. The -o is used to specify the name of the output file and HelloWorld.cc is the input file.

To execute the program, type

./HelloWorld

in the same directory. This would launch the program using Qt interface. To run using the ncurses UI, we need to unset the DISPLAY variable.

unset DISPLAY; ./HelloWorld

OR

DISPLAY= ./HelloWorld

(Note the space)

The last two lines restore DISPLAY variable to it’s previous value. If this is not done, ncurses UI will be used to display the YUI apps till we start a new terminal session.

The gtk interface can be obtained by using –gtk as a command line argument.

./HelloWorld --gtk

Have fun programming with YUI!
And stay tuned for more code…

GsoC 2011

After being an enthusiast about computing (well programming) for so many years, I finally got an opportunity to go beyond the realm of small time programming into the world of open source. Though I am not exactly new to open source, I must say that i have not done anything significant. After nearly 3 years of being a hardcore linux fan, i finally decided to start contributing to open source – through GsoC 2011.

I attended GNOME.Asia summit 2011 in Bangalore earlier this month where i attended a talk by one of my friends (Manu Gupta) on libYUI. Impressed by it’s abilities and it’s potential, I thought that it would make an ideal project to start out with as it was also a GsoC topic (http://www.google-melange.com/gsoc/homepage/google/gsoc2011). And hence, here we are!

I am applying for GSoC, with the http://en.opensuse.org/openSUSE:GSOC_2011_Ideas#Separate_libyui project.

For those of you who do not know what libYUI is, here is a quick intro. libYUI can be thought as an abstraction layer for Qt, GTK and ncurses UI’s. So, a single program written using YUI can produce outputs in 3 different frameworks. Seems great, doesn’t it? But there is a downside to it as well. It was written for the YaST tool (openSUSE) and is heavily coupled along with it. As a consequence, it is available only on the openSUSE platform.

The goal of the project is to separate libYUI from it’s YaST bindings so that it can be an independent framework on it’s own. Moreover, this would allow it to be easily ported to other linux based distributions so that more people can use and benefit from this brilliant framework!

Google has done a great job with GsoC. It is an ideal platform to start off with if open source is your passion. The results would be out later this month, but i have already started working! The yast community is small, but very helpful. Along with help from the community and Martin Vidner (Mentor for libYUI project), quite a bit of work has been done and a lot has been learnt. Over the next few posts, I will post the challenges faced, the hurdles overcome and the progress made.

Current status : Qt and ncurses run on ubuntu!

Follow

Get every new post delivered to your Inbox.