Things become different when lengthy calculations need to be done. If no precautions are taken, the GUI ``freezes'' during the time that such a calculation is performed. If this is not acceptable, some scheme must be used to break up the calculation in parts and allow events to be handled in between.
Within Ggi there are two mechanisms to achieve this.
The most `orthodox' approach would be registering a timer handler using ScheduleTimer(), with a time interval of zero, and then letting this handler perform the partial calculations, one after the other. Mainloop() would then handle any intervening events, at most one of every event type for each call to the timer handler. Though not difficult, programming such a handler may be experienced as counter-intuitive, especially when the calculation is just a simple (nested) loop.
The second mechanism is provided by the function GgiHandleEvents(), which can be called whenever the application decides to allow the handling of intervening events. Please note that this can cause recursion on a number of levels, including application-specific event handlers. Any handlers which can be called recursively must be programmed to take care of the recursion.
The following example shows the use of the second mechanism. It is derived from GUI example 2. Differences are indicated in red. In this example a hundred million cosines are calculated. This is done from within the keyword handler work() which is activated by the keyword WORK=. Every fifty thousand calls to cos(), GgiHandleEvents() is called to allow the handling of intervening events. One of these events could be setting the keyword WORK= to NO. If this happens, work() is called recursively. In this recursive call the static variable worktodo is set to FALSE, which eventually causes the first invocation to execute the break statement and return.
/* example5.c -XT */ #include "stddef.h" #include "math.h" /* for the cos function */ #include "gipsyc.h" #include "cmain.h" #include "init.h" #include "finis.h" #include "userfio.h" #include "ggi.h" /* * Keyword handler for QUIT= */ static void quit(ident id, char *key, int code, void *arg) { bool finished=toflog(FALSE); (void)userflog(&finished, 1, 2, key, " "); if (tobool(finished)) finis_c(); } /* * Keyword handler for both NUMBER= and GETAL= */ static void number(ident id, char *key, int code, void *arg) { float result; if (userfreal(&result, 1, 2, key, " ")==1) anyoutf(0, "The number %f was given for %s", result, key); } /* * Keyword handler for both DRINK= and FOOD= * Note that the argument 'items' was specified by the registration calls. */ static void order(ident id, char *key, int code, void *items) { fint result; if (userfint(&result, 1, 2, key, " ")==1) anyoutf(0, "You ordered %s", ((char**)items)[result]); } /* * Keyword handler for WORK= */ static void work(ident id, char *key, int code, void *items) { static bool worktodo=toflog(FALSE); int count=0, i, j; (void)userflog(&worktodo, 1, 2, key, " "); worktodo = tobool(worktodo); if (worktodo) { for (j=0; j<2000; j++) { volatile float x=1.111; for(i=0; i<50000; i++) { (void)cos(x); /* pretend doing useful work... */ count++; } if (GgiHandleEvents()) { if (!worktodo) break; /* abort calculation */ } } if (worktodo) wkeyf("%sNO", key); /* if necessary, reset button */ anyoutf(0, "Calculated %d cosines", count); } } /* * Main program */ MAIN_PROGRAM_ENTRY { static char *drinks[]={"Water","Milk","Beer","Wine","Sherry","Whisky",NULL}; static char *foods[]={"Beans","Peas","Lettuce","Potatoes","Meat",NULL}; ident button, text1, text2, gauge, menu1, menu2; /* declare elements */ ident workbt; init_c(); /* * Specify explicit layout and postponed realization. */ GgiAutoLayout(FALSE); /* layout done explicitly by program */ GgiPostponeRealize(TRUE); /* show after all elements have been positioned */ /* * Create elements. */ button = GgiButton("QUIT=", "Terminate program"); workbt = GgiButton("WORK=", "Work hard..."); text1 = GgiTextField("NUMBER=", "Type a number in this field", 15); text2 = GgiTextField("GETAL=", "Typ een getal\nin dit veld", 15); gauge = GgiGauge("NUMBER=", "Produce numbers from -1 to +1", 200, -1.0, 1.0); menu1 = GgiMenu("DRINK=","Order your drink", drinks); menu2 = GgiMenu("FOOD=","Order your food", foods); /* * Modify some elements' properties. */ (void)GgiSetLabel(text1, "Number", 60); /* change default keyword label */ (void)GgiSetLabel(text2, "Getal", 60); /* change default keyword label */ (void)GgiSetLabel(gauge, " ", 1); /* suppress gauge's keyword label */ /* * Position the elements. */ GgiSetPosition(button, 0, NULL, 0, NULL); /* top left */ GgiSetPosition(workbt, 0, button, 0, NULL); /* right to button */ GgiSetPosition(text1, 0, NULL, 10, button); /* below button */ GgiSetPosition(text2, 0, NULL, 0, text1); /* below text1 */ GgiSetPosition(gauge, 0, text1, 10, button); /* right of text1 */ GgiSetPosition(menu2, -GgiWidth(menu2), gauge, 0, NULL); /* above gauge */ GgiSetPosition(menu1, -GgiWidth(menu1)-GgiWidth(menu2)-10, menu2, 0, NULL); /* left of menu2 */ /* * Show what has been created until now. */ GgiRealize(); /* * Register keyword callbacks. */ (void)ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL); (void)ScheduleKeyevent(number, "NUMBER=", KEYCHANGE, NULL); (void)ScheduleKeyevent(number, "GETAL=", KEYCHANGE, NULL); (void)ScheduleKeyevent(order, "DRINK=", KEYCHANGE, drinks); (void)ScheduleKeyevent(order, "FOOD=", KEYCHANGE, foods); (void)ScheduleKeyevent(work, "WORK=", KEYCHANGE, NULL); /* * Put things into action. */ MainLoop(); }
Maintained by J. P. Terlouw |