Interactive Python Scripting Example

For my first blog entry I wanted to write an article explaining what a dynamic language is. Since I prefer to think concretely about things, I'm going to start with an example of using a dynamic language (Python) to play with an interesting API. For now, my working definition of a dynamic language is any language which could be easily used for the following example.

I’m going to use Windows.Forms as a standard API for this demo. (Note: This probably won't work on Mono-1.0, but it shouldn't be hard to port this example to GTK#. Here's an example of using IronPython with GTK# for those who are interested.) If you want to follow along, you can download IronPython-0.6. Remember that IronPython is at a pre-alpha 0.6 release, so don’t be surprised if it has bugs.

Formatting note: I'm sorry if these examples are a little hard to read. I'm having trouble talking my blog editor into showing PRE blocks correctly. The extra lines are an artifact of the blog editor and are not intentional.

The first step is to bring up the interactive interpreter. This should be simple. See README.html in the IronPython zip file for a few more hints on different platforms.

 C:\IronPython\bin> IronPythonConsole 
 >>>

Whenever I bring up a new interactive interpreter I feel compelled to get it to do some tricky math to let me know that it's really working.

 >>> 2+2 
 4

Next we’ll create a new Form and show the window.

 >>> from System.Windows.Forms import * 
 >>> from System.Drawing import * 
 >>> f = Form(Text="Experiment #1") 
 >>> f.ShowDialog()

You should see a nice little window popup now. However, there’s a big problem. f.ShowDialog has taken over the main thread and it’s impossible to type anything more at the interpreter prompt. Let’s fix that by first closing the window (click on the ‘X’). You should then see:

 Cancel 
 >>> 

Now let’s start the ShowDialog method in its own thread so that it doesn’t block the interpreter

 >>> from System.Threading import * 
 >>> Thread(f.ShowDialog).Start() 
 >>> 

Now we have our window showing and the interpreter is still “live” for us to keep typing at. We can change different properties of the form like this:

 >>> f.MinimizeBox = False 
 >>> f.Size = Size(200,200) 
 >>> f.Text = "New Title"

It’s fun playing with the form, but we’d like to add a button to give it some more functionality.

 >>> b = Button(Text="Push Me") 
 >>> f.Controls.Add(b) 
 System.ArgumentException: Controls created on one thread cannot be parented to a contro <snip>

Oops! That didn’t work as well as we might have liked. This is our second experience of the interactions between the threading model in Windows.Forms and the interactive interpreter. There are a number of creative solutions to this problem (see …) To keep things simple for this blog, we’re going to just close the window again, add the control, and then restart the main event thread. Don’t forget to close the window before proceeding with the next two lines.

 >>> f.Controls.Add(b) 
 >>> Thread(f.ShowDialog).Start()

Now we should see the form appear again with a button on it. We can play with the button to make it bigger and brighter.

 >>> b.Size = f.ClientSize 
 >>> b.BackColor = Color.Yellow

Let’s make the button do something interesting. In preparation for this we’re going to create a list of the named colors in the Color class. This is the one piece of code that includes the most Python specific code. If you’re unfamiliar with Python you should just treat most of this code as magic. Dive Into Python is a good first resource for an experienced programmer who wants to become familiar with Python. It also has a very short section on indentation of blocks in Python. To make this example work, the one thing you should be aware of is that for each line which begins with "..." you will need to type a TAB to create the correct indentation and get your program to run correctly.

 >>> colors = [] 
 >>> for name in dir(Color): 
 ...    c = getattr(Color, name) # Use TAB to indent this line and below 
 ...    if type(c) == Color: colors.append(c) 
 ... 
 >>> colors 
 [Color [AliceBlue], Color [AntiqueWhite], Color [Aqua], Color [Aquamarine], Colo 
 <snip> 
 ke], Color [Yellow], Color [YellowGreen]]

Now that we have this list of colors, we can set the background color of the button to these colors in sequence. This requires creating a function to do the work and then setting that function to be run on the Click event. (Note: def is used in Python to define a new function, and just like above you should use TAB to indent each line in the body of the function.)

 >>> index = 0 
 >>> def push(b, e): 
 ...    global index # Use TAB to indent this line and below 
 ...    b.BackColor = colors[index] 
 ...    b.Text = colors[index].Name 
 ...    index += 1 
 ... 
 >>> b.Click += push 
 >>> 

Click on the button to cycle through all of the named colors. Finally, we need to exit the interpreter. This is done with ‘Ctrl-Z’.

 >>> ^Z 
 C:\IronPython\bin>

Now that we’re done playing with the interactive shell, we can put the results of our experiment into a file for running stand-alone. Now that we’re done experimenting, we can simplify the code a little bit by moving some of the property settings into the Form and Button constructors. We can also leave out all of the separate Thread nonsense and just call f.ShowDialog at the end of setting everything up.

 ------ colors.py ------ 
 from System.Windows.Forms import * 
 from System.Drawing import * 
 f = Form(Text="Playing with colors", MinimizeBox=False, Size=Size(200,200)) 
 b = Button(Text="Push Me", Size=f.ClientSize) 
 f.Controls.Add(b) 
 colors = [] 
 for name in dir(Color): 
     c = getattr(Color, name) 
     if type(c) == Color: colors.append(c) 
  
 index = 0 
 def push(b, e): 
     global index 
     b.BackColor = colors[index] 
     b.Text = colors[index].Name 
     index += 1 
  
 b.Click += push 
 f.ShowDialog() 
 ------

Now you can run this as a stand-alone program:

 C:\IronPython\bin> IronPythonConsole colors.py

I hope that this has helpful to give newcomers a feel for what IronPython feels like to code in and to give even experienced Python programmers a few more concrete examples of how to work with the CLR. As one last recommendation to anyone new to IronPython, I’d like to pass on this advice overheard on the ironpython users mailing list. IronPython will sometimes look more like VB than C# in the way that it uses CLR libraries and you should look at both C# and VB examples if you’re confused about how to call something in IronPython.