Interpreted Python and WindowsForms experiment
I have been shopping around for a while now for a dynamic language to use when prototyping pet projects at home. Everything I read about Python seemed perfect except, of course, the lack of a .NET compiler. This week Jim Hugunin of Jython fame finally dropped the first binary build of IronPython (Python for .NET).
According to the readme.htm you can build regular IL assemblies (.exes and .dlls) with IronPython, but what I have been playing with is the interpreted python console.
Something I was pleasantly surprised to find was how well the design goals of WindowsForms mesh with the needs of interpreted languages. I think there are two reasons for this:
· We have tried to avoid invalid states (e.g., for the most part you can set properties in any order, you can call methods at any time, etc.) This is part of the Managed API Guidelines and is just common sense when building a predictable platform.
· The second is that WindowsForms supports imperative style programming. When working in a compiled language I tend to follow a pattern of subclassing Form and having all of my child controls encapsulated. When working with the python interpreter I found it easier to write in an imperative style where I instantiate a vanilla Form (i.e., not a MyDialog subclass) and start adding controls to it as follows:
>>> from System.Windows.Forms import *
>>> f = Form()
>>> b = Button()
>>> def OnClick(sender, args):
... MessageBox.Show("Clicked");
...
>>> b.Click += OnClick
>>> f.Controls.Add(b)
>>> Application.Run(f)
Which as it turns out, WindowsForms was intentionally engineered to support.
Why was WindowsForms engineer to support this you ask? It is because WindowsForms had an interpreter as a customer since day 1: the Visual Studio designer. When you are designing a dialog the designer does not create an instance of your derived class. It creates an instance of the base class (which is essentially a new Form() unless you are designing an inherited Form) and then interprets the code statements in the InitializeComponents() method to populate the form with buttons, checkboxes, etc. (BrianPe goes into the details here).
I have run into a snag, however, while experimenting with WindowsForms. When the Form is closed it is automatically disposed which means you can not tweak it and then Application.Run(f) a second time because WindowsForms does not allow resurrection of a Disposed() class. This kills iterative development which is pretty much the whole goal of an interpreted language. I haven't played with it long enough to know if there is a work around (I suspect with some foresight one could sink the Closing event to Hide() rather than Close() because I seem to recall that the Closing event was cancelable).
When I get a chance I would like to recompile IronPython with the CLR build from Longhorn 4074 and see how Avalon fairs in the same scenario. We have an intrinsic advantage in that so far we have avoided needing the Dispose() pattern for our controls.
Oh, and if you miss the news, Jim Hugunin is starting his first day as a Microsoft employee on Monday.
Comments
- Anonymous
August 01, 2004
I've solved this problem in Python.NET, and although I haven't yet ported it to IronPyton, the solution is the same:
1. Write a Python "console" in .NET, i.e. a textbox Form instead of the Windows console, such that .NET is running the message loop. (See Idle (Tkinter), and PyCrust (wxWindows) for examples on how to do this.)
2. Using this environment, create forms as above, but instead of using ">>> Application.Run(f)", display the form with ">>> f.Show()", where "f" is the Windows Form Object.
This returns you to the Python ">>> " prompt since .NET is running the message loop for the App.
You now can interactively change the form. Please email me if you'd like a copy of my source-code. Email: thane@magna-capital.com - Anonymous
August 01, 2004
Thane - I see what you're getting at... Rather than attempt to supress the dispose you have taken the approach of showing the form non-modaly so you do not have to close/dispose it in order to continue tweaking it. Good suggestion.