[PREVIOUS] [NEXT] [UP

Interactive programs

This lesson will discuss some more complex examples to illustrate how the previously explained techniques can be combined to create interactive programs.

A simple standard set-up for interactive programs

For many interactive programs, a suitable "standard structure" is a collection of viewing windows that are embedded (using named areas) in a shared input window. The input window offers additional widgets, such as button or sliders, to manage interactive input. Example#1 below illustrates this structure (it has only a single embedded viewing window in this case). In simple cases, all interaction can occur via the widgets of the enclosing input_window. In this case, a straightforward control scheme is to equip the input_window with an OK button (to enforce an implicit loop) and to bind all the necessary actions as callbacks (implemented as correspondingly named NST units) to the widgets of the input window.

How to provide mouse interaction for embedded windows

However, this simple scheme does not yet permit to interact directly with any objects in the embedded windows. Such interaction usually requires to use the mouse pointer as well. Usually, one wants to pass control to a mouse unit while the mouse pointer is in a particular window, and regain control for the embedding input_window, when the pointer leaves the window area again. To this end

(i) bind the mouse_xy unit as a %!-callback to the embedded window. This triggers the mouse_xy unit whenever the pointer enters the embedded window (if there are several embedded windows, each of them must have its own mouse unit, since each mouse unit  instance can only be made responsible for a single window).

(ii) Create the mouse_xy instance in autoloop mode, i.e., with option %L. Choosing %L makes the mouse unit keep control (after triggered from the input_window) while the  mouse pointer is within the associated window. As soon as the pointer leaves the window, the autoloop terminates and controll automatically falls back to the caller, i.e., the input_window in the present case.

(iii) Choose "NoWait" in the mouse_xy parameter dialog window so that the unit does not wait for a button press before any other events, such as pointer motion in the window, are processed. The mouse unit callback options (as described in the previous chapter Using the mouse) can be used to trigger further actions from mouse pointer motion or button press events.

Example#1 illustrates this set-up: the %!mouse callback following the named area "plot" in the input_window unit triggers the named mouse unit when the pointer enters the plot area; the mouse unit in turn has (in addition to its %L autoloop option) three callbacks:  %0!prog:left_but_down and %0!!!prog:left_but_up invoke two methods (implemented in a prog_unit) to print a corresponing message when the left button (which is button 0) is pressed or released, while %!!printvec is called after each pointer motion event to display the new mouse coordinates in a little window. Experiment with a few changes to explore how the behavior of the circuit changes, e.g., when you configure the mouse unit in WaitMode.

Example#2 illustrates how the same control scheme can cope with a more complex situation: now there are two embedded windows and the mouse unit is bound to the larger one, named "pixel". Whenever the mouse pointer enters window "pixel",
the mouse unit is triggered and will continuously read the pointer coordinates. These are fed to an output_window unit, which is executed as a callback of the mouse_xy unit and  whose output appears in a small named area (non-sunken mode) of the input window. Additionally, mouse button 0 toggles a 0/1 counter that activates/deactivates the container named "subrect".
When activated, the circuitry inside the container will display a zoomed subrectangle of the original image into the second, smaller embedded area.

Additionally, the input window contains a "Load" button with a callback to a file read unit and the write_pix display unit for the loaded image data. The operation of these components is largely separate from the others. The no_op in front of most of the circuit ensures that a "Step" will only call the input_window unit, which then controls all the rest via its callbacks.

How to embed a view3d viewer

Example#3 demonstrates the use of the explained method for embedding a view3d unit. This is a NST operator unit for rendering graphical objects in OpenGL. The to-be rendered objects appear in the view3d window when they are placed in the scope of the operator unit (additional, non-graphical units can be executed as well, e.g., for doing auxiliary computations; for a more detailed explanation, see chapter Creating 3d Plots).

Unlike most other units, the view3d unit comes with a built-in cursor control loop (i.e., you can rotate the view directly with the left mouse pointer). Therefore, this time we don't need an extra mouse unit, however, it still is important to configure the view3d unit again such (choosing mode 3 in its parameter dialog window) that its cursor control loop exits when the mouse pointer leaves the view3d window (again to avoid blocking the surrounding input_window).

Everything else is analogous to the previous example. The mouse pointer can rotate the view when entering the embedded window (to rotate, you must hold the left mouse button down; this requirement is inherited from the view3d's cursor control loop).

Additionally, the robot will respond to changes in the slider settings, since the view3d unit is also bound as a callback to the sliders (you don't need to bind the make_puma unit itself; it is executed via the view3d unit, which here plays a similar role as the mouse unit before. The difference is that you don't need to specify any callbacks for it, since it accesses the to-be-executed units by virtue of being an operator unit).

Context specific mouse actions: a graph editor

As a final example we show how the previous concepts can be used to implement an interactive editor for planar graphs. This demonstrates how to make mouse actions context specific, so that nodes can be created or deleted, links inserted or cut, and nodes dragged with the links co-moving, with all these modes selected from buttons in an input_window. The entire, rather complex task can be solved with only seven NST units (one of which is even a no_op).

Load and start the example#4. With the buttons you can select what the mouse pointer does (all mouse actions are carried out with the left mouse button 0). With "Exit" you will return to the Neo command level.

Open the creation dialog window of the input_window unit ('m') to see the the by-now familiar control structure of the circuit: the embedded window is followed by the callback %!sum_val,mouse which triggers two named units when the mouse pointer enters window: sum_val computes (for the subsequently executed mouse unit) a "context-index", indicating which command button of the input_window unit is currently "On". The command buttons form a group configured such that exactly one button must always be "on" (this is achieved by enclosing the widget directives by the "group directives" %![ and %]).
The context index computed by the sum_val unit is fed to the mouse_xy control input (the value is computed from summing the values of all command buttons; since only one button is on, the result is the value of the active button).

The mouse unit itself has several callbacks (open the mouse creation dialog window to see them). As explained in chapter "Using the mouse", the value ("context index") at the mouse control input can be used to bind different sets of callbacks to the same mouse button (here the left button 0).

The callbacks themselves are all implemented as public methods in the prog unit, operating on two shared data arrays that hold the positions of the graph nodes and node index pairs representing the edges. The node position array is made accessible as the first output connector and allows the plot_xy unit to draw the node circles. A second output connector encodes the edges in a way so that they can be easily drawn with the plot_xy unit: the edges are coded as a long array of pairs of markers; every marker is given as a quintuple of x,y,color,type and size ("xycts"). By specifying t=1 for all even, and t=-1 for all odd points, only the odd points are connected with a line segment to their predecessors, as required.

How to autostart windowed circuits with the mouse pointer

For windowed applications, Neo provides a simple way of faking some of the conveniences of multitasking by allowing to specify  for each unit the name of an NST window so that the unit becomes "autostarted" as soon as the mouse cursor enters the specified window and "autostopped" when the mouse cursor leaves the window again. If the autostarted units are implemented such that each autostart can resume execution at the point where the previous "autostop" had terminated,  working with several such unit will appear as if each unit were continuously running, since each unit always is active when you wish to do something with its GUI window.

Typically, you will specify as the "activation window" the main GUI window of the autostarted unit. Usually, this window also will have a button to terminate execution of the unit. Pressing this button should then be the exit action that is specified for that window.

The necessary specifications are made with Neo's Define command, applied to the to-be-autostarted unit. The relevant entry field is labelled "Associated activation window (if any):" and expects the name of the activation window, optionally followed by the exit action. If the exit action is a button press in an input_window,  the specification takes the form

   windowname %f:p:v

where windowname is the name of the input_window, f and p are the field and pin numbers of the selected button, and v the value to which the button shall be set (defaults are f=0, p=0, v=1, if no values are specified). @i-Variables can be used within the above specification; in this case, however, @i refers to the i-th parameter in the Creation dialog window of the container unit for which the input_window is specified (and not to the i-th parameter slot of the creation dialog window of the enclosing container, as usual).

NOTE: for safety reasons, the autostart feature is disabled for circuits pushed into Neo from the netscape browser. Therefore, to run the following example, use the next example link in the example#4. This will load example#5 from Neo and, therefore, have the autostart feature enabled. Directly clicking Example#5 below would push the example from the browser, and the autostart feature would then be disabled.
 

Example#5 is a demo-circuit with two containers demo0 and demo1. The left unit demo0 has as its autostart window the left input_window named demowindow0. The right unit demo1 has as its autostart window the right write_pix window named demowindow1. This illustrates a situation where the autostart window is not of type input_window and, therefore, has no buttons to specify an autoexit operation. In this case, the autoexit is achieved by querying the pointer position and executing a break_unit when the mouse pointer has left the window.

Other control schemes

When a named area has no callback, it is considered as exterior region of the enclosing input- or output_window.Before the availability of callbacks for named areas, this property was exploited for the following, now obsolete, control scheme: the embedding input- or output window unit was configured in non-blocking mode 3 and put  into a continuously running for-loop, together with additional units that implemented the operations needed when the pointer was within an embedded window. This made the loop execute the embedding window while the pointer was inside it (and not in a named area), while execution of the embedding window immediately returned and the other loop units were executed while the pointer visited one of the "exterior" embedded window areas (this scheme is, e.g., used in the data mining plot units to achieve the data point selection with the mouse cursor).

While this control scheme works fine, it has the drawback that the "busy loop" continuously executes CPU cycles and should, therefore, be avoided in new work. The %! and %!!! callbacks are without this drawback, as well as the "continuous" %!! callbacks of the mouse_xy unit and the widget managing operations of the input_window and output_window units. All these react only when a pointer motion or some button event occurs so that CPU usage falls to zero when no further user events occur. A notable exception are the continuous %!! callbacks of the input_window and output_window unit: they are provided to explicitly allow continuous repetition of callback unit(s) even when user events are absent , e.g., to run some operation while a button is "On" or the mouse pointer is inside a named area (note the differenct to the mouse_xy %!!  callback, which is "continuous" only while pointer motion events occur!).


[PREVIOUS] [NEXT] [UP]