On 21.09.2010, at 12:32, paul wrote:

> Thanks Albrecht, I have copied this to a file and will review later, at first 
> glance it seems to be an evolved version of your earlier button example which 
> I found useful working last night.

Yep, that's true. It simulates a long-running callback (by using
sleep) to show how the GUI can be kept alive during the callback.

> Anyway, I made progress last night, the button example callback had me really 
> confused at first, the prototypes and calls just weren�t adding up as I 
> tried to hack a working version into an exisiting test project. Also when I 
> ran the actual example itself  in the debugger I was amazed to see I could 
> not �get back out� of the callback function, �by step� requests in 
> the normal way. I thought what the�! And �how does it know how to 
> exit?� �How does it know to even do this same callback again?� Then I 
> had a bath, and like a modern day Archimedes I had my (I hope) Eureka moment! 
>  Something like this came to me:
> The program flow could not be followed in the debugger in the normal way 
> because nothing else had happened. The callback had completed and the lower 
> level GUI loop had again taken over, the only time anything was going to go 
> anywhere was if another event triggered a different callback, and it exits 
> the whole program when required because �exit� is a callback too!

Well, that seems about almost right. Let me rephrase it: As you know,
Fl::run() does all event handling by reading the system's event queue.
When the user clicks on a button that has a callback, FLTK *calls* this
callback function, just as if you would call any function or subroutine.
Upon return, FLTK continues its event handling work. If/when there are
no events to service, FLTK sits there waiting ... (w/o using CPU time).

[ side note: of course you can also debug the FLTK event loop if you
compiled FLTK with debug info and have the sources in place. ]

WRT exit(): exit isn't a "callback" function, it's a system function
that exits your program. It doesn't return to the caller.

> As I say the call to  �callback()� code and user defined function 
> definition had me confused also.
> There seem to be several overloaded versions of callback() could anybody 
> illustrate those for me?

Ah, yes, the docs are very sparse about that. This should really be
improved. I'll come back to this below.

> I see that as a minimum it takes a pointer to a function, does the function 
> parameter (ie the user defined callback function)  have to be a function that 
> takes an Fl_widget* as a parameter? Or is this just a good idea as it is 
> probably going to be used internally In user callback implementation for 
> redraw() or other operations?
> And what is the void* parameter in function button_cb for?

FLTK calls your callback function with exactly 2 arguments. The
first argument is the widget that triggered the callback (in our
case the button), the second argument is the widget's user_data
member variable. You can set this user_data to any (compatible)
value you like to have it available in your callback function.
Confused? Well, I hope that it will become clear with the following:

The callback function should (must?) have this prototype:

   void my_callback (Fl_Widget *widget, void *data);

As mentioned above, the first argument is the widget that triggered
the callback, thus is it normal to cast it to the widget type you
expect here (you can of course define different callback functions
for different widget types - and even for different widgets of the
same type):

   Fl_Button *button = (Fl_Button *)widget;

Now to the second argument: void *data. This is the user_data() of
the widget that has been set before. You can do this explicitly by
calling

   void Fl_Widget::user_data (void *v);

or you can do this with one of the overloaded callback() methods.
The first argument is always your callback function (this will be
stored as a function pointer in the widget), and the second argument
(if any) is the user_data that will also be stored in the widget's
user_data member. The user_data member is defined as "void *", thus
the "natural" callback method is:

   void Fl_Widget::callback (Fl_Callback *cb, void *p);

This means that calling:

   window->callback(win_cb,(void *)button);

is the same as:

   window->callback(win_cb);
   window->user_data((void *)button);

There is one more convenience method that lets you specify
the user_data as a long value:

   void Fl_Widget::callback (Fl_Callback *cb, long p=0);

This stores the long argument in user_data by casting it
to (void *), just to make it easier to store a long value.

> I managed to get this working in my program and I used Fl::Check() called 
> within a loop that counted to a million and output it to a textbox, whenever 
> a rudimentary �timer� variable reached10, in this was I managed to get my 
> first look at what I originally was after, watching a variable been written 
> quickly to the output box, itran nice and fast with only a little flickering, 
> probably due to the check() call? And was certainly fit for purpose, only 
> problem is, as expected it �hosed� the CPU ;->  Until I tell it not to 
> run at full whack then this is going to happen no matter what the spec of the 
> machine I suppose. I have just told the program to �GO� so I think I 
> could afford to cap the framerate (if that is accessible in FLTK?) a little 
> and reduce CPU that way, but I think the clear answer is really to go with 
> the multithreading.

As Ian wrote already, if you need the CPU time for doing the
calculation, then there's no way to avoid that. It doesn't
make any difference whether you do it with threads or with
a direct callback. The only question is how you manage to
do the GUI updates in a useful way. If you do it in every
iteration of your loop, then the GUI updates need too much CPU
time (more than your real work!). The trick would be to know
how often and when you should update the GUI. In my example
this could be done in every iteration (10 ms), or maybe only
every 5th iteration (50 ms -> 20 GUI updates per sec.).

This could be done by a timeout to be independent of your
calculation speed. But that would be another example, and
I don't have the time to explain this further now. :-(

But look at Fl::add_timeout() to find out yourself.

> My only other issue was I could only get this to work last night using a 
> global object, when I tried to define a callback within a class and then use 
> it later.
> I tried to cast the Fl_Widget* as a window, within mycallback definition 
> which worked fine, but I wanted it to be MY window, which was constructed 
> with an output box� ahh I see� I should have cast the widget as an output 
> box???

Yep, this sounds right and should be clear with the explanations
above (I hope).

> I have added some additional comments to the code below to show my 
> understanding so far.
>
> // global variables for easier coding
>
> static int state = 0;
>
> // simple callback function
>
> void button_cb (Fl_Widget *w, void *) {
>
>    Fl_Button *b = (Fl_Button *)w; // Cast widget to the required local type

okay.

>    if (!state) {              //if block checks if any event occured at all 
> (to the button that sent the callback)
>                               //why does this not trigger when you get a 
> 'mouseover' type event?
>                               //if this is just checking if(1) or if(0) 
> surely mouseover button = 1?

This is because a 'mouseover' event needs more internal (lower level)
event handling. You can do all this, but you need to derive your own
class and define its handle() method. See the doc's chapter about
event handling. But that's more advanced GUI programming than you
may need for your task.

The fact that the callback is only called when you click the button
is because the default is exactly so. You can change that a little
by calling Fl_Widget::when(), but that still doesn't give you any
mouse events - but you can get the callback for instance when the
button is clicked and when it is released.

WRT the state variable: This is a really simple communication between
the callback and the user interface. 1 means that the callback is
running (set up by the first button click), and 0 means that the
callback (a) doesn't run or (b) should stop as soon as possible.
The button label (START or STOP) reflects this state and shows
the user what's going on.

>      b->label (" - S T O P -");
>      state = 1;
>    } else {
>      b->label ("S T A R T");
>     state = 0;
>    }
>    b->redraw();               //This is not always neccesary, depending on 
> widget

Yes, maybe, sometimes the widget in question will be drawn for
another reason... But to be sure, you should call this whenever
you change a widget's visible attributes. Note that this is cheap,
because it only marks the widget to be drawn, but doesn't actually
do it right now. It will be done when you call Fl::flush(),
Fl::check(), or Fl::wait(), or when your callback returns to FLTK's
event loop.

> }
>
> int main(int argc, char **argv) {
> //
> //
> //
>    button->callback(button_cb); // callback takes function pointer parameter, 
> allows for great flexibility
> //
> //
>    return Fl::run();   //low level event loop, not exposed.
> }

Yep.

Albrecht
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk

Reply via email to