IronPython
Written by Mike James   
Thursday, 15 July 2010
Article Index
IronPython
Duck Typing
.NET interop

.NET interop

The whole point of using IronPython as opposed to just Python is that it promises access to .NET and what this really means is access to the .NET Framework.

You can create a range of project types but the basic principles of interop are the same in each. Lets start with a Windows Forms application - create a new project. You can't make use of the drag-and-drop designer to build a user interface so you have to create all of the objects needed and wire up interrupts manually.

To create a window with a textbox and a button you would use first create a form class that inherits from Form and define all of the controls needed:

import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import *
class MyForm(Form):
def __init__(self):
self.Button1 = Button()
self.Button1.Text = 'Click Me'
self.Button1.Click+=OnClick
self.Controls.Add(self.Button1)
self.Textbox1 =TextBox()
self.Textbox1.Text='Ready'
self.Textbox1.Top=50
self.Controls.Add(self.Textbox1)

The initial instructions load the assemblies and references we need to use. Next the class is defined and its constructor creates a Button and a Textbox. In a real example you would have to spend a lot more effort setting properties to achieve a reasonable layout.

To define a click event handler for the button we need to define the OnClick method:

def OnClick(source,event):
form.Textbox1.Text='clicked'

This simply has to have the correct number of parameters. Finally we need to create the instance of the MyForm class and start the whole application running:

form = MyForm()
Application.Run(form)

Now the program will display the message in the textbox when you click on the button.

Notice that you can use the .NET objects just as if they were Python objects and the only real difference is that you have to remember to make use of the self keyword as appropriate.

To make use of any .NET classes you have to first import than using the import statement. Specifically you have to import the special System module that connects IronPython to the .NET classes. If you simply use import System you have to use fully qualified class names but you can also import namespaces so that you can use short names.

For example a standard Windows Forms project would need something like:

import System
from System.Windows.Forms import *
from System.ComponentModel import *
from System.Drawing import *
from clr import *

Once you have imported a .NET class you can use it in Python style, i.e. you don't need a "new" to create an instance. For example:

a=System.String("Hello World")
form.TextBox1.Text=a

creates a .NET String which can be assigned to any .NET property that accepts a String.  If you don't want to use a fully qualified name then you can use:

from System import String
a=String("Hello World")

What is perhaps less obvious is that once you import a .NET class it can mix with the pure Python classes.

For example, if you use a Python string and don't import System then the string doesn't support the usual .NET string methods such as ToUpper. That is:

c="Hello World"
form.TextBox1.Text=c.ToUpper()

will fail as a Python string object doesn't have a ToUpper Method.

However, if you try the same thing after importing System you will discover that it does work.

In other words, importing a .NET class can change the behavior of your program.

This is very odd and something you need to be aware, or perhaps beware, of.

IronPython in action with WPF

IronPython works with WPF and XAML but it is still underdevelopment so there are parts that are supposed to work but don't.

To see this aspect of IronPython in action the best thing to do is to start a new WPF application project. As long as you are using IronPython Tools you will see the familiar drag-and-drop designer and the XAML editor.

The drag-and-drop designer is supposed to work but in the version of the Tools used for this article it didn't. However it was possible to enter XAML to create controls and then edit these in the designer by dragging and sizing.

To get started enter the following XAML into the editor:

<Grid x:Name="Grid1">
<Button Name="Button1"
Margin="20,10,150,215">
"Hello World"
</Button>
<TextBox Name="Textbox1"
Margin="20,60,60,170">
</TextBox>
</Grid>

At this point you might be tempted to try to work with names such as Button1 and TextBox1 but at the moment this doesn't work. IronPython works with uncompiled XAML and as such bindings between names in XAML and names in IronPython aren't made and so you can't simply use XAML assigned names in a IronPython program. It is possible to write some code that adds the names but this would take us too deep into the interaction between IronPython and other .NET languages and facilities.

The XAML is converted into a WPF object graph by the use of XamlReader:

window=XamlReader.Load(
FileStream('WpfApplication5.xaml',
FileMode.Open))

XamlReader returns an object which is the root of the object graph i.e. a Window object in the case of the XAML code given above.

The controls are dynamically created as child nodes within the tree and to find a reference to any given control the simplest solution is to use the LogicalTreeHelper object and its FindLogicalNode method. This will return a reference to the control with a specific name that is a child of the initial control. So for example:

tb=LogicalTreeHelper.FindLogicalNode(
window,"Textbox1")

returns a reference to a child control of the window with the unique name 'Textbox1'. Once you have the reference to the control you can work with it as normal. For example:

tb.Text="hello"

The only problem with this approach is that you don't get any help from IntelliSense prompting for properties of the WPF controls.

As a more complete example consider the task of setting the Textbox's caption to "click" when the button is clicked. First we need to import all of the .NET support we need:

import clr
clr.AddReference('PresentationFramework')
from System.Windows.Markup import XamlReader
from System.Windows import *
from System.IO import FileStream, FileMode

The click event hander is just:

def OnClick(source,event):
tb.Text="Clicked"

Notice that you don't have to type the parameters - as long as you have the correct number for the event handler then everything just works.

Next we load the XAML and customise the controls:

window=XamlReader.Load(
FileStream('WpfApplication5.xaml',
FileMode.Open))
tb=LogicalTreeHelper.FindLogicalNode(
window,"Textbox1")
btn=LogicalTreeHelper.FindLogicalNode(
window,"Button1")
btn.Click+=OnClick

Finally we set the .NET application running and this is done in exactly the same way as in most .NET projects - only in the case of C# and VB it is hidden from the programmer within generated code:

app = Application()
app.Run(window)

If you run the whole program you will be able to click the button and see the text in the Textbox change.

Where next

Python is an interesting language that has many powerful features that we haven't had space to touch on.

In particular you can use COM interop from IronPython and you can use Python libraries, two features which extend the language's usefulness. 

IronPython is surprisingly good for a language that has been grafted onto .NET. It still has some potentially serious drawbacks - you can't use attributes and its integration with XAML isn't perfect. Better integration with Visual Studio or any IDE for that matter would make the whole thing easier to use and progress in this area has been slow and hampered by the need to keep up with .NET technology as it evolves.

It is worth knowing that there is a Silverlight project option within the Visual Studio Tools but at the moment creating Silverlight applications is just as ad-hoc as for WPF or Windows Forms.

IronPython also suffers from the usual problem associated with most open source projects, the documentation is terrible.There is plenty of documentation telling you about advanced features and even very simple features. What tends to be missing are the crucial pieces of information that you need to get started, such as how to wire up WPF event handlers, how to get at controls generated by Xaml and so on.

But even after all of these difficulties are taken into account it's an interesting example of a dynamic language running on the .NET platform - which after all was not designed for dynamic languages.

Banner

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Tuesday, 15 November 2022 )