Languages - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Languages arrow Page 5 - A First Look at IronPython: Where Python meets .NET
A First Look at IronPython: Where Python meets .NET
By Jeff Cogswell

Rate This Article: Add This Article To:

A First Look at IronPython: Where Python meets .NET - ' Python All The Way '
( Page 5 of 6 )

Generic Collections

One of the nicer features of .NET 2.0 is the introduction of generics. The purists will balk at what I'm about to say, but generics are a lot like C++ templates.

ADVERTISEMENT

When you create a generic collection, unlike the more, uh, generic collections of .NET 1.0, you specify exactly what type of items you'll put in the collections.

IronPython supports the use of the .NET generic collections.

Here's a sample IronPython session where I create a dictionary object that maps strings to integers.

I then iterate through the keys, grabbing the individual values stored in the dictionary:




>>> from System.Collections.Generic import *
>>> d = Dictionary[str, int]()
>>> d.Add('abc', 1)
>>> d.Add('def', 2)
>>> d['abc']
1
>>> for i in d.Keys:
...     print d[i]
...
1
2

Don't forget the () after the declaration of Dictionary. Otherwise, your calls to Add will fail, because you're simply storing the type Dictionary[str, int] into your d variable, and not actually creating a new instance.

Incidentally, notice what I place inside the type parameters for the Dictionary class: str and int. These are built-in Python type names that are available to regular Python, not just IronPython. However, once again, inside IronPython, these items map to the .NET classes:

>>> d['abc'].GetType()
System.Int32
Delegates and Events

If you're a seasoned .NET programmer, then you're likely familiar with delegates and the power they provide. If you're not familiar with delegates, think of them more-or-less as pointers to a member function of a particular object (called an instance method). Delegates can also point to static methods that are not instances of a particular object, and further, delegates can multicast, meaning they can be chained together and viewed as a single delegate that calls multiple methods.

Delegates are particularly useful in the event-handling system of .NET. When you create a button, you will likely have code that you want called when the user of your program clicks the button. You put the code inside an object's member function. The .NET system uses delegates for the event. The Button class includes a Click member, which is a delegate. Inside this delegate, you supply the instance function (called an event handler) containing the code that runs in response to the button click.

IronPython provides native support for event delegates. As you already saw in the preceding example, adding handlers for events is easy:

this.Click += this.ClickHandler

This code added a Click event to the Form object.

Another interesting example is described in a tutorial that ships with IronPython; I'll provide a similar but more complete example here. This example makes use of the FileSystemWatcher class, which is a .NET class that lets you monitor file system activity. For example, you might be writing a program that lets users edit a file on disk. Suppose the file is modified from outside your program. A nice feature is for your program to detect that the file changed, and to notify the user.

The FileSystemWatcher class is part of the System.IO namespace. It's easy to use; you simply create a new instance, passing the name of the directory containing the files you wish to watch. Then you specify a pointer to the function that runs (that is, the event handler) when a change occurs to the files being watched.

Here's a complete sample.

import clr
clr.AddReference("System.Windows.Forms")
from System.IO import *
from System.Windows.Forms import *

class MyForm(Form):
    def __init__(self):
        Form.__init__(self)
        self.watcher = FileSystemWatcher()
        self.watcher.Changed += self.FilesChanged
        self.watcher.Deleted += self.FilesChanged
        
        # Create the menu
        mainMenu1 = MainMenu()
        fileMenu = MenuItem()
        fileMenu.Text = "File"
        menuItem1 = MenuItem()
        menuItem1.Text = "Open..."
        menuItem1.Click += self.OpenMenuHandler
        menuItem2 = MenuItem()
        menuItem2.Text = "Exit"
        menuItem2.Click += self.ExitMenuHandler
        fileMenu.MenuItems.Add(menuItem1)
        fileMenu.MenuItems.Add(menuItem2)
        mainMenu1.MenuItems.Add(fileMenu)
        self.Menu = mainMenu1
        
        self.RichText1 = RichTextBox()
        self.RichText1.Dock = DockStyle.Fill
        self.RichText1.ReadOnly = True
        self.Controls.Add(self.RichText1)
        
        self.OpenDialog = OpenFileDialog()
        self.OpenDialog.InitialDirectory = "c:\\"
        self.OpenDialog.Filter = "*.txt|*.txt|*.rtf|*.rtf|*.*|*.*"
        
        print "Ready"
        
    def ButtonHandler(self, source, ev):
        pass
        
    def OpenMenuHandler(self, source, ev):
        if self.OpenDialog.ShowDialog() == DialogResult.OK:
            
            # Turn off current watcher
            self.watcher.EnableRaisingEvents = False
            
            ext = self.OpenDialog.FileName.lower().split(".")[-1]
            # or you can use the .NET framework to extract ext
            # ext = Path.GetExtension(self.OpenDialog.FileName)
            # ...but be careful, you'll have a dot at the beginning
            # e.g. if ext == ".rtf"
            if ext == "rtf":
                richtype = RichTextBoxStreamType.RichText
            else:
                richtype = RichTextBoxStreamType.PlainText
            print richtype
            print self.OpenDialog.FileName
            self.RichText1.LoadFile(self.OpenDialog.FileName, richtype)
            
            # Set up watcher for this file
            self.watcher.Path = Path.GetDirectoryName(self.OpenDialog.FileName)
            self.watcher.EnableRaisingEvents = True
    
    def ExitMenuHandler(self, source, ev):
        self.Close()

    def FilesChanged(self, source, ev):
        if self.OpenDialog.FileName == ev.FullPath:
          if ev.ChangeType == WatcherChangeTypes.Changed:
              MessageBox.Show(self, "Warning: File Changed.",
              "Warning", MessageBoxButtons.OK)
          elif ev.ChangeType == WatcherChangeTypes.Deleted:
              MessageBox.Show("Warning: File has been deleted.",
              "Warning", MessageBoxButtons.OK)
        
    def ClosedHandler(self, f, a):
        self.watcher.Changed -= self.FilesChanged
        print "finished"

f = MyForm()
Application.Run(f)

This code creates a form that contains a menu bar and a rich text box in read-only mode. When the user clicks the File'Open menu, an Open dialog box appears, letting the user choose either a text or RTF file to display in the rich text box.

You can see how I added handlers for the menu items:

menuItem1.Click += self.OpenMenuHandler

This tells IronPython to run my OpenMenuHandler code in response to the Open menu item. Similarly, I set up events for the FileSystemWatcher class, like so:

self.watcher = FileSystemWatcher()
self.watcher.Changed += self.FilesChanged
self.watcher.Deleted += self.FilesChanged

This code creates a FileSystemWatcher object, and assigns my FilesChanged method to both the Changed event and Deleted events. The FileSystemWatcher class doesn't do anything, however, until you tell it what to watch for, and then tell it to start watching. I do those two things after the user opens a file:

self.watcher.Path =
    Path.GetDirectoryName(self.OpenDialog.FileName)
self.watcher.EnableRaisingEvents = True

Notice that I use the .NET framework's Path class to extract the DirectoryName from the Filename string. I save that directory in the watcher's Path member. Then I start watching events by setting EnableRaisingEvents to True. (The word True is a built-in regular Python identifier; it wasn't available in early versions of Python.)

The handler function is easy then; I check the event object that comes in for the type of event (changed or deleted), and then pop up a message box with a warning.

To try out the program, run it, and use its Open menu to open a text or RTF file. Then go to either Windows Explorer or a command prompt, and delete or otherwise modify the file you're looking at. When you switch back to your Python program, a message box warns you that the file changed. In a real application, you would probably include an option to reload the file if it changed, or to save or close it if it was deleted.



 
 
>>> More Languages Articles          >>> More By Jeff Cogswell
 



Microsoft's Future: A Chat With Their CTO, Barry Briggs

Play Video >

All Videos >

Julia explores the Robotics Studio!

Read now >

Messages to Bill Gates!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.