Home Geovia Surpac Tip – TCL/SCL macro series (3 of 10)

Surpac Tip – TCL/SCL macro series (3 of 10)

by Alvina Panggestuty
0 comment
Part 3 – Iterating layers, finding layers and introduction to creating GUIDO forms

SCL provides a number of excellent functions to create, add, import and manipulate layers.  There is also great reference material in the Surpac->Help menu.  My aim in this series is to introduce some of these features in more layman’s terms.  Some knowledge of how macro’s work with variables and commands will certainly be helpful but is not essential.

In the following tip;

  • My comments in the code blocks will have a # in front of them. They are also in bold and italicized.
  • If you would like to test this code in Surpac, the entire code block (Including previous parts 1 and 2) has been added to the end of the tip.  Copy it into any text editor and save it with a .tcl extension to use it in Surpac.  The final result will look similar (but not the same due to styles) as the screen capture below.

Write the series number to the message window using the TCL “puts” command.

puts ""
puts "Part 3 - layer iteration and finding the layer you want. Plus, creating a small GUIDO form."

Get a handle to the current active viewport.

# Get a handle to the active viewport and store it in reference variable "vh"
SclGetActiveViewport vh

Now iterate over all the layers that are in the current viewport.  We do this by using the “sclGraphicsLayers” command in conjunction with the SclIterateFirst subcommand and a variable called “Iterator” (The variable “Iterator” can be called anything though)

# Use sclGraphicsLayers command with SclIterateFirst command to setup a layer iterator
sclGraphicsLayers SclIterateFirst Iterator

The while loop below reads in English as -> While there is another layer to iterate over, keep iterating. Each new layer name is stored in a reference variable called “SwaHandle” (Again, SwaHandle can be called anything.)

# Using a TCL while loop, keep iterating over the layers until there are no more to iterate over. 
while {[$Iterator SclIterateNext SwaHandle] == $SCL_TRUE} {

Using the reference variable “SwaHandle” use the SCL command SclGetId to find out the name of the layer.  Use the TCL command “puts” to print the name of the layer to the message window.

  # Write the layer name to the message window by inquiring the SwaHandle reference variable using the SclGetId command.
  puts "Layer name is [$SwaHandle SclGetId]"

Using the same code as above, we can store the layer name into a variable instead of just writing it to the message window.  Use the TCL “set” command to do this.

  # store the layername into a variable called "layerName"
  set layerName [$SwaHandle SclGetId]

As we iterate over the layers, we can reject certain layers from being added into our layer list by using the TCL “if” command with the boolean operator “!=” which is “not equal to”.  So it reads, if the current layer does not equal “main graphics layer” add it to our list, effectively eliminating the “main graphics layer” from being added.

Lists are simple structures in TCL.  They consist of a variable name i.e. “shoppingList” an index number from zero to however many items there are i.e. 0 1 2 3 4 5 etc plus the value you want to store i.e bread, milk, cheese, icecream.  A better way to view it is below.

shoppingList (variable name)

0 bread

1 milk

2 cheese

3 icecream

We’ll cover lists in more detail at a later stage but hopefully this gives you an indication of how they are stored and used.

  # Don't store the main graphics layer into the list of selectable layers as it may be empty.
  if {$layerName != "main graphics layer"} {
    # Append the layerName into a list called layerList.  TCL lists are simple.  Just imagine a shopping list.
    lappend layerList $layerName
  }
}

Click in graphics to continue.

puts "Layers added to a list... Click in Graphics to continue"
SclPause

Now we have all our relevant layers in a list, let’s create a GUIDO form to give the user the ability to select a layer.  This is done using a widget called a ComboBox.  If you are wondering what GUIDO stands for it is “Graphical User Interface Design Objects”

First, we create a variable that will store our form definition.

# Form definition start here.  Details of the form are stored in the "layerSelection" variable.
set layerSelection {

Now create a GUIDO container to store our GUIDO widget (ComboBox) in.  All containers and widgets in GUIDO can have switches that modify the container or widget behaviour.  The switch is just a minus sign “-” with the switch name after it. i.e -label “Select Layer”

#GuidoForm container has two switches, the label and default_buttons switch.   
  GuidoForm form {
    -label "Select Layer"
    -default_buttons

The code above would look like the screen cap below if we compiled and ran it. Note that the label switch becomes the name of the form.

Let’s add a combobox widget into the form.  It has four switches that control its behaviour.  The -default switch will make the design layer appear in the form if it is available.  Note that the GuidoComboBox widget will store information that it receives into the “layer” variable.  We will populate the “layer” variable with all our layers stored in the “layerList” variable.

    GuidoComboBox layer {
      -label "Layer"
      -width 15
      -exclusive true
      -default "design"
    }
  }
};# Form definition ends here.

To display the form we must use two more Scl Commands. 1. SclCreateGuidoForm and 2. SclRun

The SclCreateGuidoForm command essentially compiles the form in the background with default values and objects ready for display. This is also where we set the “layer” variable to hold all our layer names contained in the “layerList” variable.

Note that the form ($layerSelection) is now compiled and stored in the “formH” variable.

# Use the SclCreateGuidoForm command to compile the form into a variable called "formH"
SclCreateGuidoForm formH $layerSelection {
  # This populates the combobox with all the layers in the layerList variable
  set layer.setValues $layerList
}

The SclRun command will then display the form.  It is running the $formH variable which is storing our compiled form.

$formH SclRun {}

Use the TCL “if” command to cancel the form if the user does not press apply.

if {"$_status" != "apply"} {
  puts "Macro cancelled"
  # Normally you would stop the macro if someone pressed cancel but in our example we press on.  Just unhash return for normal function.  "return" is another TCL command that in this instance will stop the macro.
  #return
}

The form will look similar to below.

Print to the message window the chosen layer.

puts "You have selected the \"$layer\" layer"

The code below will set the active layer to the one that was chosen in the form by the user.  It is essentially the same piece of code used in the layer iterator above with some small changes.

We are iterating over all layers one by one.  Each time we iterate, the reference variable called “ourSwa” stores the handle to the layer.

We then use the SclGetId to check what the layer name of $ourSwa is.  If the name matches the one chosen in the GUIDO form, we use SclSetActiveLayer to make it the active layer.

The TCL “break” command exits the while loop once the user selected layer has been found.

sclGraphicsLayers SclIterateFirst Iterator
# ourSwa is the reference variable that we can use when setting the active layer
while {[$Iterator SclIterateNext ourSwa] == $SCL_TRUE} {
  # store the layername into a variable called "layerName"
  set layerName [$ourSwa SclGetId]
  # Check to see if the current layer stored in $layerName is the one selected which is stored in $layer variable.
  if {$layerName == $layer} {
    puts "Active layer is now $layer.  Check the \"layers\" pane to see if this is true."
    # If we find the required layer, use the SclSetActiveLayer command to change the active layer referenced by $SwaHandle
    $vh SclSetActiveLayer $ourSwa
    puts "Click in graphics once layer is confirmed."
    SclPause
    # no need to search once we have found the layer we are after.  Break will exit the while loop.
    break
  }
}

puts "End of part 3"

Entire code block – Parts 1, 2 and 3 – Copy into text editor and run in Surpac with .tcl extension

####### PART 1 #######
# Get a reference to the active viewport (You can have more than one) store it in reference variable "vh"
SclGetActiveViewport vh
# Using the "vh" reference, get the active layer and store the reference in the "initialSwa" variable
$vh SclGetActiveLayer initialSwa
# Extract the layer name into the layerName variable using the SCL command "SclGetId"
set layerName [$initialSwa SclGetId]
# Print the layer name to the message window
puts "Layer name is $layerName"

# Create a layer called design that can be manipulated/inquired with the "designSwa" reference variable
SclCreateSwa designSwa "design"

# Use the SclAdd function to add the layer into the layer panel
sclGraphicsLayers SclAdd $designSwa $vh

# Using the reference to "designSwa", create a reference variable "designStr" and make the string number 8
$designSwa SclCreateString designStr 8


# Using the reference to the "designStr" create a reference variable "designSeg" and count how many items (i.e. segments) already
# exist for string 8. If there are 5 existing segments in string number 8, [$designStr SclCountItems] will return a value of 5.
# 5 will become the new segment number.  Now, why doesn't this clash with the existing segments if there are already 5 of them?
# This is because the segments are stored as an index position which starts at zero. i.e. segment 1 = index 0, segment 2 = index 1
# ... segment 5 = index 4 and so on.
# All of this to say that when we count how many items are in string 8, the number returned will work as the next index position
# which is what the "SclCreateSegment" function requires. So if I write it manually it becomes
# $designStr SclCreateSegment designSeg 5 
$designStr SclCreateSegment designSeg [$designStr SclCountItems]

# Using the reference variable "designSeg" count how many points there are and add this one to the end
$designSeg SclCreatePoint designPnt [$designSeg SclCountItems]
$designPnt SclSetValueByName X 10
$designPnt SclSetValueByName Y 10
$designPnt SclSetValueByName Z 10
$designPnt SclSetValueByName d1 "Testing SCL layer commands"

# Using the reference variable "designSeg" count how many points there are and add this one to the end
$designSeg SclCreatePoint designPnt [$designSeg SclCountItems]
$designPnt SclSetValueByName X 10
$designPnt SclSetValueByName Y 20
$designPnt SclSetValueByName Z 10

# Using the reference variable "designSeg" count how many points there are and add this one to the end
$designSeg SclCreatePoint designPnt [$designSeg SclCountItems]
$designPnt SclSetValueByName X 20
$designPnt SclSetValueByName Y 20
$designPnt SclSetValueByName Z 10

# Using the reference variable "designSeg" count how many points there are and add this one to the end
$designSeg SclCreatePoint designPnt [$designSeg SclCountItems]
$designPnt SclSetValueByName X 20
$designPnt SclSetValueByName Y 10
$designPnt SclSetValueByName Z 10

# To close the shape, make the x,y and z the same as the first point.
$designSeg SclCreatePoint designPnt [$designSeg SclCountItems]
$designPnt SclSetValueByName X 10
$designPnt SclSetValueByName Y 10
$designPnt SclSetValueByName Z 10

# Using the reference to the design layer (designSwa), use the command "SclDraw" to draw everything in the design layer.
$designSwa SclDraw
SclFunction "ZOOM ALL" {}

# Now, the active layer is still the initial layer that was active when the macro was first run.  Let's make it the design layer
$vh SclSetActiveLayer $designSwa

####### PART 2 #######

#Create a layer called square_2 that can be manipulated/inquired with the "square2Swa" reference variable
SclCreateSwa square2Swa "square_2" 
sclGraphicsLayers SclAdd $square2Swa $vh
# Using the reference to "square2Swa", create a reference variable "square2Str" and make the string number 6
$square2Swa SclCreateString square2Str 6
# Create a new segment.  See details from part 1 about how this works.
$square2Str SclCreateSegment square2Seg [$square2Str SclCountItems]

# Procedure is called "newPoint" and takes 4 parameters.  
# 1. The segment reference 2. The X coord, 3. The Y coord and 4. the Z coord
proc newPoint {segHandle x y z} {
  # The rest of the code is exactly the same as what we did in part one when creating a new point
  # The only difference being that we are using variables i.e. $x that are input when the procedure is called.
  $segHandle SclCreatePoint pntHandle [$segHandle SclCountItems]
  $pntHandle SclSetValueByName X $x
  $pntHandle SclSetValueByName Y $y
  $pntHandle SclSetValueByName Z $z
  return $pntHandle
}

newPoint $square2Seg 11 11 10;# First point, note that we are using square2seg as the segment reference
newPoint $square2Seg 11 19 10
newPoint $square2Seg 19 19 10
newPoint $square2Seg 19 11 10
newPoint $square2Seg 11 11 10;# Closing point is same as first point

# Create a layer called square_3 that can be manipulated/inquired with the "square3Swa" reference variable
SclCreateSwa square3Swa "square_3"
sclGraphicsLayers SclAdd $square3Swa $vh
# Using the reference to "square3Swa", create a reference variable "square3Str" and make the string number 9
$square3Swa SclCreateString square3Str 9
# See details from part 1 about how SclCreateSegment works.
$square3Str SclCreateSegment square3Seg [$square3Str SclCountItems]

newPoint $square3Seg 13 13 10;# First point, note that we are using square3seg as the segment reference
newPoint $square3Seg 13 17 10
newPoint $square3Seg 17 17 10
newPoint $square3Seg 17 13 10
newPoint $square3Seg 13 13 10;# Closing point is same as first point

$square2Str SclDraw
$square3Str SclDraw "style=ssi_method=line,HGS_color=line=red,HGS_line_weight=2"

####### PART 3 #######

puts ""
puts "Part 3 - layer iteration and finding the layer you want. Plus, creating a small GUIDO form."

# Get a handle to the active viewport and store it in reference variable "vh"
SclGetActiveViewport vh
# Use sclGraphicsLayers command with SclIterateFirst command to setup a layer iterator
sclGraphicsLayers SclIterateFirst Iterator
# Using a TCL while loop, keep iterating over the layers until there are no more to iterate over. 
while {[$Iterator SclIterateNext SwaHandle] == $SCL_TRUE} {
  # Write the layer name to the message window by inquiring the SwaHandle reference variable using the SclGetId command.
  puts "Layer name is [$SwaHandle SclGetId]"
  # store the layername into a variable called "layerName"
  set layerName [$SwaHandle SclGetId]
  # Don't store the main graphics layer into the list of selectable layers as it may be empty.
  if {$layerName != "main graphics layer"} {
    # Append the layerName into a list called layerList.  TCL lists are simple.  Just imagine a shopping list.
    lappend layerList $layerName
  }
}

puts "Layers added to a list... Click in Graphics to continue"
SclPause

# Form definition start here.  Details of the form are stored in the "layerSelection" variable.
set layerSelection {
#GuidoForm container has two switches, the label and default_buttons switch.   
  GuidoForm form {
    -label "Select Layer"
    -default_buttons
    
    GuidoComboBox layer {
      -label "Layer"
      -width 15
      -exclusive true
      -default "design"
    }
  }
};# Form definition ends here.

# Use the SclCreateGuidoForm command to compile the form into a variable called "formH"
SclCreateGuidoForm formH $layerSelection {
  # This populates the combobox with all the layers in the layerList variable
  set layer.setValues $layerList
}
$formH SclRun {}

if {"$_status" != "apply"} {
  puts "Macro cancelled"
  # Normally you would stop the macro if someone pressed cancel but in our example we press on.  Just unhash return for normal function.  "return" is another TCL command that in this instance will stop the macro.
  #return
}

puts "You have selected the \"$layer\" layer"

sclGraphicsLayers SclIterateFirst Iterator
# ourSwa is the reference variable that we can use when setting the active layer
while {[$Iterator SclIterateNext ourSwa] == $SCL_TRUE} {
  # store the layername into a variable called "layerName"
  set layerName [$ourSwa SclGetId]
  # Check to see if the current layer stored in $layerName is the one selected which is stored in $layer variable.
  if {$layerName == $layer} {
    puts "Active layer is now $layer.  Check the \"layers\" pane to see if this is true."
    # If we find the required layer, use the SclSetActiveLayer command to change the active layer referenced by $SwaHandle
    $vh SclSetActiveLayer $ourSwa
    puts "Click in graphics once layer is confirmed."
    SclPause
    # no need to search once we have found the layer we are after.  Break will exit the while loop.
    break
  }
}

puts "End of part 3"

You may also like

Leave a Comment

@ Copyright Acala 2025 – All Rights Reserved.

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?
-
00:00
00:00
Update Required Flash plugin
-
00:00
00:00