GIDForums  

Go Back   GIDForums > Computer Programming Forums > FLTK Forum
User Name
Password
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
 
Thread Tools Search this Thread Rate Thread
  #1  
Old 30-Jan-2005, 14:40
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough

Fluid, classes and callbacks


I post this here in the hopes of finding someone who has done this in fluid and can expain show it a little better than the docs do.

After fighting with this long enough to qualify as foolish I must "Ask the Experts" in hopes of ending my pain.

To start:
FLTK 1.1.6 (built from cvs) and running under cygwin

What I am trying to figure out is how to have access to a class's members from a callback. After reading through the docs about the callbacks and the CubeViewUI section of the fluid chapter I can't seem to get at my class members. I will give a stripped down example below. If I can do it for one I can do it for many after all.

The GIM project has some rules defined to make long term growth a learning experience as well as a way to show people how to do things they didn't think they could do with fluid. Having a fluid project file will make it easier for someone with only a small amount of hand coding from scratch in FLTK experience (like myself) have the abiltiy to customize an application to their own needs. I can define the class and it's members by building everything in fluid I am just hung up on the callbacks. Perhaps someone can give me a basis to work from.

Code:
#include (for the back end data) #include (FL/ stuff not added by fluid like fl_ask.H) class MyClass{ bool modified foo_db* current (struct defined at data side corresponding to GUI elements char foo_name[200] (file/db name) Foo() Foo_Window Foo_Menu_Bar Foo_Menu_Submenu Foo_Menu_Item - open (cb_open) Foo_Group Foo_Input first_name Foo_Output file_name strcpy(foo_name,"unnamed") modified = false current = foo_malloc() create space for the struct ~Foo() foo_free(current) free the malloc'd mem foo_delete() foo_members() foo_mo_members() }; end of Foo cb_open(Fl_Widget* caller, void* data) check if modified flag is true if yes prompt save *current (default with Foo::file_name) if successful modified is false prompt to open a file (I am using fl_file_chooser) if successful set current->db info from load using back end functs set Foo::file_name->(value(cleaned with fl_filename_name) set Foo::foo_name->value(file_name->value()) set Foo::first_name->value(current->first_name) main() create a Foo object mainFoo initialize the db (required by the back end) mainFoo->show() Fl::run() delete mainFoo return happily

Now, I am just learning to use this wondrous toolkit and would like to know (in a basic form) what I need to do to get over this hump. I have worked through the related fluid posts in the newsgroups (Greg was describing how to inherit values by using a dummy group in a window in main() that calls the class constructor and the info seems closely related. If what I am looking for is not clear (I know it makes my head spin) ask for specific info. As I said, this is a learning experience for me. Quite frankly I am astounded at how little you actually need to learn (especially with fluid picking up the slack) to get at the power of FLTK. Makes it fun to learn as long as you have the tenacity of a pit bull.

Mark

A nice and simple example in a fluid project is really what I am after. The callbacks need to read and/or modify the base members and if I can't get closer I will have two choices.

1. Go back to my non-class version and straighten out the spagetti.
2. Admit defeat and create a non-fluid generated version of Contacts.
3. (hmm. this is more than 2) Take up knitting and change my name to Mary.
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
  #2  
Old 30-Jan-2005, 14:45
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough
I got a response from the newsgroups by the big gun and yet I seem no closer to my goal.

Quote:
Originally Posted by Bill Spitzak @ fltk.org newsgroups
Re: Fluid - Classes and Callbacks Bill Spitzak Sun, 30 Jan 2005 02:08:24-0500
C++ does not allow a real "method" to be used as a function pointer, so fltk
works around it in the simplest way. The normal way to solve this is:
CPP / C++ / C Code:
MyClass {
  inline callback(Fl_Widget* widget) {
    do_whatever...
  }
public:
  inline static_callback(Fl_Widget* widget, void* data) {
    ((MyClass*)data)->callback(widget);
  }
};

widget->set_callback(MyClass::static_callback, pointer_to_my_class_instance);
This means that you don't get to use the data* argument to the callback,
because it is taken up by the pointer to your widget. Fluid attempts to
solve this by putting the pointer into the data of the top-most window,
allowing child widgets to use their data pointers for something else.

Maybe it will help someone help me get past this problem. I sure hope so.

Mark
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
  #3  
Old 30-Jan-2005, 17:21
MatthiasWM MatthiasWM is offline
VIP
 
Join Date: Nov 2004
Posts: 62
MatthiasWM will become famous soon enough
Quote:
Originally Posted by cable_guy_67
I got a response from the newsgroups by the big gun and yet I seem no closer to my goal.

Maybe it will help someone help me get past this problem. I sure hope so.

Mark

OK, well, the problem is that you cannot call a 'normal' method of a class, because C++ was missing the syntax to do that (you can't store the pointer to a member function in C++ ; err, well, you can, but the syntax to actually call that address is so horrendously horrible, that we simply don't do it).

In FLTK, you must either call a 'regular' C function that is not a member of a class, or you must mark a member function as 'static'. This has the drawback though, that stati functions have no access to the 'this' variable. FLTK helps you here by providing the 'user_data' field, which you can use to store 'this'.

FLUID will try to write those static functions for you if you only put a function name into the callback field. If you add braces 'my_func(a, b)', it'll generate direct call code.

So this is how that can look:

CPP / C++ / C Code:

class SomeClass {
  int p;
  void fltk_can_not_call_me_directly();
  static void but_it_can_call_me(Fl_Widget *w, void *user);
};

void SomeClass::fltk_can_not_call_me_directly() {
  p = 2;
}

void SomeClass::but_it_can_call_me(Fl_Widget *w, void *user) {
  SomeClass *fake_this = (SomeClass*)user;
  fake_this->fltk_can_not_call_me_directly();
}


SomeClass *someclass = new SomeClass("spaghetti");
myPushButton->callback(SomeClass::but_it_can_call_me, someclass);

Last edited by cable_guy_67 : 02-Jul-2006 at 08:04. Reason: Added [c++] ... [/c++] code tag
  #4  
Old 30-Jan-2005, 17:43
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough
Quote:
Originally Posted by MatthiasWM
OK, well, the problem is that you cannot call a 'normal' method of a class, because C++ was missing the syntax to do that (you can't store the pointer to a member function in C++ ; err, well, you can, but the syntax to actually call that address is so horrendously horrible, that we simply don't do it).

I followed up to this point in the docs (there used to be a warning about this in the docs)
Quote:
Originally Posted by MatthiasWM
In FLTK, you must either call a 'regular' C function that is not a member of a class, or you must mark a member function as 'static'. This has the drawback though, that stati functions have no access to the 'this' variable. FLTK helps you here by providing the 'user_data' field, which you can use to store 'this'.
When I added 'this' to the user data field I kept getting incorrect usage of 'this' at top level or something like that. I believe this was when doing it for a menu callback.

Quote:
Originally Posted by MatthiasWM
FLUID will try to write those static functions for you if you only put a function name into the callback field. If you add braces 'my_func(a, b)', it'll generate direct call code.
Thanks for this explanation. I was working with Bill's example and was wondering where those inline functions were being generated.

Quote:
Originally Posted by MatthiasWM
So this is how that can look:
CPP / C++ / C Code:
\c++

class SomeClass {
  int p;
  void fltk_can_not_call_me_directly();
  static void but_it_can_call_me(Fl_Widget *w, void *user);
};

void SomeClass::fltk_can_not_call_me_directly() {
  p = 2;
}

void SomeClass::but_it_can_call_me(Fl_Widget *w, void *user) {
  SomeClass *fake_this = (SomeClass*)user;
  fake_this->fltk_can_not_call_me_directly();
}


SomeClass *someclass = new SomeClass("spaghetti");
myPushButton->callback(SomeClass::but_it_can_call_me, someclass);
\end_c++

I will give this a go and see if I can't get this working. I left the last bit in with your code to show you our markup tags in action. They are merely:

[c] to [/c] or [c++] to [/c++] or a plain old [code] to [/code]


This sheds some light on some things that have been causing me great trouble. Thanks again. I more than likely should not even be attempting this at such an early stage in my learning but if learning is not like hanging out with Louis and Clark what fun is it. :-P

Mark
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
  #5  
Old 30-Jan-2005, 20:27
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough
Ok, thanks to the help of the community I have managed to get this silly little problem to work (at least I think so, moving back with GIMcontacts will tell the tale) as it has been explained to me.

Here is the FLUID project file:
CPP / C++ / C Code:
# data file for the Fltk User Interface Designer (fluid)
version 1.0107 
header_name {.h} 
code_name {.cxx}
decl {\#include <FL/fl_ask.H>} {} 

class FooClass {open
} {
  decl {int p;} {}
  Function {FooClass()} {open
  } {
    code {p=0;} {}
    Fl_Window mywin {open
      xywh {370 118 104 100} type Double visible
    } {
      Fl_Button mybut {
        label {push me}
        xywh {0 5 100 40} down_box DOWN_BOX color 0 labelsize 22 labelcolor 128
      }
      Fl_Value_Output output_p {
        label {value of int p}
        private xywh {5 55 90 25} align 2
      }
    }
  }
  Function {not_directly()} {open
  } {
    code {p+=2;
output_p->value(p);} {selected
    }
  }
  Function {directly(Fl_Widget *w, void *user)} {open return_type {static void}
  } {
    code {FooClass *fake_this = (FooClass*)user;
fake_this->not_directly();} {}
  }
} 

Function {} {open
} {
  code {FooClass *fooclass = new FooClass;
fooclass->mybut->callback(FooClass::directly, fooclass);

fooclass->mywin->show();
Fl::run();
delete fooclass;
return (0);} {}
} 

I have to thank MatthiasWM and Bill Spitzak for the responses that got me moving in the right direction. Hopefully this will help someone else out. I think I can explain what is going on now in case anyone has any (basic) questions. At the very least this should keep me from having to scrap my Class version and go back to my mess.

Back to GIMcontacts. I have been working on it quite a bit and this should be the last hump. It surely isn't because the backend isn't ready, gim.h seems from my testing to work quite well.

Mark
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
Last edited by cable_guy_67 : 30-Jan-2005 at 21:42.
  #6  
Old 05-Feb-2005, 09:30
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough
By using the simple example here I have made wonderful progress with GIMcontacts. I do have one issue with this method though. Since the callbacks are set in main() I have to make any of the callback functions and their related widget public. If anyone has a suggestion on how to get around this I would appreciate hearing it. I'm sure it must be a simple "put this here in fluid" kind of answer, I just haven't found it yet. If I get through the add a new record stage this weekend I will post the fluid project for anyone interested.

Mark
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
  #7  
Old 05-Feb-2005, 14:43
cable_guy_67's Avatar
cable_guy_67 cable_guy_67 is offline
Senior Member
 
Join Date: Oct 2004
Location: Nescopeck, PA
Posts: 1,109
cable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the roughcable_guy_67 is a jewel in the rough
Quote:
Originally Posted by cable_guy_67
I do have one issue with this method though. Since the callbacks are set in main() I have to make any of the callback functions and their related widget public. If anyone has a suggestion on how to get around this I would appreciate hearing it. I'm sure it must be a simple "put this here in fluid" kind of answer, I just haven't found it yet.

The FAQ finally sunk in. The fluid way is to put the code in the constructor. My class constructor is where the window lives so when I create the GUI elements I set the defaults (some groups hidden) and then call

CPP / C++ / C Code:
your_widget_name->callback(static_callback, this);

I also call the main window ->show from here as well. Now all the widgets can remain private and main() is reduced to :
CPP / C++ / C Code:
// any prep work the class might need
// GIMcontacts preps the db with a call to ct_new() here
ct_new(size_values_here);
// create the object
GCT_contact *maincontact;
// now the GUI is already displayed so
Fl::run();
delete maincontact;
return(0);

Note: fluid will add the return(Fl::run()); at the end of your main if it is built in fluid so the code will have the above routine past the return(0). It won't hurt anything but your sense of aesthetics. I wanted the entire GUI to be contained in a single fluid project file to make it easy to modify in the future. The dependencies for the back end and support files are designed to look in include paths (that can be done from a simple compile script). Mine looks like this:
Code:
GIMINCLUDE=./Headers DATAINCLUDE=./Headers FLUIDINCLUDE=./Fluid echo echo echo "*****************************" echo "*** FLUID CONTACT COMPILE ***" echo "*****************************" g++ $FLUIDINCLUDE/contacts005.cxx $DATAINCLUDE/gct_data.cpp $GIMINCLUDE/gim.c -I$GIMINCLUDE -I$DATAINCLUDE <all one line> `fltk-config --ldflags --cxxflags` -s -o GCT_contacts

Thanks to dsmith for that last little sanity saver. Now the includes in the project files are #include <filename.h> variety instead of the #include "filename.h" type.
__________________
"Opportunity is missed by most people because it comes dressed in overalls and looks like work."
--Thomas Alva Edison
"Those who would give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety."
--Benjamin Franklin
"A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes."
--Hugh Downs
  #8  
Old 06-Feb-2005, 14:22
MatthiasWM MatthiasWM is offline
VIP
 
Join Date: Nov 2004
Posts: 62
MatthiasWM will become famous soon enough
Quote:
Originally Posted by cable_guy_67
The FAQ finally sunk in.

<grin>

I like to either make a class for every dialog in fluid. Then I create a method 'create_ui()' which, um, creates the UI. I then try to keep everything inside this class private and provide some access functions for the main app.

Another way is to create a 'partner class' to an existing class that reflects the settings of the class in a UI, something like this:

CPP / C++ / C Code:
class Address {
public:
  const char *get_first_name();
  void set_first_name();
   ...
};

// everything below can be implemented in FLUID

class AdressDialog {
  Address &addr;
  static void ok_button_callback(Fl_Widget*,void*);
  static void cancel_button_callback(Fl_Widget*,void*);
public:
  void create_and_show_dialog(Address&);
  void apply_dialog();
  void cancel_dialog();
  ...
};

void AddressDialog::ok_button_callback(Fl_Widget*,void *user) {
  AddressDialog *This = (AddressDialog*)user;
  This->apply_dialog();
}

void AddressDialog::apply_dialog() {
  addr.set_first_name( text_input_widget->value() );
  ...
}

void AddressDialog::create_and_show_dialog(Address &a) {
  addr = a;
  ... (create ui)
  text_input_widget->value(addr.get_first_name());
}


The same thing works even for the main application window. Just seperate everything into coveniently small parts.
 
 

Recent GIDBlogToyota - 2009 May Promotion by Nihal

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The

All times are GMT -6. The time now is 22:24.


vBulletin, Copyright © 2000 - 2009, Jelsoft Enterprises Ltd.