Author: Stefan Hundhammer <sh@suse.de>
Prev: Introduction | Top: Event Handling Index | Next: Event Reference |
UserInput() | Waits for user input and returns a widget ID. |
PollInput() | Checks for pending user input. Does not wait. Returns a widget ID or nil if no input is available. |
TimeoutUserInput ( int timeout ) | Waits for user input and returns a widget ID. Returns ID `timeout if no input is available for timeout milliseconds. |
WaitForEvent() WaitForEvent ( int timeout ) |
Waits for user input and returns an event map. Returns ID `timeout if no input is available for timeout milliseconds. |
Note: This section describes only those builtin functions of the YaST2 user interface that are relevant for event handling. The YaST2 UI has many more builtin functions that are not mentioned here. Refer to the UI builtin reference for details.
UI::UserInput() waits for the user to do some input. Normally this means it waits until the user clicks on a push button.
Widgets that have the notify option set can also cause UserInput() to return - i.e. to resume the control flow in the YCP code with the next statement after UserInput().
As long as the user does not do any such action, UserInput() waits, i.e. execution of the YCP code stops in UserInput(). In particular, entering text in input fields (TextEntry widgets) or selecting an entry in a list (SelectionBox widget) does not make UserInput() continue unless the respective widget has the notify option set.
UserInput() returns the ID of the widget that caused it to return. This is usually a button ID. It does not return any text entered etc.; use UI::QueryWidget() to retrieve the contents of the dialog's widgets.
Such a widget ID can be of any valid YCP type, but using simple types like symbol, string or maybe integer is strongly recommended.
Although it is technically still possible, using complex data types like map, list or even term (which might even contain YCP code to be executed with eval()) is discouraged. Support for this may be dropped without notice in future versions.
Since it depends on exactly what types the YCP application developer choses for his widgets, UserInput()'s return type is any. You may safely use a variable of the actual type you are using (usually symbol or string).
any widget_id = UI::UserInput();
// UserInput.ycp // // Example for common usage of UI::UserInput() { // Build dialog with two input fields and three buttons. // // Output goes to the log file: ~/.y2log for normal users // or /var/log/YaST2/y2log for root. string name = "Tux"; string addr = "Antarctica"; UI::OpenDialog( `VBox( `TextEntry(`id(`name), "&Name:", name ), `TextEntry(`id(`addr), "&Address:", addr ), `HBox( `PushButton(`id(`ok ), "&OK" ), `PushButton(`id(`cancel ), "&Cancel" ), `PushButton(`id(`help ), "&Help" ) ) ) ); symbol widget_id = nil; // All widget IDs used here are symbols // Event loop repeat { widget_id = UI::UserInput(); if ( widget_id == `ok ) { // process "OK" button y2milestone( "OK button activated" ); // Retrieve widget contents name = UI::QueryWidget(`id(`name ), `Value ); addr = UI::QueryWidget(`id(`addr ), `Value ); } else if ( widget_id == `cancel ) { // process "Cancel" buttton // or window manager close button (this also returns `cancel) y2milestone( "Cancel button activated" ); } else if ( widget_id == `help ) { // process "Help" button y2milestone( "Help button activated" ); } // No other "else" branch necessary: None of the TextEntry widget has // the `notify option set, so none of them can make UserInput() return. } until ( widget_id == `ok || widget_id == `cancel ); // Close the dialog - but only after retrieving all information that may // still be stored only in its widgets: QueryWidget() works only for // widgets that are still on the screen! UI::CloseDialog(); // Dump the values entered into the log file y2milestone( "Name: %1 Address: %2", name, addr ); }
PollInput() is very much like UserInput(), but it doesn't wait. It only checks if there is a user event pending - the user may have clicked on a button since the last call to PollInput() or UserInput().
If there is one, the ID of the widget (usually a button unless other widgets have the notify option set) is returned. If there is none, nil (the YCP value for "nothing", "invalid") is returned.
Use PollInput() to check if the user wishes to abort operations of long duration that are performed in a loop. Notice that PollInput() will result in a "busy wait", so don't simply use it everywhere instead of UserInput().
Notice there is also TimeoutUserInput() and WaitForEvent() that both accept a millisecond timeout argument.
any widget_id = UI::PollInput();
// PollInput.ycp // // Example for common usage of UI::PollInput() { // Build dialog with two labels and a "stop" button. integer count = 0; integer count_max = 10000; UI::OpenDialog( `VBox( `Label( "Calculating..." ), `Label(`id(`count ), sformat( "%1 of %2", count, count_max ) ), `PushButton(`id(`stop), "&Stop" ) ) ); any widget_id = nil; // Event loop repeat { widget_id = UI::PollInput(); // Simulate heavy calculation sleep(200); // milliseconds // Update screen to show that the program is really busy count = count + 1; UI::ChangeWidget(`id(`count), `Value, sformat( "%1 of %2", count, count_max ) ); UI::RecalcLayout(); // Might be necessary when the label becomes wider } until ( widget_id == `stop || count >= count_max ); UI::CloseDialog(); }
TimeoutUserInput() is very much like UserInput(), but it returns a predefined ID `timeout if no user input is available within the specified (millisecond) timeout.
This is useful if there is a reasonable default action that should be done in case of a timeout - for example, for popup messages that are not important enough to completely halt a longer operation forever.
User interface style hint: Use this with caution. It is perfectly OK to
use timeouts for informational messages that are not critical in any way
("Settings are written", "Rebooting the newly installed kernel"), but
definitely not if there are several alternatives the user can choose from. As a
general rule of thumb, if a dialog contains just an "OK" button and nothing
else, TimeoutUserInput() is appropriate. If there are more buttons, chances are
that the default action will cause disaster for some users.
Remember, timeouts are nearly always a desperate means. They are always both
too short and too long at the same time: Too short for users who know what
message will come and too long for users who left to get some coffee while the
machine is busy.
Another possible use of TimeoutUserInput() would be to periodically update the screen with data that keep changing (time etc.) while waiting for user input.
any widget_id = UI::TimeoutUserInput( integer timeout_millisec );
// TimeoutUserInput.ycp // // Example for common usage of UI::TimeoutUserInput() { // Build dialog with two labels and an "OK" button. integer countdown_sec = 30; integer interval_millisec = 200; integer countdown = countdown_sec * 1000 / interval_millisec; UI::OpenDialog( `VBox( `Label( "Rebooting Planet Earth..." ), `Label(`id(`seconds ), sformat( "%1", countdown_sec ) ), `PushButton(`id(`ok ), `opt(`default ), "&OK" ) ) ); any id = nil; // Event loop repeat { id = UI::TimeoutUserInput( interval_millisec ); if ( id == `timeout ) { // Periodic screen update countdown = countdown - 1; integer seconds_left = countdown * interval_millisec / 1000; UI::ChangeWidget(`id(`seconds ), `Value, sformat( "%1", seconds_left ) ); } } until ( id == `ok || countdown <= 0 ); UI::CloseDialog(); }
WaitForEvent() is an extended combination of UserInput() and TimeoutUserInput(): It waits until user input is available or until the (millisecond) timeout is expired. It returns an event map rather than just a simple ID.
In the case of timeout, it returns a map with a timeout event.
The timeout argument is optional. If it isn't specified, WaitForEvent() (like UserInput()) keeps waiting until user input is available.
Use WaitForEvent() for more fine-grained control of events. It is useful primarily to tell the difference between different types of events of the same widget - for example, if different actions should be performed upon selecting an item in a SelectionBox or a Table widget. Notice that you still need the notify option to get those events in the first place.
On the downside, using WaitForEvent() means accessing the ID that caused an event requires a map lookup.
Notice that you still need UI::QueryWidget() to get the contents of the widget that caused the event. In the general case you'll need to QueryWidget most widgets on-screen anyway so delivering that one value along with the event wouldn't help too much.
Important: Don't blindly rely on getting each and every individual event that you think should come. The UI keeps track of only one pending event (which is usually the last one that occured). If many events occur between individual WaitForEvent() calls, all but the last will be lost. Read here why. It is relatively easy to programm defensively in a way that losing individual events doesn't matter: Also use QueryWidget() to get the status of all your widgets. Don't keep redundant information about widget status in your code. Ask them. Always.
map event = UI::WaitForEvent();
map event = UI::WaitForEvent( integer timeout_millisec );
// WaitForEvent.ycp // // Example for common usage of UI::WaitForEvent() { // Build dialog with a selection box and some buttons. // // Output goes to the log file: ~/.y2log for normal users // or /var/log/YaST2/y2log for root. integer timeout_millisec = 20 * 1000; UI::OpenDialog( `VBox( `SelectionBox(`id(`pizza ), `opt(`notify, `immediate ), "Select your Pi&zza:", [ `item(`id(`napoli ), "Napoli" ), `item(`id(`funghi ), "Funghi" ), `item(`id(`salami ), "Salami" ), `item(`id(`prociutto ), "Prosciutto" ), `item(`id(`stagioni ), "Quattro Stagioni" ), `item(`id(`chef ), "A la Chef", true ) ] ), `HBox( `PushButton(`id(`ok ), "&OK" ), `PushButton(`id(`cancel ), "&Cancel" ), `HSpacing(), `PushButton(`id(`details ), "&Details..." ) ) ) ); map event = $[]; any id = nil; // Event loop repeat { event = UI::WaitForEvent( timeout_millisec ); id = event["ID"]:nil; // We'll need this often - cache it if ( id == `pizza ) { if ( event["EventReason"]:nil == "Activated" ) { // Handle pizza "activate" (double click or space pressed) y2milestone( "Pizza activated" ); id = `details; // Handle as if "Details" button were clicked } else if ( event["EventReason"]:nil == "SelectionChanged" ) { // Handle pizza selection change y2milestone( "Pizza selected" ); } } if ( id == `details ) { y2milestone( "Show details" ); } if ( id == `timeout ) { // Handle timeout y2milestone( "Timeout detected by ID" ); } if ( event["EventType"]:nil == "TimeoutEvent" ) // Equivalent { // Handle timeout y2milestone( "Timeout detected by event type" ); // Open a popup dialog UI::OpenDialog( `VBox( `Label( "Not hungry?" ), `PushButton(`opt(`default ), "&OK" ) ) ); UI::TimeoutUserInput( 10 * 1000 ); // Automatically close after 10 seconds UI::CloseDialog(); } } until ( id == `ok || id == `cancel ); UI::CloseDialog(); }
Prev: Introduction | Top: Event Handling Index | Next: Event Reference |
$Id: event-builtins.html,v 1.13 2003/07/11 15:15:11 sh Exp $