I'm particularly grateful to IronPython. Because of it I'm now able to earn a living programming with Python, for Resolver Systems.

I'm also writing a book, IronPython in Action for Manning publications. Expect to see it out early autumn time.

Resolver Systems Ltd (London)

Resolver Systems is a small company (6 developers) based in London. Resolver Systems was created to develop a new business application. This is about to go out for a private beta test with our first customers and we should be going public with the details soon.

Resolver the company started in late 2005. Andrzej joined Resolver in March 2006, Michael joined in April 2006.

Resolver the Application

Resolver is a desktop application for businesses. .NET was initially chosen as the development platform, but a scripting language was needed as an integral part of the application. After discovering and trying IronPython (whilst it was still Beta) the two developers, as they then were, decided to write the whole application with IronPython.

The Resolver codebase currently stands at over twenty thousand lines of production code and around seventy thousand lines of test code. About 1% of the production code is in C# and the rest IronPython.

Resolver integrates Python in a very interesting way...

Talk Aims

To introduce IronPython

To demonstrate the richness of Windows Forms

To show you how to get started creating a Windows Forms application

To provide examples of using a few specific controls

Along with this we will discuss (briefly) why you might (or might not) want to use IronPython, we'll also encounter and explain some .NET terminology along the way. The talk will be illustrated with code snippets from the The Multi-Tabbed Image Viewer, which of course will be shown in action.

Python

Python is used for a wide variety of purposes: games (Eve Online, Civilization IV), science (particularly bioinformatics and genomics), industry (Seagate), GIS, film industry, desktop applications, system administration, web development. It is used by companies like Google, NASA, Yahoo and Industrial Light and Magic.

Written by Guido van Rossum, first version around 1992 (older than Java)

Named after Monty Python

A general purpose language

Emphasizes a philosophy of flexibility, readability and clarity.

What is IronPython?

IronPython was started by Jim Hugunin, who now heads a Microsoft team running the development.

IronPython is a very faithful implementation of Python 2.4. All the core language features are there.

Python + .NET = IronPython

Started by Jim Hugunin, now developed by Microsoft

IronPython is Python 2.4 (Some 2.5 features already there)

Written in C# for the .NET framework

Also runs on Mono

Integrated Visual Studio support

Since version 1.2.3, the IronPython Community Edition (Fepy, maintained by the incredible Seo Sanghyeon) is now included in Mono.

Mono has partial support for Windows Forms. I think most/all of Windows Forms 1 is there and some of 2. The 1.2.3 release included a lot of fixes and additions to the Windows Forms 2.0 support.

The Visual Studio support includes the designer and debugger. It requires the full version of Visual Studio, plus the SDK.

IronPython and .NET

IronPython is a Python compiler

The IronPython engine is actually an IronPython compiler. It compiles Python code into .NET bytecode (IL) in memory. The compiled Python assemblies can be saved to disk (making binary only distributions possible). (I'll explain what an assembly is in a few moments.)

It compiles to 'in memory' assemblies

Because of the dynamic nature of Python they retain a dependency on the IronPython dlls.

So you use native .NET types

IronPython has seamless integration with the .NET framework. Types can be passed back and forth from native .NET code with no conversion, making it very simple to use .NET classes from IronPython. This means that extending IronPython with C# (including accessing unmanaged code and the win32 APIs) is much easier than extending CPython with C.

An exception to the seamless integration is that .NET 'attributes' are inacessible from IronPython. Additionally you can't consume IronPython assemblies from C#. In order to support the Python feature that you can dynamically swap the __class__ attribute on instances at runtime (and for memory efficiency) IronPython re-uses a single class under the hood. This means that classes created in IronPython can't be used directly by other .NET languages.

Setting Up IronPython

Microsoft seem to be pushing out the .NET framework through Windows Update now (possibly as an optional update?). So many people are finding they don't need to do this. Good news for distributing IronPython applications.

You can download the pre-built binary distribution, or the source code. IronPython is released under a sensible open source license, similar to the BSD License. (You can make derivative works for commercial purposes.)

This means you need Python 2.4 installed. The default location of the Python standard library will be C:\Python24\Lib. You can just copy the Python standard library to another folder and add that location to IRONPYTHONPATH (or manually to sys.path) and you can distribute the standard library with your applications.

The Interactive Interpreter

The IronPython interactive interpreter is ipy.exe, which is also used to run scripts.

This is the equivalent of python.exe.

There is also ipyw.exe which is the equivalent of pythonw.exe and runs scripts without a console box.

To switch on tab completion (very useful) and colour highlighting, run ipy.exe with the arguments:

ipy-D-X:TabCompletion-X:ColorfulConsole

Using .NET: Assemblies

importclrclr.AddReference('System.Windows.Forms')

importSystem.Windows.Forms

We want to use the Windows Forms assembly: System.Windows.Forms

An assembly is a .NET library (usually a dll could be an exe)

In order to use an assembly you must add a reference to it

To add the reference, you use the clr module, an IronPython built-in

You can then import from the namespace in the assembly

Note

Namespaces

Usually, but not always, the namespace within an assembly will have the same name as the assembly.

You can also load an assembly with a 'strong name', specifying the exact version you want:

A Simple Application

We'll now create a trivial Windows Forms application at the interactive interpreter, explaining the steps as we go.

We will create a form, with a title and a button on it that does something when it is pressed.

A Form is a GUI window in Windows Forms

The title is set with the Text property

See the Form on MSDN, and the FormMembers leading to the TextProperty.

The C# example of this on Text property docs says: publicoverridestringText{get;set;}. Meaning that it is a public property, that can be both fetched and set, and it takes (or returns) a string. (And that on the form, the Text property overrides the one inherited from Control.)

Configuring .NET Classes

We import all the names we will use in the application here. Instantiate the form, and set the title.

The random module is from the Python 2.4 standard library.

Windows Forms controls are configured with properties. The controls (widgets) inherit from the Control class, so they all have a lot of properties in common.

The C# example of this on Text property docs says: publicoverridestringText{get;set;}. Meaning that it is a public property, that can be both fetched and set, and it takes (or returns) a string. (And that on the form, the Text property overrides the one inherited from Control.)

Adding Controls

button=Button(Text='Click Me')form.Controls.Add(button)

Add a button to the parent control

Note the alternative way of setting the Text, through a keyword argument in the constructor. This can't be done with C# which doesn't have keyword arguments.

form.Controls is a collection class, with various methods.

Specifically a ControlCollection. They are enumerable and indexable and you can use len and in on it (to get the number of controls and check for membership respectively).

Instead of using Controls.Add we could have done button.Parent=form, which would have added the button to the form's controls collection for us.

The ControlCollection class has useful methods like Remove(control) and RemoveAt(index).

The order you add controls is relevant: the Z-Order

The Z-order is named because it is the 'Z-axis'. Controls you add first are higher in the Z-order and so if they overlap you will see the one you added first. This is more of a problem if you layout your GUI using absolute positions.

You can use methods on the ControlCollection (the collections property) to reorder the controls, or call the BringToFront method on a control.

Event Handlers

Create an event handler function and add it to an event

defonClick(sender,eventArgs):sender.Text=str(random.random())

button.Click+=onClick

onClick is an event handler function (a delegate in C#). We add it to the event using 'add in place'. Note that we can remove it using '-='. It is IronPython 'under the hood' which converts our function into a delegate.

The arguments that Windows Forms event handlers receive are sender and eventArgs. The sender is the control on which the event happened, eventArgs will be an instance of one of many different EventArgs classes, and may contain useful information about the event (state of the mouse buttons, location of the mouse etc). You may also be able to cancel the event by setting Handled or Cancel on the instance.

A small application to illustrate various Windows Forms controls, including a custom executable that embeds the IronPython engine. It is about 18k or so of Python code, and is all presentation layer (very little logic) !

Screenshot

The multi-tabbed image viewer.

510 lines of Python code, 52 lines of C# code.

The C# is in the custom executable, and also in a class which provides access to win32 clipboard functionality.

Initialising with a TabControl

Here we use keyword arguments in the constructor to set the properties.

We initialise the TabControl in its own method.

self is a sub-class of Form; hence self.Controls.Add(self.tabControl).

Dock=DockStyle.Fill is for layout

It tells the parent control how to layout this control. DockStyle is an enumeration (a .NET type) and DockStyle.Fill means fill all the available space. Alternatives are Top, Bottom, Left, Right and None (the default).

An alternative way to layout controls is to use the Anchor property, which can anchor controls to the edges of their parent so the move in a predictable way when resizing.

If you have several controls which use DockStyle.Top then they will be stacked horizontally - for some reason in the opposite order to which they are added: the last one going to the top of the parent, the one before last underneath the last one and so on. Can be very useful. In our application we first add the Tabcontrol (DockStyle.Fill) followed by the ToolStrip (DockStyle.Top - this appears in the middle), last of all the MenuStrip (DockStyle.Top) which appears at the top.

Screenshot on Mono

The multi-tabbed image viewer on Mono 1.2.3 on Windows.

Works ok, but not fully, on Mono. Mono treats the filesystem in a case sensitive way (which windows generally doesn't) and the GUI doesn't refresh when you delete a tab. The about dialog is also a bit scrambled.

The Mono open file dialog also appears to have a bug (on windows at least): if you try to open a directory with a space in the path it returns this as the filename rather than opening the directory.

Further Topics

More things we could have shown you or talked about...

Creating dialogs with the Visual Studio Express Designer (C#), then subclassing in IronPython

Creating C# classes and using them from IronPython

Interaction with threads (invoking back onto the GUI thread)

Why Use IronPython?

If you don't need .NET you don't want IronPython. (Probably!) IronPython is at its best for .NET programmers.

For .NET programmers, Python is nicer than C#. It is also a ready made scripting language for embedding in applications.

Use multi-core processors

Built-in security model with AppDomains

Native Windows GUI: Windows Forms

Access to third-party .NET components

Much easier to extend with C# than CPython and C

IronPython (on .NET) may be an easier corporate sell (Released by Microsoft)

The .NET runtime (the CLR) has a highly optimised JIT compiler, and has seen a lot of work to ensure that multi-threaded applications can take advantage of multi-core processors. This is something that CPython can't do (for the foreseeable future) because of the Global Interpreter Lock.

AppDomains allow you to run code or applications within their own 'domain', for which you can specify the security settings (like restricting access to the filesystem or network). Again, not currently possible with CPython.

Third party components includes a huge range of sophisticated GUI components. Due to the Windows culture you usually have to pay for them! However we use a couple of big components in Resolver, and there just aren't equivalents available for CPython..

Why Not Use IronPython?

Can't use .NET attributes from IronPython (compared to C# / other .NET languages)

IronPython runs the PyStone benchmark about thirty percent faster than CPython. However, according to the Computer Language Shootout (a better benchmark - but there is no such thing as a perfect benchmark) IronPython is generally a bit slower than CPython.

We have however seen interesting, and unexplained, speedups from time to time. IronPython does take advantage of the JIT compiler which is the likely cause of this.

Compiling Python code to assemblies is an intensive task, which can make startup times long for large programs. You can now pre-compile to assemblies to speed this up.

IronPython has no C-API. There are alternative implementations (and wrappers) of many C extensions from the standard library, but not for all of them.

Resolver started developing with IronPython around October 2005. At that point it was almost a daily occurrence to discover bugs in IronPython, create a bugtest to tell us when it was fixed and then implement a workaround.

Now this happens very rarely. We recently discovered a bug in __call__ and keyword arguments, I don't remember the last time we discovered a bug prior to that.

There isn't an awful lot of difference between being dependent on Python and being dependent on .NET though. Also see the note that goes with Setting Up IronPython.

Mono is good, but still incomplete. .NET is a predominantly Windows platform.

Which is a reason to use another .NET language. We've had to do this in Resolver to use unmanaged code from IronPython. Similarly you can't create IronPython classes which can be consumed from another .NET language.

It may be possible to solve both these problems using the System.Reflection API to dynamically emit .NET classes, but I haven't yet found time to experiment with this.