TOC PREV NEXT INDEX

Put your logo here!


4 VLT GUI Guidelines

This chapter provides guidelines on how to write GUI applications in the VLT environment.

This chapter is meant to be for the `experienced user' and collects proposed solutions to cases or problems of common interest that have been found during the development of the VLT GUI applications.

4.1 RECOMMANDATIONS

During the development of the panel editor some procedures have become obsolete therefore this chapter is a reminder to use certain procedures to improve the robustness of the GUI.

1. Use panDbRead to access database values.
Some applications still use the `old' procedure dbRead to read database values, but this procedure does not handle properly error conditions and as consequence the application may crash. The following example shows the correct usage of panDbRead.

Example :

set value 0

set attr ":PARAMS:SCALARS.scalar_int32"

# panDbRead stores the database value in the variable `value'

if {[cequal [panDbRead $attr value] FAILURE]} {

process error condition

}


2. Use of seq procedures to access the database.
The use of seq procedures such as seq_dbreadSymbolic or seq_dbWriteSymbolic, without a proper error handling could make the application crash.
The panel editor provides higher level procedures containing a proper error handling such as panDbRead. or panDbWrite.

Example :

:

# Call seq_dbWriteSymbolic with error handling.

if {[catch {seq_dbWriteSymbolic $symbAddr $value} ret] } {

... error handling ...

}

:

# read a database value : the current value is stored into

# variable value.

if {[panDbRead ":PARAMS:SCALARS.scalar_int32" value] == "FAILURE"} {

.. process error condition ..

}

3. Do not add `by hand' widgets on the canvas.
If you add `by hand' widgets (not graphic elements) in the canvas the panel editor will not be able to edit the GUI any more. (If you need to do something specific that requires widgets to be added in a specific pattern - e.g.- the widgets re-create the positioning of M1 actuators - see example 4.3.12 )


4.2 OPTIMIZATION OF PERFORMANCES

This chapter gives some suggestions on how to optimize the CPU load of a panel.

The reduction of the load can involve both the pure GUI design but also how the control programs are designed.

4.2.1 Optimization of Control Programs

The performance of the GUI applications can also depend on the design of the processes and in particular on how the control processes write data in the database.

1. Do not write VECTORS / TABLES element by element
A change on any element of a table generates an event ( this also applies for the scanning mechanism). For example if two widgets are configured with the event mechanism to display elements of a vector/table , both widgets will get an event each time the control process writes data to any location of the vector/table.

A reasonable compromise would be
· Write in one go the whole structure (vector or table).
· Update tables columns wise or row wise ( that is all elements of one record)

2. Avoid to scan of large tables.
Another situation to be treated with care is when widgets are updated on "event" and corresponding database attributes are scanned-up to the WS.

For example, let's suppose to scan a 150 elements table and that the control process change 50 of them. In the worst case this can generate 150x50 events, with the good chance of killing the environment filling the message queue.

4.2.2 Optimization of panels

1. Use "polling" to update widgets displaying VECTORS / TABLES elements.
This will reduce the number of event produces each time the control process updates the vector/table.
Beware that the filter "on change" does not work for VECTORS and TABLE, but events are always generated "at any write" .

2. Use the filter "on change" ( != ) to update widgets displaying SCALARS elements.
This filter generated an event only if the new value is not equal to the old one.
While the filter "at any write" ( w ) will generate an event each time a new value is written.
Practical experience showed that the CPU consumption was reduced to 1/3


3. Use "polling" to update widgets displaying VECTORS / TABLES elements.
This will reduce the number of event produces each time the control process updates the vector/table.

4. Use "events" to update data changing very seldom

5. Use the " Output Array " widget to display data from VECTORS or TABLE Columns
This widget is designed to read a portion of a VECTOR or of a TABLE columns and displays each element in a different output widget.

4.3 EXAMPLES

4.3.1 Show the same DB value with several widgets

Sometimes there is the need to use the same DB value with different widgets. This is not possible for all combinations of widgets, in particular when it is used for input and output widgets at the same time.

Two output widgets
They share the same variable name, but only one is connected to the database -.i.e.- for only one of the two the DB flag is set to "ON".

An Input and an output widget
This is a typical case is to display the current DB value and ask the user for a new one.
This is possible only using scale and listBox widgets, in that case both input and output widgets share the same variable name and both the variable's DB flag is set to "ON".

4.3.2 Change Widget Configuration

A typical application is to change the configuration parameters of a widget : the following is a simple example of a panel containing three widgets , two buttons and a bitmap. The two buttons re-configure the bitmap widget to switch between NTT.xbm to ESO.xbm. bitmaps.

Create the panel configuring the widgets according to the following table : just leave all other parameters to their default values.

Widget Type
Name
Label
TCL Command
Button 1
Default
Show ESOBitmap
showBitmap ESO
Button 2
Default
Show NTT Bitmap
showBitmap NTT
Bitmap (load the NTT.xbm bitmap)
myBitmap
NTT

The two buttons invoke the procedure `showBitmap' which takes as parameter the name of the bitmap : click on "Edit procedure ..." to edit the following procedure definition :

proc showBitmap { name } {
global par
panGetCvNames $par(ROOT_PANEL) cv bcv
set myFile [findVLTFile bitmaps/$name.xbm]

$cv.myBitmap configure -label $name
$cv.myBitmap configure -bitmap $myFile
}

When ready click on "Register proc" to let the TCL interpreter know the new procedure definition -i.e.- after this the procedure showBitmap can be executed by the buttons.

The procedure re-configures the widget "$cv.myBitmap" to display the selected bitmap.

4.3.3 Use panel procedures in normal tcl/tk scripts.

The programmatic interface provided by the panel is designed to work within panel-applications. However it is possible to use the procedures provided by the panel in `normal' tcl/tk scripts by calling panConfigure at the beginning of the script.

4.3.4 Interaction between classContainer and classFrame

In this example we want to create two widgets (classContainer and classFrame) which have the following behavior :

- The classContainer shows two classes (class1_uifClass and class2_uifClass) according to the selected "Mode 1" or "Mode 2" .
- In addition the class Frame should also switch to class3_uifClass when "Mode 1" is selected or class4_uifClass when "Mode 2" is selected.

User Action
Parameter
Value/Action
Create classContainer


Configure a classContainer

This configures the classContainer to switch between class1 and class2.




Widget Name

nameInstance
show
titleList
wdgClassList
wdgLibrary
Select "classContainer" from the "Common" widget set.

modeSelector

mode
1
"Mode 1" "Mode 2"
class1_uifClass class2_uifClass
libdocTest
Create classFrame


Configure a classFrame

This configures the classFrame to switch between class3 and class4.



nameInstance
show
wdgClassList
wdgLibrary
Select "classFrame" from the "Common" widget set.

frameSelector
1
class3_uifClass class4_uifClass
libdocTest

To make the classFrame switch according to the radioButton selection we have to configure the "-variable" option of classFrame to the name of the variable associated to the classContainer radioButtons.

The best way to do this, is to configure the classFrame in the <xxx>Init procedure of the panel, since this will take into account possible modifications to the classContainer widget name as well as those concerning the internal structure of the classContainer widget.

The statements to be added to the xxxInit procedure are :

proc xxxInit {} {

# Declare global variables

global cv

# Get the current name of the radioButton variable

set rbVar [$cv.modeSelector getRbVariable]

# Set the "-variable" option to the correct name

$cv.frameSelector configure -variable $rbVar

}

User Action
Parameter
Value/Action
Modify the <xxx>Init procedure

Edit <xxx>init procedure
Click On "Register Procedure"
Save the new panel definition

Click On "Save As ..."
Execute the panel

Run Make and run the panel

4.3.5 Interaction between Menus and classFrame

In this example we want to select the class to be displayed in a classFrame through radioButtons elements in the menu bar, as follows :

- The menu bar contains a menuButton "Select Mode" containing two radioButton entries.
- The classFrame shows two classes (class1_uifClass and class2_uifClass) according to the selected "Mode 1" or "Mode 2" .

User Action
Parameter
Value/Action
Create classFrame


Configure a classFrame

The variable is not automatically mapped into a gvar array element.
The user must explicitly configure it like that.

This configures the classContainer to switch between class1 and class2.



nameInstance
show
variable
wdgClassList
wdgLibrary
Select "classFrame" from the "Common" widget set.

frameSelector
1
selMode
class1_uifClass class2_uifClass
libdocTest
Create a menuButton
Click <B3> on the menuBar to get the menu configuration panel.
widget Name
Label
selectMode
Select Mode
Add to the menuButton two radioButtons

At this point the user is able to switch between the two panel.
Label
variable
value

Label
variable
value
"Mode 1"
selMode
0

"Mode 2"
selMode
1
Set the default value

At user should specify the default value for the menu variable : this has to be done in the GlobalData procedure of the panel.t

Add to xxxGlobalData procedure

global selMode
set selMode 0

4.3.6 Programmatic setting of "show" flag for classContainer

Sometimes there is the need to programmatically set the "show" flag of classContainer, for example when the panel starts.

Therefore add to the <xxx>Init procedure of the panel the following statements :

$cv.<widgetName> configure -show <logical-value>
$cv.<widgetName> show

4.3.7 Enabling/Disabling classContainer radioButtons.

The classContainer can have some radioButtons which are identified by an index which is ranging from 0, N-1 , where N is the total number of buttons.
To enable/disable some of the classContainer radioButtons add the following statements :

$cv.<widgetName> disableRadioButtons <list-of-radioButtons-indexes>
$cv.<widgetName> enableRadioButtons <list-of-radioButtons-indexes>

4.3.8 Store data of a uifClass into the database.

Let's suppose you have a class with some input widgets whose values must be stored into the database independently from the rest of the widgets. In this case a button can be configured to call panDbWriteClass which takes all input widgets contained in a uifClass and writes them into the database ( It also work for nested classes ).

Example :

:

set classPath [$cv.myUifClass getClassPath]

panDbWriteClass $classPath

:

4.3.9 Use the telescope identifier to derive an environment name.

The environment variable TCSID is the standard way to define the telescope identifier ranging from 1 to 4 , while number 0 is used in the `control model'. ( The control model is a `replica' at ESO headquarters of the HW and SW installed in Paranal )

Some subsystem environment names are named according to the telescope where they are installed -e.g - lt1m1m3 refers to the m1m3 cell subsystem of telescope 1.

Here follows the recommended code and the example assumes that :

· We need to find the LCU environment name for m1m3 subsystem.
· m1m3Lcu is the name of the variable holding the name of the m1m3 environment name

proc <xxx>GlobalData {} {
global env m1m3Lcu

# Check if the TCSID environment variable is defined
if {![info exists env(TCSID)]} {
puts "Env. variable TCSID not defined - default (1) taken";
set env(TCSID) 1
}

# Set m1m3 LCU environment name
set m1m3Lcu lt$env(TCSID)m1m3
:
}

4.3.10 Set the uifClass CWP according to a database attribute

Sometime there is the need to derive a Current Working Point (CWP) according to the current telescope setting, for example data regarding the adaptive optics are stored in different database branches according to the current telescope focus.

The goal is to have the uifClass switch automatically according to the current focus (this example comes from a TCS GUI application ) , therefore we need to :

· Configure the class at start-up to pick-up the correct values.
· Define an event to switch automatically the focus

Therefore the steps are the following.

1. Define a default value
Add in the <xxx>GlobalData procedure the code to define the db attribute where to read the value for the current focus from.


2. Add in the panel a widget displaying the current focus value.
The widget is configured to get updated by events and the event script will update both the widget's value and will switch the class CWP as well.
Widget Parameter
Value
Widget Name
currentFocus
Variable
:Appl_data:TCS:msw:foc.currentFocus
Database Mode
Event
Filter
!=
Event Script
switchFocus currentFocus %A %T %D %Q



3. Define the event script.
This is an example of user's defined event procedure which will keep the currentFocus widget up to date and at the same time it will switch the uifClass CWP according to the new focus.

proc switchFocus { w attr type data quality } {
global cv gvar

# Call the standard event procedure to update the widget
panEvtDbUpdate $w $attr $type $data $quality

# Update the configuration of the uifClass according to the focus
setFocus
}

4. Define the procedure to make the uifClass switch its database symbolic addresses

proc setFocus {} {
global cv gvar ccdStateAttr targetEnv

# Get the widget variable name
set var [$cv.currentFocus cget -variable]

switch [uplevel #0 set $var] {
2 {set focusId a ; set gvar(ccdCamera) "Nasmyth A"}
3 {set focusId b ; set gvar(ccdCamera) "Nasmyth B"}
4 {set focusId c ; set gvar(ccdCamera) "Cassegrein"}
default {
panDisplayComment "WARNING : Wrong Focus Specification"
return
}
}

panDisplayComment "Telescope focus switched to $gvar(ccdCamera)"
set cwp ":Appl_data:TCS:LCU:ag${focusId}"

# Stop events defined in the class
panEvtDbOperation $cv.tcsCcdStatus DETACH all

# Switch the class CWP to the new value and RESTART all events.
$cv.tcsCcdStatus configure -cwp $cwp
}

4.3.11 Add Simple Graphic Element

The panel editor does not support the editing of any graphical element in the canvas but it does not prevent the user to add its own graphics by using directly the tcl/tk canvas statements.

In a panel all the widgets are placed on top of a canvas widgets which provides some methods to create simple graphics such as line, rectangle, oval, polygon etc ..

These statements can be executed in both <xxx>InitData or <xxx>Init procedures.

Here follows an example (for more details see [4] or the tcl/tk man pages )

proc <xxx>Init {} {
global cv ...

# Set parameters for graphics
set lineColor Blue
set areaColor Green

# Create a line
$cv create line 30 50 100 150 -fill $lineColor -width 2

# Create a rectangle
$cv create rectangle 5 360 1250 570 -width 3 -outline $lineColor

# Create a circle at x1,y1 and of diameter d.
set d 100 ;# Set diameter
set x 200 ; set y 300 ;# Set center coordinates
$cv create oval [expr $x - $d] [expr $y - $d] [expr $x + $d] [expr $y + $d]

# Create a polygon. This is a `closed' shape specified by a set of points : the
# first and the last points will be automatically connected.

$cv create poly 10 20 50 70 120 40 -fill $areaColor
}

4.3.12 Create specific widget layout.

If the user wants to `automatically' create and position widgets in a panel it is better to use a uifFrame (frame in the widget's palette) as `container' for the widgets otherwise the panel cannot be edited any longer.

To better understand this need we take as example what has been implemented for the M1 Engineering Interface where a set of buttons are automatically placed following the pattern of the M1 actuators. At the ned of this procedure the M1 actuators can be moved in any position of the panel.

1. Create a `frame' widget in the panel with the proper size and give it a name -e.g.- ringFrame

2. Create a canvas within the frame. The code is added to the <xxx>Init because it must be executed after the creation of the standard `frame' widget.

proc <xxx>Init {} {
global cv ...

# The canvas is contained in a frame added with the panel

set top [$cv.ringFrame component uifFrame]
set ringCanvas $top.cv

# Create the canvas
canvas $ringCanvas -height 800 -width 800
pack $ringCanvas -padx 5 -pady 5

# Call a procedure which actually `fills' the canvas.
# The procedure can fill the canvas with widgets as well as with graphic elements

m1asTMCPlaceWdg
:
:
}


3. Create a procedure (m1asTMCPlaceWdg) which will automatically create and position the widgets in the canvas.

proc m1asTMCPlaceWdg {} {
global gvar NbOfTMC ringCanvas ...

for {set i 0} {$i < $NbOfTMC} {incr i} {

# Set some indexes to be used to identify the actuator
set r [expr ... ]
set j [expr ... ]

# Set widget's name
set w $ringCanvas.tmc_$i

# Create widget
button $w -text "$i,$j" -width 6 -font 6x13

# Determine x,y position of the widget
set x [expr ... ]
set y [expr ... ]

# Place the widget in the proper position
place $w -x $x -y $y
}

# Add other labels

$ringCanvas create text 40 30 -text "Side B" \
-font "-Adobe-helvetica-bold-r-normal--14*"
$ringCanvas create text 750 30 -text "Side A" \
-font "-Adobe-helvetica-bold-r-normal--14*"

$ringCanvas create text 380 15 -text "M1 Cell Top View" \
-font "-Adobe-courier-bold-r-normal--24*"
}





Quadralay Corporation
http://www.webworks.com
Voice: (512) 719-3399
Fax: (512) 719-3606
sales@webworks.com
TOC PREV NEXT INDEX