Beginner
fltk_homepageTutorial
version 1.0
Index
Who is this website for?
Prerequisite
Why use FLTK?
Getting the Software
Goto FLTK Basics
Flash video !  (New FLUID video Feb 2/04)
Simple Window Function
Simple Window with widgets that talk to each other
Simple Inherited Window
More sections to come in the future
About me




Who is this website for?

This website is for C++ programmers who wish to start coding GUI (Graphical User Interface) applications using FLTK.  The documentation of FLTK is very good. However, I found the tutorial examples went from very easy (Hello World) to a little too diffucult (editor.cxx). So this site is intended as a follow up to  FLTK Basics.

Enjoy!



Prerequisite:

I assume you already have somewhat of a foundation in C++ programming. Most importantly you need to understand classes, inheritance, pointers and dynamic memory allocation before you can attempt to learn GUI programming.

Here are some C++ tutorial sites:

Thinking In C++ 2nd Edition by Bruce Eckel (Free Online Book)
http://cplus.about.com/library/blcplustut.htm
http://www.cplusplus.com/doc/tutorial/

top


Why use FLTK as opposed to other GUI toolkits?


- it's free open source software under GNU LGPL with some exceptions.
  See the FLTK license agreement for more details
- it's efficient and straightforward
- it uses C++
- it makes small statically linked stand alone executables
- it's cross platform with Linux/Unix, Windows and MacOSX
- it supports OpenGL
- it has a graphical user interface builder called FLUID
- version 1.2 will have printing and UTF-8 support
- it's fun and easy to learn
top


Getting the Software:

Linux Install:

- Download FLTK software from www.fltk.org  (get version 1.1.4)
- Install it with the following commands in a console
        tar -xvzf fltk-1.1.4-source.tar.gz
        cd fltk-1.1.4
       ./configure
       make
       #make install

Note: if you download the .tar.bz2  file use   -xvjf   instead of  -xvzf
Linux has lots and lots of wonderful text editors you can use. I personally like gedit and nedit.


                   =============================================


Windows Install:


Option 1

-Get the latest version of (Dev-C++ with MinGW) from http://www.bloodshed.net
-Then  use the update feature to get the FLTK Devpak
-Don't get Devpak version 2.0 as this tutorial is about 1.1.4 and eventually 1.2
-If the update servers don't have it up then download it from these sourceforge servers
-Then create a new FLTK gui project. You should automatically have the "Hello World" code.



Option 2

- Download FLTK software from www.fltk.org  (get version 1.1.4)
- Get the latest MinGW, MSYS and msysDTK executable install files from sourceforge

1)  MinGW-3.x.x-x.exe  (32 bit Windows)
2)  MSYS-1.x.x.exe         (32 bit Windows)
3)  msysDTK-1.x.x.exe  (32 bit Windows)

- Follow the MSYS post install script instructions carefully!
- Install them in the same order and then launch MSYS and follow the Linux install instructions above
                  Linux newbies: Don't type the "#" in front of "make install"
- Use your favorite editor to write code and compile from the MSYS console 
- If you don't have a good Windows editor then you can use Dev-C++ without MinGW
- Note: MSYS comes with Vim for all you vi addicts out there! Newbies beware: Vim text editor takes time to learn. But once people spend the time to learn it, they seem to really like it. I think it's an acquired taste.

Option 1 is easier but I personally recommend Option 2 for a couple of good reasons. Firstly, you will learn to compile things by hand from a shell console. This might encourage you to learn console commands and maybe even makefiles.  Secondly, you will also get an entire directory full of example demo programs using most, if not all, FLTK widgets. This is the /test directory, which you will NOT get from the Devpak in Option 1.

top



Goto FLTK Basics

I don't want to duplicate too much stuff from the FLTK documentation, so at this point goto the above link. Read the section and come back when your finished. See you soon.
top


Flash Video !

This section has a 13 minute Flash video you can watch using any browser with a shockwave flash plug-in. I have tried it with Mozilla, Galeon, Konqueror, Opera and IE browsers. The author of the video is Greg Ercolano. I have his permission to mirror the video. He has also contributed Fl_Table widget to the project and is an active member of the FLTK community. I experienced a real WOW! factor when I first saw it. Hope you do too.

Get the video fltk_video.zip (6.7 M) contains 4 files

Greg Ercolano has made another great flash video. This time on FLUID (FLTK User Interface Designer)
Check out both videos here.

Note: the first video is zipped because I would prefer you download it once and have the ability to watch it multiple times from your own hard drive. Instead of streaming it from this site everytime. Unzip the file and its contents then open tutorial-fltk-hello.php with your browser. All 4 files must be in the same directory.
Linux users may have to run the browser as root OR change the permissions of /dev/dsp0 to be able to hear sound.
( #chmod 755 /dev/dsp0 )
I have Slackware 9.1 and I got sound by running the browser from a root console.

top


Simple Window Function

Okay time to look at some code. Let's look at a way to make a window and a simple button. You should be able to copy/paste the code into an editor, save it then compile it in a console with the convenient script

fltk-config  --compile  myprogram.cpp 

where myprogram.cpp is the name of the file.  fltk-config script is installed with FLTK.


#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
using namespace std;

void but_cb( Fl_Widget*  , void* );  //callback function prototype

//------------------------------------------
void make_window() {
  
   Fl_Window* win= new Fl_Window(300,200, "Testing");
   win->begin(); 
      Fl_Button* but = new Fl_Button( 10, 150, 70, 30, "Click me");
   win->end();
   but -> callback( (Fl_Callback*) but_cb );
   win->show();
}

//--------------------------------------------
void but_cb( Fl_Widget* o , void* ) {

   o->label("Good job"); //redraw not necessary
  
   o->resize(10,150,140,30); //redraw needed
   o->redraw();
}

//--------------------------------------------  
int main() {

   make_window();
   return Fl::run();
}



Let's analyse this code

   Fl_Window* win= new Fl_Window(300,200, "Testing");
creates a pointer to a new window object setting the width, height and the name in the title bar. Note: this object is allocated from heap memory.

   win->begin()
this line is optional but I recommend using it as it makes the code easier to read. It's purpose is to say whatever new widgets that are created will be added as children to the current Fl_Window that was just created. Until you reach win->end(). Don't forget the ->end();

    Fl_Button* but = new Fl_Button( 10, 150, 70, 30, "Click me");
this line creates a button with  input parameters (x, y, width, height, label) where x, y are the position in pixels relative to the top left corner (0,0). Note: this button is now a child of the parent window. This means that the button is the first child of the window with index 0.  See the Fl_Group for more info on children. Also, note that only objects derived from Fl_Group have this child parent relationship. Fortunately, Fl_Window is derived from Fl_Group. A BIG benefit of this structure is that you only need to destroy the Fl_Window (the parent). Doing so will automatically delete all the children of the window.  So if we delete 'win', then 'but' will be deleted automatically. This is a very beautiful aspect of FLTK.

   win->end();
this line sets the current group to the parent of the window (which in this case is null since the window has no parent)

   but -> callback( (Fl_Callback*) but_cb );
This line is important. Callbacks are a means of executing code when an event occurs. This is the basis for GUI programming. Usually an event is a mouse click, return key pressed etc.. There is more to be said about events but I won't go into it here. However, for this example we wish to change some of the properties of the the button when a user clicks on it.

First let's look at the prototype of the callback member function of the Fl_Widget class:
Remember 'but' points to an Fl_Button which is derived from Fl_Widget so it can use it's base classes callback member function .


void Fl_Widget::callback(Fl_Callback*, void* = 0)

Notice the second parameter is optional. That's why our example works. The second parameter (void*) is for userdata (any data structure you wish to pass that the callback code might need).  'but_cb' is the name of our callback function. BTW  it's good convention, for code readability, to end or start callback function names with 'cb'.  It must accept an Fl_Widget* and a void*. Also, we cast the function name (which is a pointer) to an Fl_Callback* type to satisfy the Fl_Widget::callback input parameter. Therefore, the Fl_Widget*  which gets passed in this case is the button pointer 'but'. More about userdata later.

  win->show();
This line puts the window on the screen. In other words, it makes it visible.

  void but_cb( Fl_Widget* o , void* ) {
This line just declares our callback function. Note the missing void* variable. We don't need it here, since we passed null by default.

   o->label("Good job");
This line changes the displayed label of the button. Note: label() and value() are the only two widget members that automatically call redraw. Everything else requires a manual call of redraw(). As you will see in the next two lines.

   o->resize(10,150,140,30);
This line resizes the button. The parameters indicate the button keeps it's position but the width is doubled from 70 to 140. What's important is that this member function will NOT redraw the widget. So you will not see the change unless redraw() is called. Hence, the next line.
     
    o->redraw();
This line redraws the widget. Needed for the resize().

   return Fl::run();
This line is part of most GUI toolkits. It sends the program into the main event loop. In other words, the program waits for events to happen.  The function run() returns 0 and ends the program when all windows are closed or hidden.  Another quick and easy way to end the program is to call exit(0). More on this later.



Simple Window with widgets that talk to each other

One of the main reasons for this tutorial was that I wanted to show how widgets (buttons, input boxes, output boxes, etc..)  can communicate with each other in a window. You might say, "what are you talking about ?"  Well,  most of the examples available simply instantiate (create) a window object in a function and add some widgets to it. But this makes it a little difficult to send information from one widget to another. First off, in order to communicate we need at least two widgets. Take a look at the following program it's like the previous one with some things added. Again try compiling it yourself.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <cstdlib>                   //for exit(0)
using namespace std;

void copy_cb( Fl_Widget* , void* );  //function prototypes
void close_cb( Fl_Widget* , void* );
void make_window();

   
int main() {

   make_window();
   return Fl::run();
}


void make_window() {
  
   Fl_Window* win= new Fl_Window(300,200, "Testing 2");
   win->begin();       
      Fl_Button*  copy = new Fl_Button( 10, 150, 70, 30, "C&opy"); //child 0
      Fl_Button* close = new Fl_Button(100, 150, 70, 30, "&Quit"); //child 1
      Fl_Input*       inp = new Fl_Input(50, 50, 140, 30, "In");              //child 2
      Fl_Output*    out = new Fl_Output(50, 100, 140, 30, "Out");     //child 3
   win->end();
   copy->callback( (Fl_Callback*) copy_cb );
   close->callback( (Fl_Callback*) close_cb);
   win->show();
 }


void copy_cb( Fl_Widget* o , void* ) {

   Fl_Button* b=(Fl_Button*)o;
   const char* temp;  
   temp = (  (Fl_Input*)(b->parent()->child(2))  )->value();
   (  (Fl_Output*)(b->parent()->child(3))  )->value(temp);
}


void close_cb( Fl_Widget* o, void*) {

   exit(0);
}


----------------------------------------------------------------------------

When run this program should look like this

simple win 2



Notice the Copy and Quit have underlined letters. These buttons can be invoked by pressing ALT-c  and ALT-q. This was achieved by simply placing an "&" in front of the letter in the label parameter in the constructor.

This program just copies whatever is in "In" to "Out" when "Copy" is pressed. The purpose though is to show communicating widgets.  Let's look at the copy_cb callback function.

void copy_cb( Fl_Widget* o , void* ) {
   Fl_Button* b=(Fl_Button*)o;

Notice 'o' is an Fl_Widget input parameter but 'copy_cb' is an Fl_Button callback function, so 'o' refers to an Fl_Button.  Therefore, we declare an Fl_Button pointer called 'b' and cast 'o' to it.  Done.

Next comes the ugly looking communication.

   temp = (  (Fl_Input*)(b ->parent()->child(2))  )->value();
   (  (Fl_Output*)(b ->parent()->child(3))  )->value(temp);


Get the parent() of Fl_Button b which returns an Fl_Group*. That's good because child(2) is a member function of an Fl_Group, so no cast is required. Then child(2) returns an Fl_Widget* of the 2nd widget created.  But the 2nd child is an Fl_Input, so we need a cast. Once the cast is done we can THEN call value() which returns the const char* of the text typed in by the user. Then this const char* gets assigned to a temporary variable temp.

The next line just sets the temp variable to the value of the Fl_Output widget out  in the same fashion as the line above. 

I must warn you that this is not a good way to do widget communication. For one thing it's ugly and hard to read. Second you must manually keep track of the widget indices (ie 0,1,2,3 etc..) Third, there is no range checking on the child(int n). So if this method of communication sucks, how do we do it???

Answer: Use C++ to make a wrapper class. See next section.



Simple Inherited Window

This next program produces the exact same GUI interface as before but it's coded completely different. I recommend this method. You will see why.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <cstdlib>       // for exit(0)
using namespace std;

//---------------------------------------------------
 
class SimpleWindow : public Fl_Window{
 
   public:
      SimpleWindow(int w, int h, const char* title );
      ~SimpleWindow();
      Fl_Button* copy;
      Fl_Button* quit;
      Fl_Input*    inp;
      Fl_Output* out;
    
   private:
      static void cb_copy(Fl_Button*, void*);
      inline void cb_copy_i(Fl_Button*, void*);
  
      static void cb_quit(Fl_Button*, void*);
      inline void cb_quit_i(Fl_Button*, void*);
};

//----------------------------------------------------

int main (){
  
   SimpleWindow win(300,200,"Testing 3");
   return Fl::run();
}

//----------------------------------------------------

SimpleWindow::SimpleWindow(int w, int h, const char* title):Fl_Window(w,h,title){
    
   begin();
      copy = new Fl_Button( 10, 150, 70, 30, "C&opy");
      copy->callback((Fl_Callback*)cb_copy, this);
     
      quit = new Fl_Button(100, 150, 70, 30, "&Quit");
      quit->callback((Fl_Callback*)cb_quit, this);
   
      inp = new Fl_Input(50, 50, 140, 30, "Input:");
      out = new Fl_Output(50, 100, 140, 30, "Output:");
   end();
   resizable(this);
   show();
}

//----------------------------------------------------

SimpleWindow::~SimpleWindow(){}

//----------------------------------------------------

void SimpleWindow::cb_copy(Fl_Button* o, void* v) { 
 
   SimpleWindow* T=(SimpleWindow*)v;
   T->cb_copy_i(o,v);
}

//---------------------------------------------------

void SimpleWindow::cb_copy_i(Fl_Button* , void*) {

   out->value(inp->value());  
}

//----------------------------------------------------

void SimpleWindow::cb_quit(Fl_Button*o , void* v) {

   SimpleWindow* T=(SimpleWindow*)v;
   T->cb_quit_i(o,v);
}

//----------------------------------------------------

void SimpleWindow::cb_quit_i(Fl_Button* , void*) {

   exit(0);  // or hide();
}

//----------------------------------------------------


Okay let's analyse this improved version.

The first thing you will notice is that I have created a class called SimpleWindow which is derived from an Fl_Window.  However, I have added public pointer members of all the widgets I want to add to my window. Since these pointers are public I can access them outside of the class if I need to. Now lets look at how callbacks are done in classes.

private:

   static void cb_copy(Fl_Button*, void*);

   inline void cb_copy_i(Fl_Button*, void*);


//----------------------------------------------------------------
void SimpleWindow::cb_copy(Fl_Button* o, void* v) { 
 
   SimpleWindow* T=(SimpleWindow*)v;
   T->cb_copy_i(o,v);
}


void SimpleWindow::cb_copy_i(Fl_Button* , void*) {

   out->value(inp->value());  
//notice how clean this communication is compared to the last section
}

//----------------------------------------------------------------

These two functions are important. They go together. They are the method for having member function callbacks. First I would like to say that callbacks in a class can only be static. In other words the THIS pointer does not exist. So the way to overcome this is by having two (yes that's right) functions. Where the static function calls a second member function of the class that DOES have the THIS pointer initialized. So you can access all data members and member functions of the class with the implicit THIS pointer. Now look here. If you think this is weird, let me remind you that FLTK does not use a precompiler as other toolkits do. Plus if you really only want to have one function, you can.  For example: the static cb_copy callback function could have been

void SimpleWindow::cb_copy(Fl_Button* o, void* v) { 
 
   SimpleWindow* thz = (SimpleWindow*)v;
   thz->out->value(thz->inp->value());

}

But this makes things more difficult to read with all the -> operators. Plus, don't forget, since the second function is inlined there really is only one function call. So there is no penalty for making two functions. Note the second function has an '_i' added to the end of it to denote that it's inlined.

Get/Set methods
One aspect of FLTK which might take some getting use to is the get/set functions. They both have the same name but are overloaded with respect to their return and input parameters. Get functions have no input parameter, so inp->value() gets or returns the value of the widget (a const char* in this case). On the other hand, out->value(const char*) sets the value of the out object. Once I got used to reading code this way I found it to be cleaner.  Also, compare how clean and efficient this sinlge line of communication is compared to the two ugly lines in the previous example. No more messy counting children or casting. This is a consequence of making the wrapper class.  Simple, clean, efficient.

Also notice I don't have to put a pointer in front of begin() or end() or show() etc... as the THIS pointer is implicit.

   copy->callback((Fl_Callback*)cb_copy, this);
Now this line utilizes the userdata void* I was talking about before. In this case, as in most, I pass the address of the class instance (the THIS pointer). Therefore, I have access to the entire class in the callback!! You gotta love it. Just in case you forgot about void* in C++, here is a little refresher.

Void Pointers:
Some of you that are new to C++ may have not seen void pointers before.  Basically, a void* is a pointer that can point to anything. Usually pointers are typed, in other words, you know the type of data to which they point. But a void* has no type. We do some casting in the callback function to deal with this issue.

From another perspective, pointers usually know the size of the object to which they are pointing. But void pointers don't, they just contain the address. Therefore, one can never dereference a void*. Therefore, we must utilize some casting to do the job.


   resizable(this);
This line allows the program window to be resized. However, I could have just as easily called  resizable(copy) which would make the window resizable also. The difference being that my copy button would have been the widget to resize both horizontally and vertically.  Whereas, in my case the whole window gets resized. Remember only one widget per group can be resizable. So if you want a certain layout behavior you need to add appropriate horizontal and vertical groups. I still need practice with this stuff myself so I will just quote an FLTK veteran. This is copied from message 1375 Dated 17 Jan 2004 from fltk general newsgroup.

Marc R.J. Brevoort wrote:

Here are a few hints. Read them carefully then try again. Good luck!

- to make things more predictable, it helps to fill groups with
  widgets only in one direction: either horizontally or vertically.
  (this also helps explain the following hints).

- If you need to fill groups both horizontally and vertically,
  fill a group WITH GROUPS in one direction, then those 'sub'groups
  in the other direction.

- In a group, at most one widget can be set to "resizable".
  Attempts to setting several widgets to 'resizable' causes
  only the last one to be marked resizable.

- Setting a widget to "resizable" means that that widget can be
  resized BOTH horizontally and vertically, not that it is the
  only resizable widget in the group.

- all other widgets in the group may resize along proportionally to
  the size of the group, but only in one direction (if the group
  is populated horizontally, 'nonresizable' widgets only resize
  vertically, only the 'resizable' widget resizes in both directions.

- if a group is only resizable in one direction, only the resizable
  widget will resize, all other widgets will stay the way they are.

Hope this helps,

grtz
MRJB


Thank you Marc. I have printed this message and included it with my FLTK documentation. It's a keeper.

Finally the last line to analyse is

  
exit(0);  // or hide();

You can quit your programs in one of two ways. One way is to call exit(0) and depend on your operating system to free all allocated memory. The other is to call hide() on all windows which will cause Fl::run() to return and destructors to be called normally. One imporatant thing to keep in mind though is that if you have global objects then using hide() may not be a good idea as those objects will not have their destructors called since they were not created in the main function scope.  But I personally don't like declaring objects globally anyway. Note: calling exit(0) will not return Fl::run() but all memory WILL be freed. Again be aware that I don't delete any of the dynamic objects created in the class constructor, in the destructor. This is because SimpleWindow is a sub-class of an Fl_Group and as such it has all of it's children destroyed by the base class destructor. This is possible since the base class has the ability to iterate through all the children with child(int n) and children(). Who needs Java when you've got FLTK. ; )

Side note: I would like to post a bit of code Jason Bryan came up with on FLTK general newsgroup.

void fl_exit()
{
  while( Fl::first_window() )
    Fl::first_window()->hide();
}

This little function should make sure that all windows hide, therebye, ensuring that Fl::run() is returned and all destructors called properly. Thanks Jason.


More sections to come in the future

As I learn more and more about FLTK I will add new sections to this site. I have plans to learn layout/resizing stuff. Also, I could do a section on menus but I held off because 1.2 is going to use a new and improved menuing system (which I haven't learned yet). I was also thinking about a little section on makefiles.  So come back once in a while to see if anything new is up.




About Me

I teach senior high school Physics and Computer Programming (C++ of course) in British Columbia, Canada. Programming is my hobby and passion. I discovered FLTK in 2003 and I am really happy that Open Source/Free Software like this exists. FLTK has opened up a whole new world of programming possibilities.  I would like to thank the FLTK general newsgroup usual suspects like Bill, Mike, Matt, Greg, Jason, Marc, Alexey and Dejan. Sorry if I left anyone out. You guys are what keeps FLTK alive and well.  BRAVO!

Also please feel free to post comments, suggestions or rate this site on the FLTK links/bazaar tutorial page that links to this site. Or if you would like to send me an email. Send it to

fltk_beginner_tutorial@yahoo.com

Robert Arkiletian


no epatents    w3c_html





Alojamiento con 500GB de espacio y 5000GB de ancho de banda por 4 euros al mes
Cómo contratar servicios de hosting con Dreamhost
Descuento, promocode para dreamhost de $50