Scripting Overview and Syntax

Overview

Scripting is used in many places in Ignition to add a significant degree of flexibility and customization where pre-canned options fall short. There are two major scripting languages in Ignition, Python and the Expression Language. It is important to understand the differences between the two, and to know where each is used.

Basic Syntax

Hello World

Lets get right to everyone's favorite example: the following script will print out "Hello World " to the output console .

print "Hello World"

The print keyword is a handy tool in Python, allowing you to put text into the output console. This is useful for debugging your scripts. You can print multiple things by separating them with commas.

Variables

Variables are created by simply assigning a value to them. Variables do not need to be declared, because Python has a dynamic type system. That means Python figures out the type of the variable on the fly, when the script is executed.

The following script would print out: 15

x=5
y=3
print x*y

Quotations

Python makes limited distinction between single and double quotes. As long as they are used consistently then there are few times when which type of quotation mark you use matters. Some of the rules are shown here:

print "This is my text" #Using double quotation marks
print 'This is my text' #Using single quotation marks
print "This is my text' #This will not work because python does not allow mixing the single and double quotation marks
print "My name is 'David'" #This will print My name is 'David'
print 'My name is "David"' #This will print My name is "David"

Strings, Numbers, and Booleans

Literal strings can be typed in using either double quotes or single quotes. This can be handy when your string contains one quote or the other. You can also use the backslash character to escape special characters.

Strings that contain characters beyond 7-bit ASCII, such as é or щ need to be marked as unicode strings by placing the letter u in front of the string. There is no harm in marking all strings as unicode strings. The following prints a unicode string:

print u'été'

Numbers can just be typed in normally, like 42 or 3.14159. Python does not have a boolean type. 0 is false and 1 is true. For convenience, Python also has constants named False and True which can be used in place of 0 and 1. This is an oversimplification, but should suffice for now. The following prints out "True".

x="isn't this grand"
y='isn\'t this grand'
print x==y

None

There is a special value in Python called None (with a capital N). This is simply a special value that means: no value. This value is equivalent to Java's null value.

List

In Python, lists (arrays) are a built-in type that contains multiple other values. Lists can contain any type of items, and the items in a list do not all need to be the same type. You can create a list by enclosing multiple items in square brackets ( [] ), separated with commas. You can pull items out of a list with the square-bracket list index notation. Note that lists are zero-indexed, meaning that the first item in the list is at position 0. This code will print out " a list ".

a = ['this', 'is', 'a list', 8, 93.928]
print a[2]

Basic Operators

Python has all of the normal arithmetic operators you'd expect, addition(+), subtraction(-), division(/), multiplication(*), modulus(%), etc.

The comparison operators are just like in C: equals(==), not equals(!=) greater than (>), greater than or equal(>=), etc.

The logical operators are just typed in plain text: and, or, not.

Comments

Comments start with a hash sign. Add comments to your code so that when you go back to it after a long time, you know what the code is trying to do.

# Prints out 'Hello World' 5 times.
for x in range(5):
print 'Hello world'

Whitespace

Perhaps its most unique feature, logical blocks are defined by indentation in Python. A colon ( : ) starts a new block, and the next line must be indented (typically using a tab of 4 spaces). The block ends when the indentation level returns to the previous level. For example, the following will print out " 5 4 3 2 1 Blast-off ". The final print is not part of the loop, because it isn't indented.

countdown=5
while countdown > 0:
print countdown,
countdown = countdown - 1
print "Blast-off!"

Control Flow Statements

Control flow statements, that is the ifs and loops, make the language do things differently based on the various conditions. Python has all of the basic control flow statements that you'd expect from a programming language.

if Statement

The if statement is familiar to anyone with a passing knowledge of programming. The idea of an if is that you want your script to execute a block of statements only if a certain condition is true. For example, this script won't do anything.

x = 15
if x < 10:
print "this will never show"

You can use the if...else form of an if statement to do one thing if a condition is true, and something else if the condition is false. This script will print out " this will show!"

 x = 15
if x < 10:
print "this will never show"
else:
print "this will show!"

Lastly, you can use the if...elif form. This form combines multiple condition checks. elif stands for else if. This form can optionally have a catch-all else clause at the end. For example, this script will print out "three":

x = 3
if x == 1:
print "one"
elif x == 2:
print "two"
elif x == 3:
print "three"
else:
print "not 1-3"

while Loops

A while loop will repeat a block of statements while a condition is true. This code will print out the contents of the items in the list. This code uses a function called len , which is a built-in function that returns the length of a list or string.

listOfFruit = ['Apples', 'Oranges', 'Bananas']
x = 0
while x < len(listOfFruit):
print listOfFruit[x]
x = x + 1

for Loops

Python's for loop may be a bit different than what you're used to if you've programmed any C. The for loop is specialized to iterate over the elements of any sequence, like a list. So, we could re-write the example above using a for loop eliminating the counter x:

listOfFruit = ['Apples', 'Oranges', 'Bananas']
for item in listOfFruit:
print item

Much more graceful! You'll often see the for loop used instead of the while loop, even when you simply want to iterate a given number of times. To do this with the for loop, you can use the built-in function range. The range function returns a variable-size list of integers starting at zero. Calling range(4) will return the list [0, 1, 2, 3]. So, to have a for loop repeat 4 times, you can simply do:

for x in range(4):
print "this will print 4 times"

break and continue in Loops

You can stop a loop from repeating in its tracks by using the break statement. This code will print out " Loop " exactly two times, and then print " Finished ".

for x in range(10):
if x >= 2:
break
print "Loop"
print "Finished"

You can use the continue statement to make a loop stop executing its current iteration and skip to the next one. The following code will print out the numbers 0-9, skipping 4.

for x in range(10):
if x == 4:
continue
print x

String Formatting

Although string formatting is a minor feature of Python, but it is incredibly useful in Ignition. String formatting is used to manipulate strings, specifically to insert the values of variables inside a string without a bunch of concatenation.

The % operator is used in Python not just for modulus, but also for string formatting. Suppose we wanted to print a weather report. We could use concatenation, like this:

temp = 65.8
city = "Sacramento"
windSpeed = 25
windDir = "east"
print city
print "Weather: " + str(temp) + "°F, wind "+ str(windSpeed) + "mph from the "+ windDir

Yuck! This kind of concatenation is really a pain to write and to read. With string formatting, we could have written it like this:

temp = 65.8
city = "Sacramento"
windSpeed = 25
windDir = "east"
 
print "%s weather: %f°F, wind %dmph from the %s" % (city, temp, windSpeed, windDir)

Ah, that's much easier on the eyes. What is happening here is that the % operator is applying the variables on its right side to the format string on its left side. It looks for placeholders (called format specifiers) inside the format string, and replaces them with corresponding values from the variables on the right side. There are various format specifiers that can be used for different types of variable types. If you actually want a % sign inside the final string, use the special format specifier: "%%"

Format Specifier
Meaning

%%

Inserts a % sign into the final string.

%c

A single character. Value must be a string of length 1 or an integer.

%d or %i

Signed integer

%f

Floating point, decimal format

%s

A String, converts the value to a string using str().

%u

Unsigned decimal

%x or %X

Unsigned hexadecimal

You can also put some extra information in the format specifiers between the % and the format specifier character. The most useful thing to do is to specify the number of decimal places to use to print floating point numbers. For example, "%.3f" would always put three digits after the decimal point.

Functions

Functions are code that can be called repeatedly from other places. Functions can have parameters passed into them, and may return a resulting value. Some functions, like len, are built-in. Some functions, like system.gui.messageBox(), are part of the scripting libraries provided by Ignition. Some functions, like math.sqrt(), are provided by the Python standard library.

Functions are invoked by using their name followed by an argument list surrounded in parentheses. If there are no arguments, you still need an open and close parenthesis.

Defining Functions

Functions are defined using the def keyword. A function needs a name and a list of the arguments that it can be passed. For example, this code defines a function that tests whether or not a number is odd. It returns a true value (1) if the number is odd. It is then used in a loop to print out the odd numbers between 0 and 9.

def isOdd(num):
return num % 2 == 1 # uses the modulus (or remainder) operator
for x in range(10):
if isOdd(x):
print x

Functions Arguments

When a function accepts arguments, the names of those arguments become variables in the function's namespace. Whatever value was passed to the function when it was invoked becomes the value of those variables. In the example above, the value of x inside the for loop gets passed to the isOdd function, and becomes the value of the num argument.

Arguments can have default values, which makes them optional. If an argument is omitted, then its default value will be used. The following code defines a function calledcap, which will take a number, and make sure it is within an upper and lower limit. The limits default to 0 and 100.

def cap(x, min=0, max=100):
if x < min:
return min
elif x > max:
return max
else:
return x
# This will print out "0"
print cap(-1)
# This will print out "100"
print cap(150)
# this will print out "150", because it uses a max of 200
print cap(150, 0, 200)

Keyword Arguments

In Ignition, some complicated script functions are designed to take keyword arguments instead of normal parameters. In the description for those functions, you may see the following info box in this User Manual:

This function accepts keyword arguments .

Arguments can also be specified by keyword instead of by position. In the example below, the only way someone would know that the 200 in the last call to cap specified the max is by its position. This can lead to hard-to-read function invocations for functions with lots of optional arguments. You can use keyword-style invocation to improve readability. The following code is equivalent to the last line above, using 200 for the max and the default for the min.

print cap(150, max=200)

Because we used a keyword to specify that 200 was the max, we were able to omit the min argument altogether, using its default.

Functions are Objects

Perhaps one of the most foreign concepts for new Python users is that in Python, functions are first-class objects. This means that functions can be passed around to other functions (this concept is similar to the idea of function pointers in C or C++).

Lets go back to the isOdd example above. Suppose we wanted a more general way to filter a list. Maybe sometimes we want the odd entries, while other times we want even ones, or entries less than 3, etc. We can define a function called extract that takes a list and another function, and returns only entries that "pass" through the other function.

def isOdd(num):
return num % 2 == 1
def isEven(num):
return num % 2 == 0
def isLessThan(num, max=3):
return num < max
def extract(filterFunction, list):
newList = []
for entry in list:
if filterFunction(entry):
newList.append(entry)
return newList
# prints out [0, 2, 4, 6, 8]
# notice that isEven as not _invoked_, but passed to the filter function
print extract(isEven, range(10))

Now, it just so happens that Python has a built-in function that does exactly what our extract function does - its called filter.

We would also be remiss at this point if we didn't mention another language feature called list comprehensions. This is a great little bit of syntax that helps make new lists out of other lists. Instead of using our filter function, we could have simply done this:

def isEven(num):
return num % 2 == 0
print [x for x in range(10) if isEven(x)]

In Ignition, you'll most commonly see functions used as objects when using the system.util.invokeLater function. This function takes a function and executes it after all pending event handling has finished processing.

Scope and Import

The concept of scope is very important in all programming, and Python is no exception. Scope defines what names are directly accessible without any qualifiers. Another way to put this is that the scope determines what variables are defined.

Definition by Assignment

In Python, a variable is defined at the time that it is assigned. What scope it belongs to is also defined by where the assignment occurs.

doSomeWork() # on this line, there is no variable 'x' in scope
 
x = 5 # now 'x' is defined in our scope, because we've assigned a value to it
 
print x # This will work just fine, because x is in scope.

Function Scope

When you define a function, that function gets its own scope. Variables that are assigned within that function body will not be available outside of the function.

def myFunction():
x = 15 # x is local to myFunction()
print x # This will work, because it is part of the function
y = x + 10 # This will fail, because x is not available in the outer scope

Import Keyword

You can import the namespaces defined in other scopes into your scope with the import keyword. Most commonly, you'll import from global library sources, like system (the Ignition standard libraries), project (your project's script library), java (importing from the Java standard library), and the various python standard libraries. When you're writing component event handlers, system, shared, and project are imported for you automatically.

The import keyword can be used in a variety of forms:

  • import X

  • from X import Y

For example, suppose you wanted to use the java.util.Calendar class for some date manipulations. You could import this in a number of different ways. These examples are equivalent, printing out a date 8 hours before the current date.

import java
cal = java.util.Calendar.getInstance()
cal.add(java.util.Calendar.HOUR, -8)
print cal.getTime()
from java.util import Calendar
cal = Calendar.getInstance()
cal.add(Calendar.HOUR, -8)
print cal.getTime()

Sequences and Dictionaries

Python offers a variety of sequence types. We've already seen one - the List. There are other kinds of sequences, most notably tuples and strings. There is also the dictionary type, which contains a list of key-value pairs.

List

Lists are a very handy kind of sequence. They can hold any number of items and can be resized on the fly. After creating a list using the square bracket notation, there are a number of functions that you can call on the list. Some common list functions are listed here.

append(x)Takes a single argument, which will be appended to the end of the list.

insert(i,x)Inserts an item x at index i

remove(x)Will remove the given item from the list.

index(x)Returns the index of the value x. Throws an error if the list doesn't contain the item. Use the in operator to check if an item is contained in a sequence.

sort()Sorts the items in the list.

myList = ['a', 'b', 'c', 'd']
print myList # --> [a, b, c, d]
myList.append("Q")
print myList # --> [a, b, c, d, Q]
myList.insert(1, "Z")
print myList # --> [a, Z, b, c, d, Q]
myList.remove("c")
print myList # --> [a, Z, b, d, Q]
print myList[2] # --> b
print myLIst.index("b") # --> 2
if 'Z' in myList:
print 'Z is in the list'
if 'c' not in myList:
print 'c was removed from the list'

Tuples

A tuple is similar to a list, but you use parenthesis instead of square brackets to define one. The major difference between a tuple and a list is that tuple's are immutable. That is, once created, they cannot be altered. Tuples are very useful for passing multiple things to and from functions. For example, you could pass a point to a function using a tuple like so:

def printPoint(point):
print "x = ", point[0]
print "y = ", point[1]
printPoint((28,89))

This can also be handy for returning multiple values from a function. For example, if you had a mouse event, you could write a function that found the component's center point, and return that point as a tuple. You could then use unpacking assignment to extract the values into separate values.

def findCenter(event):
w = event.source.width
h = event.source.height
return (w/2, h/2)
# point will be a tuple
point = findCenter(event)
# x and y will be numbers, using unpacking assignment
x,y = findCenter(event)

Dictionaries

A dictionary is a very useful type that holds a set of key-value pairs. You may have used these in other languages and know them as hashmaps, maps, associative memories or associative arrays. Dictionaries are not ordered sequences - you reference any item via its key value. The keys can be numbers, strings, or tuples of these types. Any given key may only appear once in a dictionary. Trying to set another value for that key will overwrite any previous value for that key.

Dictionaries are created using braces ({}). Key-value pairs are separated by commas, and the keys are separated from the values with a colon. You can use the .keys() function to have a set of the keys. For example:

myDict = {'Bob': 89.9, 'Joe': 188.72, 'Sally': 21.44}
print myDict['Bob'] # --> 89.9
myDict['Amir']=45.89 # Adds a key for 'Amir'
names = myDict.keys()
names.sort()
print names # --> ['Amir', 'Bob', 'Joe', 'Sally']

You can loop through dictionaries using a for loop. You can use the keys() to loop through the dictionary, and then use the key values to look up the value. For example:

for name in myDict.keys():
print name, myDict[name]

Exception Handling

Exception handling is a language feature of many high-level languages that allows you to "catch" a runtime error and deal with it as you see fit. On the flip side, it allows you to "raise" or "throw" an error in your code, which will break out of whatever code is currently executing and jump to the nearest enclosing catch block that knows how to handle your error.

For example, dividing by zero raises a ZeroDivisionError. You can catch this error using a try...except block, like this:

try:
result = 8 / 0
print "this will never get called"
except ZeroDivisionError:
print "oops - can't divide by zero"

You don't have to specify a particular type of error to catch - you can use the except keyword by itself to catch any kind of exception. You can also assign the details of the exception to a tuple of variables, which you can use in your error reporting. You can also have multiple except blocks for one try block, each that handle different kinds of exceptions. This example shows these variations:

def someDangerousFunction():
raise IOError(42,"oh no")
try:
someDangerousFunction()
except IOError, (errno, description):
print "An I/O error occurred: "+description
except:
print "An unexpected error occurred"

Accessing Java

When programming Python in Ignition, your code executes in the Jython implementation of Python. (See About Scripting - Python or Jython?). While this doesn't have any great effect on the Python language itself, one of the great side benefits is that your Python code can seamlessly interact withJava code, as if it were Python code. This means that your Python code has access to the entire Java standard library, which is saying a lot.

To use Java classes, you simple import them as if they were Python modules. For example, the following code will print out all of the files in the user's home directory. This code uses the Java classes java.lang.System and java.io.File to look up the user's home directory and to list the files. Notice that we can even use the Python-style for loop to iterate over a Java sequence.

from java.lang import System
from java.io import File
 
homePath = System.getProperty("user.home")
homeDir = File(homePath)
for filename in homeDir.list():
print filename

You can find the reference documentation for the Java standard class libraray (also known as, the "JavaDocs") at: http://docs.oracle.com/javase/6/docs/api/

Subclassing Java

You can even create Python classes that implement Java interfaces. If this is greek to you - don't worry, it isn't crucial. You'd need some understanding of Java and object-oriented programming concepts, which are outside the scope of this manual.

To create a Python class that implements a Java interface, you simply use the interface as a superclass for your Python class. For example, we could augment the example above to use the overload java.io.File.list(FilenameFilter). To do this, we'll need to create aFilenameFilter, which is an interface in Java that defines a single function:

boolean accept(File dir, String name)

To implement this interface, we create a Python class that has java.io.FilenameFilter as its superclass, and implements that Java-style function in a Python-esque way.

from java.lang import System
from java.io import *
 
class ExtensionFilter(FilenameFilter):
def __init__(self, extension=".txt"):
self.extension=extension.lower()
 
def accept(self, directory, name):
# make sure that the filename ends in the right extension
return name.lower().endswith(self.extension)
 
homePath = System.getProperty("user.home")
homeDir = File(homePath)
 
# prints out all .txt files
for filename in homeDir.list(ExtensionFilter()):
print filename
 
# prints out all .pdf files
for filename in homeDir.list(ExtensionFilter(".pdf")):
print filename