Example 1

This example demonstrates the use of the TclScript primitive for creating customized GUI elements using Tcl files.

The system can be found in MLD Examples→Tutorials→Tcl System. We will create a system containing the TclScript and the TkShowValues primitives found in MLD Libraries→SDF Domain→Tcl/Tk

This is done as follows:

  1. Click the New Model toolbar button and create a new Library called TclScript.
  2. Click the New Model toolbar button and select System as Type of Model.
  3. Give the system a name such as MyTclSystem. Select the previously created library from the Select Library dialog.
  4. Open the library MLD Libraries→SDF Domain→Tcl/Tk and drag the primitive TclScript into the Model Editor window containing MyTclSystem. From the Select Special Primitive dialog choose TclScript.input=0.output=1 and click OK.
  5. Drag the TkShowValues primitive into the Model Editor window and choose TkShowValues.input=1 from the Select Special Primitive dialog.
  6. Connect the ports of the two model instances.
  7. Create a (.tcl) file containing the following script and save it in your TclScript library as TclScript.tcl.
set s $ptkControlPanel.middle.button_$starID
if {! [winfo exists $s]} {
    button $s -text "PUSH ME"
    pack append $ptkControlPanel.middle $s {top}
    bind $s <ButtonPress-1> "setOutputs_$starID 1.0"
    bind $s <ButtonRelease-1> "setOutputs_$starID 0.0"
    setOutputs_$starID 0.0
unset s

Since in Ptolemy vocabulary a primitive is called a star, the variable starId always means the identifier of the primitive.

  1. Select the instance TclScript.input=0.output=1 to activate the Instance Properties panel in the Property Editor window.
  2. Set the value of parameter tcl_file to $MLD_USER/TclScript/TclScript.tcl.
  3. Set the value of parameter put_in_control_panel to YES.
  4. Click on the model background to activate the System Properties panel.
  5. Set the RunLength to -1. This makes the simulation run endlessly or until you decide to terminate it.

You can now run the simulation. Click the Switch to Simulation Mode toolbar button and click Go. This script creates a push button in the tclRunControl panel. The primitive outputs the value 1.0 when the button labeled PUSH ME is clicked and held and 0.0 when it is released.

The script in depth

set s $ptkControlPanel.middle.button_$starID

This defines a Tcl variable s whose value is the name of the window to be used for the button. The first part of the name, $ptkControlPanel, is a global variable giving the name of the control panel window itself. This global variable is set by MLDesigner if the put_in_control_panel parameter is set to YES and can be used under this condition by any Tcl script. The second part, .middle, specifies that the button should appear in the sub-window named middle of the control panel. The control panel, by default, has empty sub-windows named high, middle, and low.

The last part, .button_$starID, gives a unique name to the button itself. The Tcl variable starID has been set by the TclScript primitive to a name that is guaranteed to be unique for each instance of the primitive. Using a unique name for the button permits multiple instances of the primitive in a model to create separate buttons in the control window without conflict.

if {! [winfo exists $s]} {

This condition checks to see whether or not the button already exists. If, for example, the system is being run a second time, then there is no need to create the button a second time. In fact, an attempt to do so will generate an error message. If the button does not already exist, it is created by the following lines:

button $s -text "PUSH ME"
pack append $ptkControlPanel.middle $s {top}

The first of these defines the button, and the second packs it into the control panel, refer to the Tk documentation to find out more about button and pack.

The following Tcl statement binds a particular command to a mouse action, thus defining the response when the button is pushed.

bind $s <ButtonPress-1> "setOutputs_$starID 1.0"

When button number 1 of the mouse is pressed, the Tcl interpreter invokes a procedure named setOutputs_$starID with a single argument, 1.0 (passed as a string). This procedure has been defined by the TclScript primitive. It sets the value(s) of the outputs of the primitive. In this case, there is only one output, so there is only one argument. The next statement defines the action when the button is released:

bind $s <ButtonRelease-1> "setOutputs_$starID 0.0"

The next statement initializes the output of the primitive to value 0.0.

setOutputs_$starID 0.0

The last command unsets the variable s, since it is no longer needed.

unset s

As illustrated in this example, a number of procedures and global variables will have been defined to be used by the Tcl script by the time it is sourced. These enable the script to modify the control panel, define unique window names, and set initial output values for the primitive.

Much of the complexity in the above example is due to the need to use unique names for each primitive instance that sources this script. In the above example, the Tcl procedure for setting the output values has a name unique to this primitive. Moreover, the name of the button in the control panel has to be unique to handle the case when more than one TclScript primitive sources the same Tcl script. These unique names are constructed using a unique string defined by the primitive prior to sourcing the script. That string is made available to the Tcl script in the form of a global Tcl variable starID. The procedure used by the Tcl script to set output values is called setOutputs_$starID. This procedure takes as many arguments as there are output ports. The argument list should contain a floating-point value for each output of the primitive.

In the above example, Tcl code is executed when the Tcl script is sourced. This occurs during the setup phase of the execution of the primitive. After the setup phase, no Tcl code will be executed unless the user pushes the "PUSH ME" button. The command

bind $s <ButtonPress-1> "setOutputs_$starID 1.0"

defines a Tcl command to be executed asynchronously. Notice that the command is enclosed in quotation marks, not braces. Tcl aficionados will recognize that this is necessary to ensure that the starID variable is evaluated when the command binding occurs (when the script is sourced), rather than when the command is executed. There is no guarantee that the variable will be set when the command is executed.

In the above example, no Tcl code is executed when the primitive fires. Example 2 shows how to define Tcl code to be executed each time the primitive fires, and also how to read the inputs of the primitive from Tcl.