User avatar
flatmax
Posts: 336
Joined: Thu May 26, 2016 10:36 pm

Tutorial 6 - Programming GTK GUIs using gtkIOStream

Wed Feb 08, 2017 8:31 am

Hi again,

gtkIOStream is a set of C++ headers which enable you to make GTK GUI programs in a very trivial manner.

This tutorial 6 will demonstrate how to create GUI C++ classes, how to inherit and use of other GUI objects, and how to handle callbacks in C++ classes.
Tutorial contents can be found here.

Lets begin by making sure that we are in the correct path location before we start, for these tutorials, we assume gtkiostream-1.5.0 is in your home directory. You can either use the command "cd ~" or simply "cd" to get to your home directory.

The location of gtkiostream is in the home directory of the user pi (cd ~ if you aren't there and want to get there). Our current directory (pwd) is /home/pi. Great gtkiostream-1.5.0 is in our current directory and we are ready to start.

In this tutorial we are going to learn how to create a C++ class which inherits a GUI object and manages button callback methods.

Matt
p.s. Please reply to this thread with any bug fixes, questions or help requests.
Last edited by flatmax on Wed Feb 08, 2017 9:22 am, edited 1 time in total.
Check the Ultra 2 sound card - use our shop instead of Amazon Europe (Amazon USA is live).
Sound card for the Raspberry Pi with inbuilt microphone : www.audioinjector.net
Audio Inector Octo multitrack GPIO sound card

User avatar
flatmax
Posts: 336
Joined: Thu May 26, 2016 10:36 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Wed Feb 08, 2017 9:08 am

With C++ classes you can inherit GUI objects which can simplify stuff like not having to re-implement things which already exist. In this tutorial, the C++ class will implement some button callbacks. Almost all of the code/work will shift from the main loop and into a C++ class.

Lets begin by saving the following code to the file ClassWithButton.C :

Code: Select all

#include <gtkInterface.H>
#include <Buttons.H>

/* Create a class which inherits a horizontal box and                           
   Declares a button which when clicked will print it's own label.              
 */
class MemberCallback : public HBox {
  Buttons buttons; // The buttons member variable                               

  /** Define the quit callback method                                           
      \param wid The button widget which was pressed to signal the execution of this function.                                                                 
      \param data The user data you asked this callback to be given.            
  */
  static void quit(void *wid, gpointer data){
    gtk_main_quit(); // quit the GTK main loop                                  
  }

  /** Define the print label button callback method                             
      \param wid The button widget which was pressed to signal the execution of this function.                                                                 
      \param data The user data you asked this callback to be given.            
  */
  static void printLabelStatic(void *wid, gpointer data){
    MemberCallback *mc= static_cast<MemberCallback*>(data); // convert from void* back to the MemberCallback *                                                 
    mc->printLabel(); // print the button label                                 
  }

public:
  // Constructor                                                                
  MemberCallback(){
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable                                   
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt         

    *this<<buttons; // Add the button to the inherited HBox                     
    show(); // show the inherited HBox                                          
  }

  // Print the button's label to console                                        
  void printLabel(){
    cout<<buttons.getLabel()<<endl; // print the label to console               
  }
};

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

  gtk_init( &argc, &argv ); // init GTK                                         

  GtkInterface topWindow; // Create the top box                                 
  MemberCallback memberCallback; // instantiate our class                       
  topWindow<<memberCallback; // add the memberCallback::HBox to the top window.

  gtk_main(); // run GTK

  return 0;
}
We will start by looking at the simple main loop.

The main loop does the usual things, like initalise GTK, instantiate the GtkInterface top window and enter the GTK main loop. The only other thing it does is to instantiate the new C++ class we are going to implement (MemberCallback) and add it to the top window. These are implemented like so in the main function :

Code: Select all

  MemberCallback memberCallback;
  topWindow<<memberCallback;
The MemberCallback class inherits an HBox class and for that reason it can be loaded into the top window widget with the "<<" operator. Lets take a look at this MemberCallback C++ class.

The new class MemberCallback is defined as follows :

Code: Select all

class MemberCallback : public HBox {
The ": public HBox" is the part which inherits the HBox object (which has the horizontal packing back GTK widget in it).

A private member varible is defined to hold the buttons we are going to instantiate in the MemberCallback constructor. That is why the top of the class instantiates the buttons private member variable :

Code: Select all

class MemberCallback : public HBox {
  Buttons buttons;
If you don't know what I am talking about w.r.t. "private" check here for a little bit of information. You know that the member variable is a private part of the class because it immediately follows the class opening line without another "protected" or "public" specifier before.

We are going to create two buttons, one for quitting the program and another for printing a buttons label to console. We have seen the quit function before, however this time, the same function appears as a private method of the MemberCallback class.

Before we talk about the callbacks, we will look at the class constructor. Class constructors are run when you instantiate the classes in the main program, other functions or other classes. In our case the MemberCallback class's constructor looks like so :

Code: Select all

  MemberCallback(){
    buttons<<LabelFuncData("print me", printLabelStatic, this);
    buttons<<LabelFuncData("Quit", quit, NULL);

    *this<<buttons;
    show();
  }
The first thing is that we create two buttons. The quit button you have seen before, however the "print me" button you haven't so lets discuss that one first.

The "print me" button is created with a button label "print me" , told to execute the printLabelStatic method once clicked and "this" is passed to the callback function as the user data input. If you have no idea what the "this" variable is, you can read about it here. You can think of "this" as saying, hey computer, give me a pointer to the object I am currently in. As you can imagine this pointer is a very powerful way of recalling an object somewhere very remote - which is exactly why we need it. You see, static methods (such as printLabelStatic) don't know which object they belong to, so by passing that method "this" it will be able to route its way home :)

We then want to add the buttons to the inherited HBox class, which we again use this for. We point ourselves to our own object (*this) - and load the buttons into ourselves (the HBox). It looks like so :

Code: Select all

*this<<buttons;
Another way to do the same thing is to call the "<<" operator explicitly, we do that by writing operator<<(buttons).

Finally the constructor wants its inherited HBox to be visible so it calls show :

Code: Select all

    show();
C++ magically finds the show() method is inherited from the HBox and executes that, even though we don't tell it where to find the method.

At this point in execution (after instantiating the memberCallback variable in the main program) all of our buttons and packing boxes are ready to go. Once we start the gtk_main loop and press the buttons, the callbacks are executed.

Lets imagine that we have clicked the "print me" button. We know it will execute the printLabelStatic method which we are calling a callback.

Lets now look at the new callback printLabelStatic. This callback receives the pointer to the object who's button was clicked (the "this" we discussed before) it also receives the widget ID who's event was signalled by clicking. Right now we aren't interested in that widget, because we would rather use the Buttons class's methods to find detail about the widget. The printLabelStatic method looks like so :

Code: Select all

  static void printLabelStatic(void *wid, gpointer data){
    MemberCallback *mc= static_cast<MemberCallback*>(data);
    mc->printLabel();
  }
The first thing it does is to convert the void* (gpointer) which is our object pointer back into the object. This is done by using a "static_cast" function which casts the void* back into the MemberCallback object. If you haven't heard about casting before, perhaps you want to read here. Now that we have our object back (the variable mc) we can call the public method which will print our label called printLabel.

The printLabel method is public because it is declared in our class after the "public:" decleration line. We could have moved it up in the class and written it before the public decleration, that would have made it private. The printLabel method looks like so :

Code: Select all

  void printLabel(){
    cout<<buttons.getLabel()<<endl;
  }
All that this method does it get the button's label using the "getLabel()" buttons method and outputs that to console.

At this point in the execution, we have printed the button's label to console. We did this by first routing our way back to the object in which the button originated. It may seem the long way around now, but once your GUI classes become sophisticated, you can do all sorts of nice things in these callback methods which interact between different GUI widgets and quite possibly inherited classes as well.

This now completes the tutorial. We have covered the inheritance of other GUI objects, and how to handle callbacks in C++ classes.

We will continue to compile and demonstrate the program.
Check the Ultra 2 sound card - use our shop instead of Amazon Europe (Amazon USA is live).
Sound card for the Raspberry Pi with inbuilt microphone : www.audioinjector.net
Audio Inector Octo multitrack GPIO sound card

User avatar
flatmax
Posts: 336
Joined: Thu May 26, 2016 10:36 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Wed Feb 08, 2017 9:28 am

To compile this tutorial we run the following command :

Code: Select all

g++ ClassWithButton.C -o ClassWithButton `pkg-config gtk+-2.0 --cflags --libs` -Igtkiostream-1.5.0/include
We execute it like so :

Code: Select all

./ClassWithButton
When we click on the "print me" button, the following is printed out (the button's label) :

Code: Select all

print me
The GUI looks like this :
ClassWithButton.png
Tutorial 6 : gtkIOStream
ClassWithButton.png (5.58 KiB) Viewed 3334 times
Hope that you are enjoying these tutorials.
Matt
Check the Ultra 2 sound card - use our shop instead of Amazon Europe (Amazon USA is live).
Sound card for the Raspberry Pi with inbuilt microphone : www.audioinjector.net
Audio Inector Octo multitrack GPIO sound card

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Mon May 15, 2017 3:45 pm

and last, but not least,
also in that case my window looks different (functionality works though!) :
gtk2Btns_L6.jpg
gtk2Btns_L6.jpg (5.79 KiB) Viewed 2617 times

Code: Select all

// gtk2Btns_L6.c

#include <gtkInterface.H>
#include <Buttons.H>

/* Create a class which inherits a horizontal box and                           
   Declares a button which when clicked will print it's own label.             
 */
class MemberCallback : public HBox {
  Buttons buttons; // The buttons member variable                               

  /** Define the quit callback method                                           
      \param wid The button widget which was pressed to signal the execution of this function.                                                                 
      \param data The user data you asked this callback to be given.           
  */
  static void quit(void *wid, gpointer data){
    gtk_main_quit(); // quit the GTK main loop                                 
  }

  /** Define the print label button callback method                             
      \param wid The button widget which was pressed to signal the execution of this function.                                                                 
      \param data The user data you asked this callback to be given.           
  */
  static void printLabelStatic(void *wid, gpointer data){
    MemberCallback *mc= static_cast<MemberCallback*>(data); // convert from void* back to the MemberCallback *                                                 
    mc->printLabel(); // print the button label                                 
  }

public:
  // Constructor                                                               
  MemberCallback(){
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable                                   
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt         

    *this<<buttons; // Add the button to the inherited HBox                     
    show(); // show the inherited HBox                                         
  }

  // Print the button's label to console                                       
  void printLabel(){
    cout<<buttons.getLabel()<<endl; // print the label to console               
  }
};

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

  gtk_init( &argc, &argv ); // init GTK                                         

  GtkInterface topWindow; // Create the top box                                 
  MemberCallback memberCallback; // instantiate our class                       
  topWindow<<memberCallback; // add the memberCallback::HBox to the top window.

  gtk_main(); // run GTK

  return 0;
}


1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 6:20 am

update:
after Paeryn'c changes in viewtopic.php?f=67&t=173446#p1163076 it's still the same,
both buttons closely side to side each other, "print me" about twice as wide as "quit" (according to label string length), much empty space at the right side, so still exactly as shown here in comparison:

Image

Image

User avatar
Paeryn
Posts: 2635
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 11:54 am

1dot0 wrote:update:
after Paeryn'c changes in viewtopic.php?f=67&t=173446#p1163076 it's still the same,
both buttons closely side to side each other, "print me" about twice as wide as "quit" (according to label string length), much empty space at the right side, so still exactly as shown here in comparison:

Image

Image
The Buttons MemberCallback class inherits from HBox so in flatmax's image it is showing with the HBox's expand=true, fill=true but again those would have be random values and not (what looks to be the intended) default of both being false.
Last edited by Paeryn on Tue May 16, 2017 1:47 pm, edited 2 times in total.
She who travels light — forgot something.

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 12:14 pm

The Buttons class inherits from HBox so in flatmax's image it is showing with the HBox's expand=true, fill=true but again those would have be random values and not (what looks to be the intended) default of both being false.
thanks again -
so that would mean now that flatmax will have to fix that issue to make it work like in his own screenshot, and not randomly of course, just like the other thing about the 2 + 3-liner labels?

User avatar
Paeryn
Posts: 2635
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 2:47 pm

You can get the same result as flatmax was showing by altering the line at the end of MemberCallback's constructor so that it sets the parent HBox's expand and fill

Code: Select all

  // Constructor                                                               
  MemberCallback(){
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt

    *this<< EXPAND << FILL << buttons; // Add the button to the inherited HBox (with expand & fill set)
    show(); // show the inherited HBox
  }
Or call setDefaultExpand() & setDefaultFill() at the start of the constructor

Code: Select all

  // Constructor                                                               
  MemberCallback(){
    // Set expand and fill to true
    setDefaultExpand(true);
    setDefaultFill(true);
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable                                   
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt         

    *this<<buttons; // Add the button to the inherited HBox
    show(); // show the inherited HBox                                         
  }
She who travels light — forgot something.

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 3:53 pm

Paeryn wrote:You can get the same result as flatmax was showing by altering the line at the end of MemberCallback's constructor so that it sets the parent HBox's expand and fill
(...)
Or call setDefaultExpand() & setDefaultFill() at the start of the constructor

Code: Select all

  // Constructor                                                               
  MemberCallback(){
    // Set expand and fill to true
    setDefaultExpand(true);
    setDefaultFill(true);
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable                                   
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt         

    *this<<buttons; // Add the button to the inherited HBox
    show(); // show the inherited HBox                                         
  }

hmmh - must find the correct place to insert

Code: Select all

setDefaultExpand(true);
setDefaultFill(true);
it's quite confusing...

here, beyond this "public" thing ... :?:

Code: Select all

public:
  // Constructor                                                               
  MemberCallback(){

Code: Select all

setDefaultExpand(true);
setDefaultFill(true);

Code: Select all

    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable   
:?:

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 4:00 pm

at that point, with all this class and constructor and public and whatever it's already at a point when I actually understand nothing at all any more :-/
Last edited by 1dot0 on Tue May 16, 2017 4:01 pm, edited 1 time in total.

User avatar
Paeryn
Posts: 2635
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 4:01 pm

1dot0 wrote:
Paeryn wrote:You can get the same result as flatmax was showing by altering the line at the end of MemberCallback's constructor so that it sets the parent HBox's expand and fill
(...)
Or call setDefaultExpand() & setDefaultFill() at the start of the constructor

Code: Select all

  // Constructor                                                               
  MemberCallback(){
    // Set expand and fill to true
    setDefaultExpand(true);
    setDefaultFill(true);
    buttons<<LabelFuncData("print me", printLabelStatic, this); // Create a string button passing this class as the variable                                   
    buttons<<LabelFuncData("Quit", quit, NULL); // Create a string butt         

    *this<<buttons; // Add the button to the inherited HBox
    show(); // show the inherited HBox                                         
  }

hmmh - must find the correct place to insert

Code: Select all

setDefaultExpand(true);
setDefaultFill(true);
As I showed, in the constructor, as long as it is before the line that adds the buttons to the HBox *this<<buttons;
Using the setDefault* calls both sets the relevant default and current values whereas just putting EXPAND or FILL in the stream just alters the current value of those settings and not the default values.
She who travels light — forgot something.

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 4:08 pm

yes, thank you, I think we both mean the same place.
My problem is that I have no idea what a constructor is and what this public thing means, so I must take this bunch of letters just as sort of a encrypted Egyptian, Mayan, or Babylonian black box :-/

I'll test it ASAP, what's left is the 2- or 3-liner label thing then, before I'll finally have to figure out how to display a text field label with 100 quickly changing (global) variable values.
And then a canvas where the map with the driving robot's route is pictured, according to odometric data, point by point for current position and lines for detected actual obstacles.

User avatar
Paeryn
Posts: 2635
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 4:37 pm

1dot0 wrote:yes, thank you, I think we both mean the same place.
My problem is that I have no idea what a constructor is and what this public thing means, so I must take this bunch of letters just as sort of a encrypted Egyptian, Mayan, or Babylonian black box :-/
A constructor is a function of a class that gets called when a variable of that class is created. Constructors have the special property that when defining them you don't give them a return type and they have the same name as the class.

Public means that the members are accessible by anyone (just like they are in a struct), private means that they are only accessible by the class itself. Then there's protected that means they are only accessible by the class and any class that inherits from it. Classes start out private by default and you can change which members have which accessibility with the keywords public: private: and protected:, any member after either of those keywords will have that accessibility.

Welcome to C++, it's a complicated beast :)
1dot0 wrote:I'll test it ASAP, what's left is the 2- or 3-liner label thing then, before I'll finally have to figure out how to display a text field label with 100 quickly changing variable values.
And then a canvas where the map with the driving robot route is pictured, according to odometric data, point by point for current position and lines for detected actual obstacles.
You can do a similar thing in the others, either call those functions on the box objects or use the other method of putting vBox << EXPAND << FILL << labels.
She who travels light — forgot something.

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Tutorial 6 - Programming GTK GUIs using gtkIOStream

Tue May 16, 2017 5:04 pm

yep,
the bottons expand now - surprisingly not by half the windows width each, as I just observed, but exactly as shon in Matt's screenshot though. 8-)

But the labels from Tut. 4 I cannot get expanded yet, unfortunately. :-/

Return to “Graphics programming”