Sysquake Pro – Table of Contents
Sysquake for LaTeX – Table of Contents
SQ Files Reference
This chapter describes the syntax of SQ files, the "programs" run by Sysquake, and the contents of SQ Data files (files with a .sqd suffix), which store the state of a session with an SQ file.
SQ Files
SQ files define the set of interactive plots which can be displayed. Each SQ file corresponds to a specific kind of problem; for instance, one could have SQ files for the design of a digital filter or a PID controller for continuous-time systems, for the study of the effect of initial conditions on a simulation, and so on.
SQ files are text files. You can write them using any text editor or word processor. Make sure that you save them as plain ASCII files, without any style or formatting information; depending on the application, this option is called Text Document, Text Only, ASCII File, or an equivalent expression. Also, do not rely on automatic word wrapping; make sure that all lines end with an explicit end-of-line character. Sysquake accepts carriage returns, line feeds, or both, to accommodate for the text file formats found on different operating systems.
SQ files contain different kinds of elements:
- Declaration of variables
- Variables are used as parameters for the figures, menu item actions, etc. Manipulating a figure changes some of the variables, and all the figures currently displayed are updated to reflect the changes. These variables, called Sysquake variables, must not be confounded with LME variables (variables used without declaration in LME functions).
- Definition of constants
- Integer literal values can be given a name to make the code clearer. These definitions are visible in the declaration part of SQ files as well as in function definitions.
- Declaration of handlers
- Handlers are expressions executed to perform different tasks managed by Sysquake, such as initialization, figure manipulation, menu selection, etc. They have the same syntax as LME assignments, or expressions if no result is to be used. As input and output, they use Sysquake variables as well as values managed directly by Sysquake, such as the position of the mouse. Variables in the left-hand side of assignments cannot use indexing or structure field access. Values managed by Sysquake are identified with a name beginning with an underscore; they can be used either directly in the handler declaration, or indirectly in a function called in a handler declaration.
- Function definitions
- Handlers are implemented by functions written in LME, an interpreted language well suited for numeric computation.
- Help
- A textual description can be provided in SQ files. Sysquake displays it upon user request.
Syntax
SQ files contain declarations, blocks of text, and comments. Declarations are always placed at the beginning of a line; they start with a keyword, and are usually followed by other elements. Here is the list of keywords:
Remark: all these keywords are supported on all versions of Sysquake to assure the compatibility of SQ files. However, import, export and extension declarations have an effect only on the full version of Sysquake.
Declarations must be either contained on a single line or split into several lines with the continuation characters ... at the end of the first line(s):
variable a, b, ... c, d, e
Many keywords are followed by handler declarations. These handlers are implemented in LME code in a functions block and may accept input arguments and output arguments. Arguments may be variables declared in the SQ file, special arguments beginning with an underscore, or (for the input arguments) integer numbers or named constants. In some case, handler declarations may be reduced to a simple assignment such as y=x or num=3. Expressions are not allowed.
Comments are either enclosed between /* and */ or span from // or % to the next end of line. They cannot be nested.
Declaration of Variables
Variables defined at the level of the SQ file play a very important role in Sysquake. They are global; all handlers can use them as input and/or output arguments. They are preserved between handler executions. They contain the whole state of Sysquake; together with the SQ file itself, they permit to restore what appears in the Figure window, and are used in the Save mechanism. Finally, they are the base of the Undo system, which enables the user to cancel the last operations (such as manipulating a figure with the mouse, changing a subplot or entering numeric values in a dialog box) and redo them.
Variables are declared with the variable (or variables) keyword, followed by one or several variable names separated by commas or spaces. They can contain any data type supported by LME, such as numbers, arrays, strings, structures, etc. Variables are always defined; variables whose value has not been set explicitly contain the empty array []. Variable names are made of letters, digits and underscores; they begin with a letter, and case is significant, like everywhere else in Sysquake. Names beginning with an underscore are reserved for special purposes and should not be used for variables.
Five variables are managed by Sysquake and cannot be used in handlers: _plots, a string which contains the name of the subplots (see function subplots); _plotprops, an array which contains the properties and scaling of each subplot (see function subplotprops); _plotpos, an array which contains the position of subplots when Free Position is enabled (see function subplotpos); _plotsync, an array which defines which and how subplot scales are synchronized (see function subplotsync); and _plotparam, a list whose elements are arbitrary data specific to each subplot (see function subplotparam). They are revealed when the state of Sysquake is saved to SQ Data files.
Example
variable x variables num den
Variables can be initialized in the init handler(s) (see below), or directly in the variable declaration; in that case, only one variable can be declared and initialized for each declaration statement, and the expression on the right of the equal character must not depend on other variables.
Example
variable x = 2 variable vec = 1:10
If the expression on the right of the equal character depends on other variables, the variable on the left is declared, and the assignment is treated as a make handler (see below), i.e. the assignment is performed everytime the variable is used and its dependencies have changed.
Variables can be declared nondumpable to prevent Sysquake from saving and restoring them, be it with the Undo/Redo mechanism or with Save and Open. Nondumpable variables can be used and modified like any other variable; however, modifying them with any handler will prevent Undo from restoring their previous state, and Save (or Dump Data) will skip their value (the saving mechanism is detailed later in this chapter). Variables which should be declared as nondumpable include file descriptors for files and serial port connections: a file descriptor is only valid if the corresponding file or device has been opened with fopen or similar functions for devices.
Nondumpable variables are declared with the keyword _nondumpable following immediately variable. No comma may precede or follow _nondumpable. Dumpable and nondumpable variables may not be mixed in the same declaration.
Example
variables _nondumpable fd, connection
Declaring variables explicitly incites to plan how they will be used as the glue between the different handlers. It makes easier the documentation; each variable can be commented, and their enumeration, if self-explanatory names are chosen, is itself an important help for future maintenance. But in case where small scripts are preferred (maybe as a compact version for on-line distribution), implicit declarations are also possible. Implicit declarations are enabled as follows:
variable _implicit
When implicit declarations are enabled, all the variables appearing in handler declarations are managed by Sysquake exactly as if they had been declared explicitly in variable statements. Implicit variables cannot have the _nondumpable attribute; however, you can declare only the nondumpable variables and have a variable _implicit declaration for all other variables.
Definition of constants
Some numbers are used as identifiers at different places. For instance graphical objects which can be manipulated with the mouse are tagged with an identifier when they are displayed, and this identifier is used to recognize them when the user clicks them. Numeric ID can be replaced with more meaningful names with constant definitions. Constant definitions begin with the keyword define, followed by the identifier, the equal character, and the integer value or the keyword _auto. Constants defined with _auto are numbered consecutively from 1. In the example below, kGainID is 1, kDelayID is 2, etc.
Example
define kGainID = _auto define kDelayID = _auto define kMassID = _auto define kYMaxID = 1000
The definition is valid not only in the declaration part of SQ files everywhere an integer number could appear, but also in functions defined in function blocks. If the definition is placed inside a function block, it cannot be referenced in handler declarations.
Init and Terminate Handlers
The purpose of init handlers is to provide default values for the variables and the plot options. Variables are initialized with the values returned by the init handler(s); other variables (those not enumerated in the left-hand part of init handler declarations) are set to the empty array []. Initial plots are set up with commands subplots, subplotprops, subplotpos, scalesync or subplotsync, and subplotparam. By default, the first figure is displayed. The same functions can be used without any argument to retrieve the corresponding values; this is especially useful to store these settings in a data file.
Example
function [A,B,R,S] = init // default values for A, B, R and S A = [1,1]; B = 1; R = 1; S = 5; // default plots subplots('roots\tstep'); // kind of plots, separated with tabs and line feeds // (one row of two subplots) props = [0,-2,0,-1,1 // lin. scale, zoom on [-2,0,-1,1] 0,nan,nan,nan,nan]; // default lin. scale subplotprops(props);
When variables are initialized separately, i.e. when the init handler does not return multiple output arguments, and the initialization expression does not depend on another variable, initialization can be done in the variable declaration statement. The following declaration
variable x = 2
is equivalent to
variable x init x = 2
Should resources be allocated by the init handler, a terminate handler can be defined to release them. This is necessary only if commands with side effects are used, for instance if a file is opened by the init handler. The terminate handler is declared like other handlers. Output variables are useless, because the variables are discarded immediately after its execution.
terminate termFn(var)
Example
Here is an example of init and terminate handlers which open and close a file, respectively. Other handlers can read from or write to the file. In this example, a custom input handler should be written to avoid resetting the value of the file descriptor fd (see below).
variable fd init fd = init terminate terminate(fd) functions {@ function fd = init fd = fopen('data.txt', 'rt'); function terminate(fd) fclose(fd); @}
Multiple init and terminate handlers can be declared; they are all executed in the same order as they are declared.
Resize handler
The resize handler is a function called when the dimensions of the area where the figures are displayed are changed. This occurs typically when the user resizes the window. The resize handler is declared with the keyword resize. Two special input arguments can be used to retrieve the new size of the subplots area:
Name | Description |
---|---|
_height | height of the subplots area in pixels |
_width | width of the subplots area in pixels |
The height and width are usually given in pixels for the screen, and in points (1/72 inch) for high-resolution devices such as printers.
The purpose of the resize handler is to change the subplot layout, for instance to reduce their number when the display area is too small. The resize handler is not called at startup; arguments _height and _width can also be passed to the init handler.
Example
In the fragment of SQ file below, the subplot layout is set in the function setSubplots, which is called as an init handler at startup and as a resize handler when the window dimensions are changed. Depending on the width of the window, one or two figures are displayed. A separate init handler is declared for variable initialization.
variable a, b, c init (a, b, c) = initVar init setSubplots(_width) resize setSubplots(_width) ... functions {@ function (a, b, c) = initVar ... function setSubplots(width) if width > 300 subplots('Fig. 1\tFig. 2'); else subplots('Fig. 1'); end ... @}
Figure Declaration
Each figure is declared by a figure declaration, introduced by the figure keyword:
figure "Figure Name"
The string which follows the figure keyword is the figure name, which can contain any character and space. It is displayed as a title above the figure and in the Plots menu, and is used by the subplots command as an identifier. The figure declaration is followed by the draw, mousedown, mousedoubleclick, mousedrag, mousedragcont, mouseup, mouseover, mouseout, mousescroll, dragin, and dragout handlers corresponding to the figure. Only the draw handler is required.
The Plots menu follows the order of the figure declarations. Separators (horizontal lines in the menu) can be inserted to make the menu easier to read, with the separator keyword.
Related figures can be grouped in submenus. Figure entries are enclosed between beginsubmenu "name" and endsubmenu lines, where name is the name of the submenu.
Example
figure "Input Signal" draw drawInputSignal(u); mousedrag u = dragInputSignal(u, _id, _y1); mouseover _cursor = overInputSignal(_id) figure "Output Signal" draw drawOutputSignal(y); separator figure "Model" draw drawModel(u, y); separator beginsubmenu "Response" figure "Impulse" draw drawImpulse(u, y) figure "Step" draw drawStep(u, y) endsubmenu
Draw Handler
Each figure has one draw handler, declared with the draw keyword:
draw drawFn(v1, v2, ...)
The handler draws the figure with graphical commands such as plot, circle, or step. The scale command may be used to set the default scale and the scale options.
The draw handler typically has input arguments; but it never has any output argument. It also accepts the special input argument _param (see below).
Mousedown, Mousedoubleclick, Mousedrag, Mousedragcont, Mouseup, Mouseover, and Mouseout, and Mousescroll Handlers
The mousedown, mousedoubleclick, mousedrag, mouseup, mouseover, mouseout handlers are called when the mouse is over the figure they are defined for and the mouse button is pressed, double-clicked, held down, released, left unpressed, or move outside respectively. The mousedragcont handler is an alternative to the mousedrag handler (see below). The mousescroll handler is called when the mouse if over the figure and the scroll wheel or scroll ball is moved. The dragin and dragout handlers are used for drag operations between different figures. The table below summaries their differences. For example, the mousedrag handler accepts variables (declared with the variable keyword) as input and output arguments; it can set the special variable _msg with an output argument, and its role is to modify variables during a mouse drag.
Handler | Var. | _msg | _cursor | Role |
---|---|---|---|---|
mousedown | in/out | - | - | Prepares a drag |
mousedoubleclick | in/out | - | - | 2nd click of a dble-click |
mousedrag | in/out | out | - | Performs a drag |
mousedragcont | in/out | out | - | Performs a drag |
mouseup | in/out | - | - | Cleans up after a drag |
mouseover | in/out | out | out | Gives visual feedback |
mouseout | in/out | - | - | Cleans up visual fdback |
mousescroll | in/out | out | - | Performs a change |
The purpose of the mouseover handler is to give to the user visual feedback about what is below the cursor, with a message or the shape of the cursor. It is also possible to change the variable, and though them all the displayed graphics, when the mouse is moved over a figure. This should be used only to give some additional hint about what is below the cursor (such as highlighting a matching element in another graphics), not as a substitute of mousedrag, because the user has no way to select what he wants to manipulate. The mouseout handler should be used to restore the state of the variables.
The purpose of the mousedown, mousedoubleclick, mousedrag, mousedragcont, and mouseup handlers is to handle interactions. They receive as input arguments the position of the mouse and the value of variables, and return as output arguments new values for the variables. Unless an error occurs, the mousedown, mousedrag, mouseup, and draw handlers are called with the following arguments. The placeholder S0 represents the set of variables before the mouse down, and S1 the set after the mouse up.
S0 = mousedown(S0) S0 = draw(S0) S1 = S0 while the mouse button is down S1 = mousedrag(S0) S1 = draw(S1) end S1 = mouseup(S0) S1 = draw(S1)
The Undo command discards S1 and reverts to S0. Hence the changes caused by mousedown should be limited to computing auxiliary values used for the drag operation, but invisible to the user. If an error occurs, the sequence is aborted, and S1 is discarded.
The sequence above can be reduced to much simpler forms. For dragging an element, it is often sufficient to define mousedrag and draw:
while the mouse button is down S1 = mousedrag(S0) S1 = draw(S1) end
To change the controller on a simple mouse click, only the mouseup handler is used (as explained above, the mousedown function does not change S1).
S1 = mouseup(S0) S1 = draw(S1)
The mousedrag handler is always executed immediately after the mousedown handler if there is one, even if the mouse is not moved. To display some information without changing anything when you drag the mouse and remove any trace afterwards, you can define a mousedrag handler which sets variables used by the draw handlers, then discard S1 with a mouseup handler which just contains the cancel command.
The mousedrag handler uses as input arguments the values the variables had before the drag. After the drag, the variables will be affected only by the position of the mouse at the beginning and at the end of the drag operation; the trajectory of the mouse is lost. This behavior is often desirable. In the infrequent cases where it is not, the mousedrag handler should be replaced with a mousedragcont handler. Sysquake calls the handlers in the following sequence:
S0 = mousedown(S0) S0 = draw(S0) S1 = S0 while the mouse button is down S1 = mousedragcont(S1) S1 = draw(S1) end S1 = mouseup(S0) S1 = draw(S1)
The only difference with the mousedrag handler is that mousedragcont handlers use the new values of the variables as input arguments and modify them continuously. They can record the whole trajectory of the mouse (or any information derived from the trajectory). For example, a mousedragcont handler could be used to draw in a pixmap.
The mousedoubleclick handler is called when the mouse button is pressed down for the second time shortly after the first one. The mousedown, mousedrag, mouseup and draw handlers (if they exist) have been called for the first click.
The purpose of the mousescroll handler is to to change some quantity related to a figure or an object. Since all mouses do not have wheels, tracking balls, or other scrolling device, the mousescroll handler should be a shortcut to some other way of performing the same action, for instance with a dialog box or a slider. This is espacially true for horizontal scroll which is not supported by most wheels. Note also that some wheels have a coarse resolution.
Predefined variables
In addition to the variables defined with the variable keyword, you can use the following predefined variables as input argument for the mousedown, mousedoubleclick, mousedrag, mouseup, and mouseover handlers.
Name | Purpose |
---|---|
_z | initial position of the mouse as a complex number |
_x | initial horizontal position of the mouse |
_y | initial vertical position of the mouse |
_rho | initial distance between the position of the mouse and the origin |
_theta | initial angle of the vector from the origin to the position of the mouse |
_z0 | initial position of the clicked element as a complex number |
_x0 | initial horizontal position of the clicked element |
_y0 | initial vertical position of the clicked element |
_p0 | initial position of the clicked element as a 2D or 3D vector |
_rho0 | initial distance between the position of the clicked element and the origin |
_theta0 | initial angle of the vector from the origin to the position of the clicked element |
_z1 | current position of the mouse as a complex number |
_x1 | current horizontal position of the mouse |
_y1 | current vertical position of the mouse |
_p1 | current position of the mouse as a 2D or 3D vector |
_rho1 | current distance between the position of the mouse and the origin |
_theta1 | current angle of the vector from the origin to the position of the mouse |
_str1 | current string parameter |
_dx | horizontal displacement (_x1-_x) or horizontal scrolling |
_dy | vertical displacement (_y1-_y) or vertical scrolling |
_dz | displacement (_z1-_z) or scrolling as a complex number |
_kx | factor the horizontal position is multiplied by (_x1/_x) |
_ky | factor the vertical position is multiplied by (_y1/_y) |
_kz | complex factor the position is multiplied by in the complex plane (_z1/_z) |
_q | additional data specific to the plot |
_m | true if the modifier key (Shift key) is held down |
_id | ID of the manipulated object |
_nb | number of the manipulated trace (1-based) |
_ix | index of the manipulated point (1-based) |
_param | subplot parameter |
The value of _z0 is constrained to existing elements of the plot, contrary to _z which represents the actual position of the mouse click. The value of _z1 is not constrained. If you need the original position of the manipulated object, you should usually use _z0 (or _x0 or _y0) and replace it with _z1. The value of _z is used when the amplitude of the move is considered, not the initial and final positions, or when no object is selected.
The value of _id is the value you define in plot commands for elements you want to manipulate. You can use it to recognize different graphical objects in the same figure. The value of _nb defines which line is manipulated among those plotted by the same command (most graphical commands can draw multiple lines or multiple responses). The value of _ix, an integer starting from 1, is always defined when the user selected an element of the figure; it is useful mainly for traces where you define explicitly each point, e.g. plot and line. Note that you need not use IDs; all clicks generate calls to the mouse handler(s), and _z, _z1, _x, _x1, _y, and _y1 are always defined.
For each subplot, Sysquake maintains a piece of data which can be used freely for any purpose. This data is passed to and from all figure-related handlers with _param. It is initialized to the empty array []. It is useful in cases where the same figure is displayed in different subplots; _param may contain for instance initial conditions for a simulation. It may also be used as input or output argument of menu, import and export handlers and in their _enable and _checkmark expression; in these cases, the handlers are enabled when a single subplot is selected.
As output argument of the mousedrag and mouseover handlers, you can use the following special variables:
Name | Purpose |
---|---|
_msg | string displayed in the status bar at the bottom of the window |
_param | new value of the subplot parameter |
The variable _msg is typically set to describe what is below the cursor, with possibly some numeric values using sprintf. If it contains a linefeed ('\n') or carriage return ('\r') character, it is truncated.
As output argument of the mouseover handler, you can also use the following special variable:
Name | Purpose |
---|---|
_cursor | true to display the cursor as a finger in manipulate mode |
The shape of the cursor gives a hint to the user whether he can manipulate the object below the cursor. This can makes the use of SQ files much easier. By default in manipulate mode, the finger cursor is displayed in figures where mousedown, mousedrag and/or mouseup handlers are defined; otherwise, the cursor is the standard arrow. Sliders, buttons, and other controls (see command slider) are a special case; the finger is displayed only when the cursor is over a slider. In case you define mouse handlers, though, you often want to specify explicitly whether any manipulation is possible.
Example
Here are handlers for displaying the roots of a polynomial A that you can manipulate.
variable A = poly([-1, -2-2j, -2+2j]) figure "Roots" draw drawRoots(A) mousedrag A = dragRoots(A, _z0, _z1) mouseover _cursor = overRoots(_id) functions {@ function drawRoots(A) // plot the roots of polynomial A plotroots(A, 'x', 1); scale lock; function A = dragRoots(A, z0, z1) // if the click is too far away from a root of A, z0 is empty if isempty(z0) cancel; // discard the dragging operation end A = movezero(A, z0, z1); // move the root function cursor = overRoots(id) // displays a finger if the cursor is over a root, // and a plain cursor otherwise cursor = ~isempty(id); @}
One thing to note about cursors, if your mouseover handler declaration specifies _cursor as the (or one of the) output(s), and your mouseover handler is canceled by cancel, the cursor is set to the plain arrow. Hence you can have code like
if isempty(id) cancel; end
early in your mouseover handler.
Here is an example which shows how the mousedragcont handler can be used to accumulate the position of the mouse during a drag.
variable x, y // position of the mouse figure "Plot" draw draw(x, y) mousedragcont (x, y) = drag(x, y, _x1, _y1) functions {@ function draw(x, y) // display the trace of the mouse scale('equal', [0,10,0,10]); plot(x, y); function (x, y) = drag(x, y, _x1, _y1) // add the current position of the mouse to x and y x(end + 1) = _x1; y(end + 1) = _y1; @}
Handlers for a specific ID
Instead of a single handler which is called when required whatever there is under the mouse, you can restrict the handler to a specific ID value. The handler is called only if the mouse is over an object with that ID. You can have multiple handlers of the same type with different ID. This can simplify the code: the handler does not have to check that the ID is not empty and has the correct value, and multiple handlers can have a smaller number of arguments.
The ID is placed directly after the handler keyword, either as an integer or a constant name defined with define. The example below is a complete SQ file where you can move two points, the first one horizontally and the second one vertically. Note that the mousedrag handlers are so simple they do not need a function. Since there are mouseover handlers with specific ID, but no generic one, the cursor is displayed as a finger only over one of the points.
variable p1x = 0.3 variable p2y = 0.6 define kP1Id = 1 define kP2Id = 2 figure "Points" draw drawPoints(p1x, p2y) mousedrag kP1Id p1x = _x1 mouseover kP1Id _msg = overP1 mousedrag kP2Id p2y = _y1 mouseover kP2Id _msg = overP2 functions {@ function drawPoints(p1x, p2y) scale([0, 1, 0, 1]); plot(p1x, 0.5, 'x', kP1Id); plot(0.5, p2y, 'o', kP2Id); function msg = overP1 msg = 'Point 1'; function msg = overP2 msg = 'Point 2'; @}
Fighandler Handler
Separate handlers for the different events which can occur result typically in small functions. While the implementation of these functions can be very simple, code reuse is complicated: only the function definitions can be stored in separate libraries, while the multiple handler declarations (often at least a draw handler, a mousedrag handler and a mouseover handler) must be inserted in the SQ file.
Fighandler handlers replace all the handlers related to figures, i.e. draw, mousedown, mousedoubleclick, mousedrag, mousedragcont, mouseup, mouseover, mouseout, mousescroll, dragin, and dragout. In the handler, the action to perform is given by _event, which is typically used in a switch construct. Upon events to be ignored, cancel(false) should be executed.
Example
Here is the example for figure "Roots" given above, rewritten with a single fighandler function instead of separate draw, drag and over handlers.
variable A = poly([-1, -2-2j, -2+2j]) figure "Roots" fighandler A = figRoots(A) functions {@ function A = figRoots(A) switch _event case 'draw' plotroots(A, 'x', 1); scale lock; case 'drag' if isempty(_z0) cancel; end A = movezero(A, _z0, _z1); case 'over' _cursor(~isempty(_id)); otherwise cancel(false); end @}
The interactive manipulation of graphical elements is not suited for all the modifications of the variables. To change the structure of a controller or to specify numeric values, a command triggered by a menu entry, possibly with the help of a dialog box, is more convenient. This is the purpose of menu handlers, which are installed in the Settings menu. Menu handlers are declared as follows:
menu "Title" (out1, out2, ...) = function(in1, in2, ...)
The string is the menu entry title. The function typically gives new values to one or more variables. If numeric values must be entered or modified by the user, or if a confirmation is required, the dialog command should be used. To cancel the action, the cancel command must be used to prevent a new set of variables from being created in the Undo buffer.
Like figures, menu entries are listed in the Settings menu in the same order as their declaration in the SQ file. Separators can be added with the separator keyword, and entries can be grouped in submenus with beginsubmenu and endsubmenu keywords. In addition, instead of the default Settings menu, menu entries can be grouped in different menus with the beginmenu and endmenu keywords. On versions of Sysquake which do not support multiple menus, these keywords are also accepted for compatibility; separators are inserted automatically.
The appearance of the menu entries can be modified in two ways: they can be disabled (they are written in gray and cannot be selected) with the _enable keyword, and they can be decorated with a check mark with the _checkmark keyword. Both keywords must appear between the menu handler title and the handler function declaration. They use a single LME boolean expression (typically based on the variables) as argument.
Examples
In the following example, the variable color has the value 0 for black, 1 for blue and 2 for red.
variable color, polygon, sides init color = 0 init polygon = 0 init sides = 3 beginsubmenu "Color" menu "Black" _checkmark(color==0) color=0 menu "Blue" _checkmark(color==1) color=1 menu "Red" _checkmark(color==2) color=2 endsubmenu separator menu "Polygon" _checkmark(polygon) polygon=toggle(polygon) menu "Number of Sides..." _enable(polygon) sides=setNumberOfSides(sides) functions {@ function b = toggle(b) b = ~b; function sides = setNumberOfSides(sides) (ok, sides) = dialog('Number of sides:', sides); if ~ok cancel; end @}
In the fragment below, two menus are declared:
beginmenu "Color" menu "Black" _checkmark(color==0) color=0 menu "Blue" _checkmark(color==1) color=1 menu "Red" _checkmark(color==2) color=2 endmenu beginmenu "Parameters" menu "Polygon" _checkmark(polygon) polygon=toggle(polygon) menu "Number of Sides..." _enable(polygon) sides=setNumberOfSides(sides) endmenu
Sysquake will display two menus whose titles are "Color" and "Parameters".
Keydown Handler
To react to a key pressed down, multiple specific keydown handlers and a single default keydown handler can be installed. Specific keydown handlers are declared as follows:
keydown "k" (out1, out2, ...) = function(in1, in2, ...)
The string "k" contains the key which triggers the handler when pressed. Most keys are denoted by the corresponding character; arrow keys are denoted by "up", down, left, and right.
The default keydown handler is declared as follows:
keydown (out1, out2, ...) = function(in1, in2, ...)
It is triggered if a key without a specific keydown handler is pressed. In both kinds of keydown handlers, the function typically gives new values to one or more variables. In addition to the variables defined with the variable keyword, you can use the following predefined variable as input argument:
Name | Purpose |
---|---|
_key | key pressed as a string |
Example
In the following example, the variable n is incremented or decremented when the user type "+" or "-", respectively. When another key is pressed, cancel is executed, so that no new undo frame is created.
variable n init n = 1 keydown n = keydownHandler(n, _key) functions {@ function n = keydownHandler(n, key) switch key case '-' n = n - 1 case '+' n = n + 1 otherwise cancel; end @}
Make Handler
It is often useful to have variables which hold the result of a computation based on other variables. If several figures depend on it, this avoids the need to calculate it in each draw handler and reduce the computation time. The computation may be performed in the handlers which change the independent variables. However, this forces to add arguments; handlers become more difficult to write, especially when dependencies are complicated. Make handlers describe the way to calculate variables not as the direct effect of some user action, but when their output arguments are required by another handler. The name make is borrowed from the programming utility which builds a complicated project based on a set of rules and dependencies. Make handlers do the same for variables. Their declaration is simply
make (output_variables) = makeHandler(input_variables)
Suppose that a variable x is changed, and that the draw handler of a figure uses variable y as input argument. If the following make handler is declared,
make y = f(x)
Sysquake calls the function f to compute y based on x. More complex dependencies may be defined using several make handlers. There must not be circular dependencies; no variable may depend on itself through one or more make handlers.
When a make handler gives the value of a single variable, it can be specified in the variable declaration statement. The following declaration
variable x, y variable z = x + y
is equivalent to
variable x, y, z make z = x + y
Examples
The examples below represent three different ways to implement the same behavior. A variable x may be modified either in a menu handler or by interactive manipulation of a figure. Two figures uses variable y, which is a function of x.
The first implementation does not store y in a variable declared to Sysquake. It is computed in each draw handler.
variable x init x = init menu "Change x" x = changeX(x) figure "Figure 1" draw drawFig1(x) mousedrag x = dragFig1(_x1) figure "Figure 2" draw drawFig2(x) functions {@ function x = init x = 3; function x = changeX(x) // (dialog box to let the user edit x) function drawFig1(x) y = f(x); // (plot based on x and y) function x = dragFig1(x1) x = x1; function drawFig2(x) y = f(x); // (other plot based on x and y) function y = f(x) // computation of y @}
The second implementation declares y, which is computed in all handlers which change the value of x. When both figures are displayed, y is computed only once when the menu or mousedrag handler is invoked.
variable x, y init (x, y) = init menu "Change x" (x, y) = changeX(x) figure "Figure 1" draw drawFig1(x, y) mousedrag (x, y) = dragFig1(_x1) figure "Figure 2" draw drawFig2(x, y) functions {@ function (x, y) = init x = 3; y = f(x); function (x, y) = changeX(x) // (dialog box to let the user edit x) y = f(x); function drawFig1(x, y) // (plot based on x and y) function (x, y) = dragFig1(x1) x = x1; y = f(x); function drawFig2(x, y) // (other plot based on x and y) function y = f(x) // computation of y @}
The third implementation also declares y, which is computed by a make handler when it is needed by another handler. Note how each handler is as simple as it can be. If none of the two figures is displayed, y is not computed at all.
variable x, y init x = init make y = f(x) menu "Change x" x = changeX(x) figure "Figure 1" draw drawFig1(x, y) mousedrag x = dragFig1(_x1) figure "Figure 2" draw drawFig2(x, y) functions {@ function x = init x = 3; function x = changeX(x) // (dialog box to let the user edit x) function drawFig1(x, y) // (plot based on x and y) function x = dragFig1(x1) x = x1; function drawFig2(x, y) // (other plot based on x and y) function y = f(x) // computation of y @}
Function Definitions
Functions declared by the different handlers must be built-in, defined in separate libraries, or defined in one or several function blocks.
A library (.lml file) must be referenced with the use keyword:
use library
The library name does not include the file suffix .lml. Libraries are searched in the same folders or directories as SQ files.
Functions defined directly in the SQ files are placed in a function block, with the following syntax:
function {@ ... @}
The keyword functions is a synonym of function. The functions can be defined in any order and in any number of blocks. In addition to functions declared as handlers, other functions can be defined to extend the set of built-in commands.
Function blocks can also include use statements. Whether to place use statements outside or inside function blocks is a matter of style. SQ-level use statements should be preferred for libraries which define functions called directly as handlers.
Embedded files
In some cases, especially when a large amount of constant data is required in an SQ file, it may be convenient to store these data in the SQ file itself and to use the standard input functions of LME, such as fread and fscanf, to retrieve them. This avoids the need of separate files. Blocks of texts, introduced by keyword embeddedfile, provide such a facility:
embeddedfile "name" {@ Embedded file contents... @}
The contents of the block may be as large as required. They can be either plain text, or binary data encoded with base64. The name is used to identify the embedded file; it corresponds to the argument of function efopen. The number of embedded files in an SQ file is not limited.
Title, Version, and Help
SQ files may include a title, version information, and explanations about their purpose and how they can be used. The title is specified by a string:
title "..."
It is used instead of the file name in some windows and menus titles.
The version and help are provided in blocks of text:
version {@ Text... @} help {@ Text... @}
In the block of text, paragraphs are separated by one or more empty lines. Initial and trailing empty lines and spaces are ignored. For cases where preformatted text is preferred, such as for program code or equations, the HTML tags <pre>/</pre> are used. Here is an example:
help {@ Here is an identity matrix: <pre> [ 1 0 0 ] I = [ 0 1 0 ] [ 0 0 1 ] </pre> @}
Upon user request, Sysquake displays the version or the help of the current SQ file.
The purpose of the version text is to give any version number, release date, and copyright information relevant to the SQ file.
User interface
The standard user interface of Sysquake has menus to customize the layout of figures and their options. Some of them can be disabled once the layout has been carefully tuned. This is done with the userinterface keyword, followed by comma-separated options:
userinterface option1, options, ...
Here is the list of supported options:
Name | Purpose |
---|---|
appmenus (default) | Standard menus |
noappmenus | No standard menus |
figoptions (default) | Submenu "Figure>Options" for margin, title, etc. |
nofigoptions | No submenu "Figure>Options" |
plotchoice (default) | Menus "Plots" and "Layout" |
noplotchoice | No menus "Plots" and "Layout" |
resize (default) | The figure window can be resized |
noresize | The figure window has a fixed size |
selectall (default) | Menu entry "Edit>Select All" |
noselectall | No menu entry "Edit>Select All" |
toolbar (default) | Figure toolbar |
notoolbar | No figure toolbar |
Depending on the version of Sysquake, the menus or menu entries are disabled or completely removed. Option noplotchoice also disables the dragging of subplots. Option noappmenus removes most menus and menu entries.
To have finer control on exactly which plot options should be available, the Figure menu should be removed with nofigoptions and the appropriate options added back in menus with sqguicmd. The code below shows a reduced menu with logarithmic scale for x and y axis, and labels and legends.
userinterface nofigoptions beginmenu "Options" menu "Log x" _enable(sqguicmd('scale/log-x','e')) _checkmark(sqguicmd('scale/log-x','c')) sqguicmd('scale/log-x') menu "Log y" _enable(sqguicmd('scale/log-y','e')) _checkmark(sqguicmd('scale/log-y','c')) sqguicmd('scale/log-y') separator menu "Label" _enable(sqguicmd('figure/label','e')) _checkmark(sqguicmd('figure/label','c')) sqguicmd('figure/label') menu "Legend" _enable(sqguicmd('figure/legend','e')) _checkmark(sqguicmd('figure/legend','c')) sqguicmd('figure/legend') endmenu
SQ Data Files and Input/Output Handlers
SQ Data files (or SQD files) are composed of a succession of LME statements which define variables. The contents of these variables are used by an input function which translates them to the variables used by Sysquake itself and which restores the subplots and their settings. This filtering step serves two purposes:
- Sysquake can use more or other variables than what the SQ Data files define. For instance, a proportional-integral-derivative controller (PID) could be defined by three parameters, but described by a transfer function in the Sysquake variables.
- A validity check is often useful, because the data files are text files which can be written by the user.
To avoid the hassle to write input and output handlers when these advanced features are not needed, Sysquake has a default behavior if the handlers are not declared in the SQ file. If the output function is missing, all the variables and the settings are written to the file. If the input function is missing, the variables declared in the SQ file are set to the values defined in the SQD file, or to the empty array [] if they are not found.
To permit the user to simply open the SQ data file without specifying the SQ file which can make use of it, the first line should form a valid LME comment with the name of the associated SQ file:
%SQ sqfile.sq ...
When the user opens a file, the first line is read. If it corresponds to a data file, the associated SQ file is read and its init function processed. Finally, the data file is executed and the contents of its variables converted by the input function. If the file opened by the user is a SQ file, it is read and its init function executed.
A typical data file could be
%SQ greatDesign.sq A = [1,2]; B = 2; kp = 20; _plots = 'greatView\tstep'; _plotprops = [0,-1,1,-1,1; 0,10,50,0,0];
Variables A, B, and kp correspond directly to variables defined in the SQ file. Variable _plots is the list of the subplots which were displayed when the file was saved; it corresponds to the input or output argument of the command subplots. Variable _plotprops is the properties of the subplots (options like logarithmic scaling and grids, and zoom); it corresponds to the input or output argument of the command subplotprops. The names _plots and _plotprops are those used by the default input and output handlers; it is better to use them also if you write your own handlers.
Input and Output Handlers
To generate and read back the data file above, the following handlers can be declared:
output outputHandler(_fd, v1, v2, ...) input (v1, v2, ...) = inputHandler(_fd)
The output handler must write to file descriptor _fd the contents of the variables. You need not save all the variables; for example, if pol is a polynomial and you have defined a variable r to store its roots, saving pol is enough; you can restore r in the input handler with r=roots(pol). The variable _fd represents the file descriptor of the output file:
Name | Purpose |
---|---|
_fd | file descriptor of the output or input file |
Like all special arguments, _fd can also be used directly in the function definitions, without being passed from the handler declaration.
You can use it with functions such as fprintf and dumpvar. You can write the data using any format. However, it is better to follow the format of standard SQD files, as described above, with perhaps calls to the built-in functions of LME. The first line, which contains the name of the SQ file, is required to permit Sysquake to find the appropriate SQ file.
The input handler reads the SQD file using the file descriptor _fd, and produces values stored in Sysquake variables. The input handler is always called after the init handler; variables which are not output arguments of the input handler keep the value set by the init handler.
If the SQD file contains variable assignments, the easiest way to parse it is to read the whole file with fread, and to interpret the string with sandbox.
The following example shows how to write input and output handlers which save and restore three variables, check the validity of the variables in the SQD file, and save and restore the subplots.
output myOutputHandler(A,B,kp) input (A,B,kp) = myInputHandler() function {@ function myOutputHandler(A,B,kp) // write header line fprintf(_fd, '%SQ greatDesign.sq\n\n'); // write variables dumpvar(_fd, 'A', A); dumpvar(_fd, 'B', B); dumpvar(_fd, 'gain', kp); // different name // write current subplot settings dumpvar(_fd, '_plots', subplots); dumpvar(_fd, '_plotprops', subplotprops); if ~isempty(subplotpos) dumpvar(_fd, '_plotpos', subplotpos); end function (A,B,kp) = myInputHandler() // read and interpret SQD file into struct s str = fread(_fd, inf, '*char'); s = sandbox(str); // map the SQD variables to the SQ variable kp = s.gain; // check the validity of the transfer function if length(s.A) >= length(s.B) dialog(['The transfer function of the system B/A ', 'must be strictly causal.'); cancel; end A = s.A; B = s.B; // restore subplot settings subplots(s._plots); subplotprops(s._plotprops); subplotpos(s._plotpos); @}
The contents of what might be a data file can also be written in the data block of a SQ file. The user may find more convenient to save the data in the SQ file itself, either to have an SQ file with new default values or to distribute it to other people. The data block should be at the end of the SQ file, so that all the variables and the functions used by the input handler are defined. You can write it manually, but its purpose is to be written by Sysquake when the user chooses to save the data as an SQ file. Its syntax is
data {@ contents of a data file @}
Error Messages
Here is the list of error messages; some of them may appear in a message box when you read a bad file, but many are internal errors. LME errors are listed in the LME Reference chapter and are caused by a bad function either at compile time or at execution time.
- Exhausted resources
- Not enough memory for the current operation.
- Too small name buffer
- Not enough memory for the table of variable names.
- No more name slot
- Too many variable names.
- Too small Sysquake buffer
- Not enough memory to launch Sysquake.
- Not enough memory for decoding SQ file
- Class file too complex.
- Too small Undo buffer
- Not enough memory to create the Undo structures.
- Too many variables for the Undo buffer
- Undo structures too small for the large number of variables.
- Variable too large for the Undo buffer
- The new value given to a variable is too large.
- Variable not found
- Attempt to retrieve a variable which has not been defined (should never occur).
- Variable already exists
- Attempt to redefine a variable.
- Standard variable unexpected here
- A standard variable, such as _x1, is not supported here.
- Syntax error in class file
- Unbalanced quotes or parenthesis, missing element, or other syntax error in the class file.
- Bad block of text
- Block of text (such as a block of function definitions) without end mark.
- Bad variable definition
- The variable keyword is not followed by a valid variable name.
- Bad function definition
- The function keyword is not followed by a valid function call.
- Undefined element
- The keyword is unknown.
- Nonexistent set of variables in the Undo buffer
- Attempt to revert to an undo state which is not available.
- Not a data file
- The file does not begin with the expected "% SQ" characters.
- Cannot undo
- Too many attempts to undo.
- Cannot redo
- Too many attempts to redo.