STUI scripts are a way to automate tasks you perform frequently.
Scripts are written in Python. If you are not familiar with python, I strongly recommend the python tutorial at Python's home. A knowledge of python's basics will make script writing much easier.
Once you have the basics of python, I recommend looking through the Scripting Tutorial. It offers many simple example scripts. Other scripts may be found in <tui_root>/STUI/Scripts.
A STUI script must be a text file whose name ends in ".py". The file must include either a ScriptClass class or a run function. I strongly suggest you use a ScriptClass for all but the very simplest of scripts.
A class with the following special methods:
run(sr)
In addition, ScriptClass may contain any other methods and any variables you like. For examples, see the Scripting Tutorial.
For very simple scripts, you may prefer to just define a run(sr) function. Your script may also contain init(sr) and/or end(sr) functions, and all work the same as the ScriptClass methods discussed above. However, if your script needs init and/or end, I strongly recommend writing it as a ScriptClass, so data can more easily be passed around.
init(sr)
end(sr)
To report an error, raise sr.ScriptError. For example:
sr.ScriptError
def run(sr): ... if expTime <= 0: raise sr.ScriptError("Specify exposure time")
This will print the specified message to the status bar and halt the script. Other exceptions do this, as well, but also print a traceback to the error log. Thus other exceptions are ideal for internal bug checks, but sr.ScriptError is best for reporting "normal" errors.
The run function (as well as init and end, if supplied) is passed a ScriptRunner object. This script runner provides support for your script, including:
run
init
end
The following methods wait, so a yield is required:
getKeyVar(keyVar, ind, defVal)
Return the current value of a keyword variable. Note: if you want to be sure the keyword data was in response to a particular command that you sent, then use the keyVars argument of startCmd or waitCmd, instead. Inputs: keyVar: the keyword variable of interest ind: the index of the desired value. If None then the whole list of values is returned. defVal: the default value to return if the keyVar's data is invalid (this can happen if it was not supplied by the hub since the last time you connected). If defVar is omitted then the script fails if the keyVar's data is invalid. A keyword variable contains data read from an instrument, the telescope or other "actor". Although you can create your own keyword variables, you rarely have to do so. Every actor has an associated model in STUI containing most or all of the useful keyword variables for that device. Every keyword variable contains a list of 0 or more values. Often you only want one particular value. Thus the "ind" argument. See also waitKeyVar, which can wait for a value.
Return the current value of a keyword variable.
Note: if you want to be sure the keyword data was in response to a particular command that you sent, then use the keyVars argument of startCmd or waitCmd, instead.
Inputs:
keyVar
ind
defVal
A keyword variable contains data read from an instrument, the telescope or other "actor". Although you can create your own keyword variables, you rarely have to do so. Every actor has an associated model in STUI containing most or all of the useful keyword variables for that device.
Every keyword variable contains a list of 0 or more values. Often you only want one particular value. Thus the "ind" argument.
See also waitKeyVar, which can wait for a value.
showMsg(msg, severity=RO.Constants.sevNormal)
Display a message on the status bar. Inputs: msg: string to display, without a final "\n" severity: one of RO.Constants.sevNormal (default), sevWarning or sevError Note that the status bar also shows the execution status of the script; for instance it says "Done" when the script finishes successfully. If you want more permanent output, consider adding a text or label widget and writing your message to that.
Display a message on the status bar.
msg
severity
Note that the status bar also shows the execution status of the script; for instance it says "Done" when the script finishes successfully. If you want more permanent output, consider adding a text or label widget and writing your message to that.
startCmd( actor = "", cmdStr = "", timeLim = 0, callFunc = None, callCodes = opscore.actor.DoneCodes, timeLimKeyword = None, abortCmdStr = None, keyVars = None, checkFail = True, )
Start a command using the same arguments as waitCmd. Returns a command variable that you can wait for using waitCmdVars.
Start a command using the same arguments as waitCmd.
Returns a command variable that you can wait for using waitCmdVars.
waitCmd( actor="", cmdStr = "", timeLim = 0, callFunc=None, callCodes = opscore.actor.DoneCodes, timeLimKeyword = None, abortCmdStr = None, keyVars = None, checkFail = True, )
Start a command and wait for it to finish. A yield is required. If you want to read any data that must only be in response to this command and no other then be sure to use the keyVars argument. Inputs: actor: the name of the device which issued the keyword cmdStr: the command; no terminating \n wanted timeLim: maximum time before command expires, in sec; 0 for no limit (which is normally what you want). callFunc: rarely needed; see opscore.actor.CmdVar for details callCodes: rarely needed; see opscore.actor.CmdVar for details timeLimKeyword: rarely needed; see opscore.actor.CmdVar for details abortCmdStr: a command string that will abort the command. This string is sent to the actor if the command is aborted, e.g. if the script is cancelled while the command is executing. keyVars: a sequence of 0 or more keyword variables to monitor. Any data for those variables that arrives in response to this command is saved and can be retrieved using cmdVar.getKeyVarData or cmdVar.getLastKeyVarData, where cmdVar is returned in sr.value. checkFail: check for command failure? If True (the default) command failure will halt your script. See also startCmd and waitCmdVars.
Start a command and wait for it to finish. A yield is required. If you want to read any data that must only be in response to this command and no other then be sure to use the keyVars argument.
keyVars
actor
cmdStr
timeLim
callFunc
callCodes
timeLimKeyword
abortCmdStr
checkFail
See also startCmd and waitCmdVars.
waitCmdVars(cmdVars)
Wait for one or more command variables (returned by startCmd) to finish. A yield is required. Inputs: cmdVars: one or more commands (opscore.actor.CmdVar) Returns successfully once all commands succeed, but fails as soon as any command fails. See also startCmd and waitCmd.
Wait for one or more command variables (returned by startCmd) to finish. A yield is required.
cmdVars
Returns successfully once all commands succeed, but fails as soon as any command fails.
See also startCmd and waitCmd.
waitKeyVar(keyVar, ind=0, defVal=Exception, waitNext=False)
Get data from a keyword variable. A yield is required. Inputs: keyVar keyword variable ind which value is wanted? (None for all values) defVal value to return if value cannot be determined (if omitted, the script halts) waitNext if True, ignores the current value and waits for the next transition. The data is returned in sr.value. If the value is currently unknown or if waitNext is true, wait for the variable to be updated. See also getKeyVar (which does not wait).
Get data from a keyword variable. A yield is required.
The data is returned in sr.value.
If the value is currently unknown or if waitNext is true, wait for the variable to be updated.
See also getKeyVar (which does not wait).
waitMS(msec)
Wait for a period of time specified in milliseconds. A yield is required. Inputs: msec number of milliseconds to pause
Wait for a period of time specified in milliseconds. A yield is required.
msec
waitThread(func, *args, **kargs)
Run a function as a background thread and wait for completion. A yield is required. Inputs: func: the function to run as a background thread any additional arguments are passed to func Any value returned by func is put into sr.value. Warning: func must NOT interact with Tkinter widgets or variables (not even reading them) because Tkinter is not thread-safe. (The only thing I'm sure a background thread can safely do with Tkinter is generate an event, a technique that is used to detect end of thread).
Run a function as a background thread and wait for completion. A yield is required.
func
Any value returned by func is put into sr.value.
Warning: func must NOT interact with Tkinter widgets or variables (not even reading them) because Tkinter is not thread-safe. (The only thing I'm sure a background thread can safely do with Tkinter is generate an event, a technique that is used to detect end of thread).
For more information, see opscore.actor.ScriptRunner.
Whenever your script calls an sr.wait... function (i.e. wants to wait for anything), it must use yield, as in:
yield sr.wait...(...)
This is a painful, but it could be much worse. Most languages would force you to break code into many small functions, each of which is registered as a separate callback function. That sort of programming is fine for GUIs (and is used extensively in STUI), but it is not a nice way to write a script.
If you forget the "yield", your script will plow ahead instead of waiting, which is a recipe for trouble. However, STUI will catch this problem the next time you use an sr.wait...() function, at which point it will kill your script, print a message to the status bar and print details to the error log.
Repetitive tasks can be separated out into sub-tasks. If you find your script running the same bit of code more than once, you may want to move that bit into a separate function.
To execute a sub-task that contains yield you must use yield. To remind yourself, I suggest using a name that starts with "wait". For example:
yield
class ScriptClass(object): def run(self, sr): #... yield self.waitDumbTask(sr, 3, 1000) #... def waitDumbTask(self, sr, nreps, dtime): for n in range(nreps): sr.showMsg("Wait %d" % n) yield sr.waitMS(dtime)
Only call a function with yield if the function contains yield. Note that __init__ and end cannot contain yield (cannot wait), so they cannot call functions that contain yield.
__init__
To make a script show up in the Scripts menu, put it into the Scripts directory (or a subdirectory) in the shared or local version of STUIAdditions. If the directory STUIAdditions or STUIAdditions/Scripts doesn't exist, create it. Some details:
The script runner includes a basic debug mode which is enabled by putting sr.debug = True in your script. In debug mode, getKeyVar, startCmd and all the wait... commands print a diagnostic message to the error log when they run. Also:
sr.debug = True
getKeyVar
startCmd
wait...
To reload a script (i.e. after modifying it), select Reload from the contextual pop-up menu for any of the control buttons (Start, Pause or Cancel).
Warnings:
print statements will print to to the error log. sr.showMsg is an alternative if standard output is not convenient for your platform.
APO Documentation contains pointers to manuals for the various instruments, the hub and the TCC.
The manual STUI Programming contains much information about the internals of STUI. Some of it is made simpler by using the scripting interface, but much of it is relevant -- especially if you want your script to use widgets (i.e. for user input or output).