Wednesday, February 24, 2010

Click, Drag and Drop widgets in wxPython

Recently, I was running through a few posts at http://python-forum.org during my free time today and I came across someone who need help in creating a frame, where in the widgets (namely a push button) could be pressed and dragged around the frame, and dropped off at any part of the frame once it was depressed.

At first, I thought it was nothing to break my head over. In fact I wrote a little snipped of code in IDLE (on Windows Vista) and it worked! What was a bit disconcerting was the fact that the widget moved around alright, but didn't really flow smoothly along the frame. The transition from one point to the other was really jarring.

I have worked extensively on wxPython and know how faulty its behavior is on Windows. The true test for an application in wxPython is on a Linux box. I booted into my Linux partition, in a hope to recreate the effect that I had so easily created in windows. A few shell commands later, I could see the familiar window, but to my disappointment, the buttons didn't drag. They just sat there in one place, doing nothing.

Determined to solve this issue, I dug deeper into wxPython. Starting out with penning down the sequence of events that occur, I understood that we have to organize the events starting with the pressing of the left mouse button (only when the pointer is on the push button); followed by the drag/motion event of the mouse and finally concluding with the mouse up event.

However, what was challenging was the fact that once the mouse button was pressed down, the event loop didn't recognize any other mouse events on the button. So how does one go about recognizing the drag/motion event?? I tried to skip (event.Skip()) after receiving the mouse down event, but that didn't seem to work. After about an hour of google, I devised a mechanism to complete the desired effect.

After receiving the mouse down event, we turn on something called "mouse capture" - this focuses all the events coming from the mouse (motion, right click etc) to be directed to the frame and not to the button or the panel. That way, I was able to catch and handle the motion/dragging event. This was bound to the frame and not to any widget in particular. Once the dragging around of the button was complete, the mouse button is depressed causing the "mouse up" event to be released (again directed/bound to the frame, not to any widget in particular). In the handler for the mouse up event, I make a simple check to see if the mouse capture is turned on. If it is, then we simply "release capture" of mouse events, restoring the frame back to its original state.

Here is what the results looked like:

video

The source code for the same is available here (look for a file named wx_DragButtons.py).

The code contains the bulk of the documentation. This is a small, yet elegant solution, which only makes me wonder the kind of effort that goes into creating dynamic user interfaces. Alas, I hope the folks at the forum find this post useful.

Monday, February 22, 2010

Iron ‘Mic(rosoft)’ Python – Creating a simple app from ground up

 

I have heard a lot about Microsoft’s initiative to include Python programming into their existing .NET framework. DotNET is selling like hot cake nowadays and almost every one I know writing professional application level software seems to have sold his soul to the devil.I have resisted this temptation time and time again, and perhaps, the inclusion of Python was what was needed to tip me over the cliff.

So before I signed the thousand page contract with Elizabeth Hurley, and watch her turn into a grotesque red faced monster, I decided to poke around and whet my appetite. I have been toying around with the idea of crossing over to .NET for a while, and I guess with the advent of IronPython, I had run out of excuses.

I started reading ‘Pro IronPython’ by Alan Harris. I always like to get my hands on some well organized reading material to get a feel of what I’m getting into. The book was surprisingly short (it was 300+ pages, but there was little worth reading in it!); and I skimmed through the nine chapters at leisure. The first five chapters were enough reading material for any experienced Python programmer to gauge what IPY was all about.

I downloaded “SharpDevelop”; an open source equivalent to Visual Studio, but only ten times lighter. Along with that I got a hold of the IronPython interpreter from http://codeplex.com/IronPython

That was perhaps the bare minimum that was needed to kick of some reptilian development. The next step is perhaps very important and I, like most programmers or computer geeks, like to delve into a new programming language by making an application of some sort. Something not too big but with a usage that spans many areas of programming. I had an idea loitering around in my cranium for a while, but sheer lassitude and lethargy was preventing me from coding it. I recently got a huge chunk of music from a friend, who was extremely meticulous in organizing his MP3 files. Over 16 GB of music was stored on my filesystem in multiple directories. Here’s how it looked:

image

The music files were segregated by language and then further classified based on the artist. So I had a whole load of files, stored in over 1000 folders (artist folders). I usually like my music stored in a single directory with the file name in this format :<artist name><hyphen><song>.mp3

Obviously, I wasn’t going to sit and manually cut and paste each file, and search utility on Vista is perhaps the lamest application ever. So I took it upon myself to create a small app to do this for me.

I had made a command line application for recursively searching, indexing and organizing my files before, using Python on Linux. I wanted to check if IPY would work with previously designed core python modules as well.

So, objectives for this application:

  1. Get a feel of GUI design and associated generated code in IPY
  2. Check if IPY plays well with core Python modules
  3. Create an application that will allow the user to recursively search any directory for files of one or more extenstions/file types, and allow him to cut/copy all these files from their respective source directories to a specified destination
  4. Understand IPY’s peculiarities with respect to worker threads and their independence of the UI threads
  5. Assess convenience in application deployment

STEP 1 : Creating the GUI

image

Voila !!! EXODUS is born !!!

Download the source from my groups page (look for exodus.rar)!!

There was nothing to creating a GUI. The one thing (also the only thing) I’ve always loved about MS, was that they made GUI creation so simple. No long hours of coding… a few mouse clicks and my GUI was ready.

PITTFALL : There is however one problem with GUI creation and code generation in IPY. I didn’t face this while working with Visual Basic Express 2008. When you create a widget or tool (e.g. the push button, or a text box etc.); you must decide on the “name” of the object then and there. If you accidentally switch to the code view before appropriately naming the object, the code is generated for that widget/object with the standard names that the IDE gives them. So you’re going to have names like ‘textBox1’ and ‘button1’ sitting all over your GUI code. In Visual Basic, this wasn’t such a big issue, because you could go and rename the object later on, and the generated code would be changed accordingly… NO SUCH FEATURE WITH IPY!!! So name first, code later.

Barring that, there was no other problem in designing the GUI. You can pick out a widget of your choice from the toolbox panel (dockable… so look hard!)

The GUI code (auto generated) appears as a class with the class name as the name of the form. So I accidently ended up with a form called “MainForm” (default name given to the form) and the class created was also given the same name. This didn’t worry me so much as my application was only a single form application. But if you’re working on more than one form, things might get a bit difficult. So I re-iterate myself… name first, code later. In this form was a method titled InitializeComponent(self).

This method is very readable and neatly organizes the code for all the widgets that you have created. Some properties of these widgets are also assigned in this part of the code. This method is the first to be called from the class constructor or __init__(self) method.

STEP 2 – Creating the event handlers for widgets

The GUI is good to look at and that is all that it is worth, without any code a.k.a “Wiring”. My application had been designed keeping the following in mind:

  • Use at least four to five different widgets (buttons, radio buttons, checked style list box, text boxes, status strip with a progress bar)
  • There must be some kind of background task (a long running process), which in our case is the copying or moving of files from the source folder to the destination. This will help illustrate the effectiveness of the UI-worker thread independence
  • Use at least one file/folder/print/open/save dialog box – this will help us understand how dialog boxes return selected information
  • Use at least one message box or alert box to display passive messages to the user or draw attention to something

You can code a handler for a particular widget, or generate it by double clicking on it in the design view. Automatically, the code for the handler associated with that event and widget (e.g. click event for a button widget) will be generated. The body of this handler will be a simple “pass” statement. You have to write your code in that handler.


Note:

If you want to code an event handler, read this note. Consider a button widget with the name btnOK and you want to code the handler for a ‘Click’ event. Now all you have to do is bind the widget to its handler. Let’s also say that you have written a routine called ‘btnOKClicked(self,sender,event)’ which you want to invoke once this button is clicked.

To bind the method to the widget for the event, the code looks like this:

<widget_name>.<event_name> += self.<method_name>

In the above scenario, you’ll have:

self._btnOK.Click += self.btnOKClicked

The code generation scheme does exactly the same thing and saves you the trouble of doing it manually.


My application demanded that I’d be able to use a folder browse dialog box to select both source and destination directories; have a list box with commonly used extensions – for this I have used a checked list box so that one may select more than one extension; a text box is provided so that one may enter an extension (e.g: .rtf, .xyz etc) that is not present in the list – this allows flexibility since new file formats will come out every now and then and there is no way to know what type of files the user wants to move.

If you seek information about widgets and their associated members or methods, you can refer to any resource on C# or VB for the .NET framework. IPY implements the same methods as well. All the assemblies from .NET are available in IPY.

For instance the checked list box widget, implements a method called Add. Let’s assume that we have a widget with the name clbExtension (a checked list box). In Visual Basic, you’d probably access this as follows:

clbExtension.Items.Add(“Item number 1”)

In IPY, the usage is exactly the same. The only difference being that since there is a class in place for the entire form, all widgets on that form are members of this class. All event handlers are also part of the class for the form. Here’s how it goes in IPY, pay close attention to the last line:

image

So IPY preserves the same methods that are consistent with the rest of the .NET framework. If you’re familiar with widget objects and their members and have used them on VB or C#, you only have to master Python syntax and its libraries. Even if you’re not and are having a hard time finding resources specific to IronPython for a particular widget; fret not, all you have to do is find a VB or C# equivalent. That combined with a little common sense will have you coding away.

Please note, that in the snippet above, the only part that you actually code is the handler. The rest is generated by the IDE :-) just like it is in Visual Basic. Double click a particular widget or you can use the pull down menus at the top of the code view to generate an empty handler for a particular widget. Then simply feed in your code into that handler.

If you were paying close attention, you’d notice that in the snippet above, the component for the checked list box has been created and the code for the event handler has been put down. But there is no code to bind the handler to the widget. This is because, I typed out the snippet by hand. Be rest assured that the IDE will make no such blunders :D

If you’re familiar with Python syntax, you’d realize that the underscore (_) before the name of every widget object is used/generated to ensure that these widgets have private access only.

STEP 3 – Understanding a Background Worker

When designing GUI, one must understand, that any front end that responds to events (clicks, text entry etc.) has a process that runs in a continuous loop to sample these incoming events. This is commonly called the ‘UI Event loop’ or the UI thread.

If you interrupt this thread (run a really long for loop in the same thread), you’ll realize that the UI becomes unresponsive and freezes. To avoid this, all “heavy duty” work or long running or time consuming processes must run in separate threads that are concurrent with the UI thread. This will allow your application’s UI to remain responsive and prevent your operating system’s scheduler or process manager, from declaring your application as a passive process.

Here too, there are a few subtle techniques. Imagine a scenario with a form having a button. When clicked, the form starts downloading a really large file from the web, and reports its status using a progress bar. Another common feature is disabling this button when the download begins and enabling it when the download is complete.

Now, in order to do that you have to know the following:

  • when the download has commenced
  • the current status of the download
  • and when the download is complete

Since we have to run our download in a separate thread, to allow for the UI to be updated, the downloader thread must communicate with the UI thread. Each GUI toolkit offers some mechanism or the other for this communication to take place (PyQT for instance employs a signal and slot mechanism, it is one of the simplest and most effective mechanisms till date and offers the programmer complete flexibility). IronPython, like the remainder of the .NET framework uses something called a “Background Worker”

A background worker is basically an object with the following characteristics:

  • it has a property that stores the name of the routine which is the primary task viz. DoWork
  • it has a property that stores the name of the routine which must be invoked, when the primary task is complete viz. RunWorkerCompleted
  • and another property that stores the name of the routine which will update the UI on the progress of the primary task viz. ReportProgress (in my application I have not utilized this property. Instead I’ve directly invoked the routine to update the GUI)

Lets take a look at the code:

image

The first line creates a member called bgWorker which is an object of the type BackgroundWorker. The second line indicates that this worker object reports the progress of the task. The third and final line in the snippet above, indicate the two properties mentioned above. searchFilterAndMove & resetUI are two methods that perform the primary task (copying the files from source to destination) and reset the UI, once the primary task is complete (a cleanup task after the heavy work is over).

That was about the creation and initialization of the background worker. Now lets see how we can get some real work done. I have a button on my form that reads “MOVE IT!”; which when clicked, causes the movement of files from source to destination. Here is the event handler for that button:

image

The lines indicated the intermediary processing that happens before the background worker is told to start doing its work. The last line is the most important one. The RunWorkerAsync() method of the bgWorker object, causes the routine searchFilterAndMove to get activated. Moreover, this routine now runs asynchronously of the UI thread, thereby helping us achieve our objective. When the searchFitlerAndMove routine is complete, it will call the resetUI method as discussed earlier. The routine to update the GUI is called from within searchFilterAndMove.

You may look up the definitions of these routines to get a better understanding of how they work by downloading the source.

 

STEP 4 – Mixing up IPY and Core PY

At the beginning of this article, I had enlisted out the collaboration of IronPython with core Python modules as an important issue. Well, to say the least, its not smooth. But with a little work, anything is possible :-)

I had created a library (just another .py file) with some helpful functions. I call this module “helpermodules.py”

It contained a routine that would recursively scan a source directory for all its children, another routine to validate the extension (manual entry) and two routines to cut or copy files from one place to another.

In designing these, I had used modules such as os, shutils and glob.

I wanted to know how well the IPY interpreter would handle these. When I was working with the SharpDevelop IDE, I would use the in built compiler/interpreter, to run my code. Initially, while testing the widget related methods, all was well. But the moment I included routines that required core Python modules, a lot of errors started showing up. Therefore, I had a form with widgets that would look nice and seem hale and hearty on the UI front, but had little or no real functionality.

A few Google searches later, I realized that a lot of people were complaining about the use of various core Python modules. But I had come too far to turn back now. What was more disconcerting was the fact that imports of the core python modules worked brilliantly at the IPY interpreter shell, and failed miserably on the SharpDevelop IDE’s run. I finally found a post that bailed me out of my predicament. Apparently, there is a certain addition one has to make to allow IPY to use the core Python modules. Here it is:

image

The first two sections are what need to be added to the code generated by SharpDevelop. The logic of these statements is merely to provide a link between the IPY (which is the python interpreter) and the CLR (Common Language Runtime of the .NET framework). This way, all the resources that CLR (and hence, .NET) has to offer will be made available to the IronPython interpreter.

Another queer observation was that after this was done, the design file of the project (which is nothing but the design of the GUI which we have made by dragging widgets from the toolbox to the form etc.) was corrupted. Hence, SharpDevelop couldn’t display the design of my form. Thankfully, at this stage, I was through with the design of the UI, but if I needed to make a few changes later on, it would have been only through code and not through an interactive UI designer. I think this had more to do with the SharpDevelop IDE than with IronPython. Still trying to figure out why exactly did something likes this cause a ripple :D

Now this code file ran beautifully using the IronPython interpreter at the command line. The same effect can be achieved by running the project through SharpDevelop’s built in IronPython interpreter (Ctrl+Shift+W).

What is more important is that the core python modules worked flawlessly. The form takes a little time to pop up but that is simply because IronPython is built on C# and really doesn’t create an intermediate bytecode like Python does (which is also the reason for its superior performance).

There is a large part of core Python that is still not supported by IPY (there is a little dispute over this issue), but perhaps that will change in times to come.

My application is currently in its raw state and has not been deployed. I hope to understand deployment shortly and want to check out Microsoft’s IronPython Studio to see if they have “click once deployment” (like in Visual Basic 2008); that helps in creating a ready to use bundle for your application.

This project was purely for a didactical purpose. Hopefully, I was able to help you find what you were looking for. I will be immersing myself into IronPython in the days to come, and target more specific topics in forthcoming posts.

Summary

  • GUI design using SharpDevelop IDE
  • IronPython’s compatibility with Visual Basic.NET (or C#)
  • Creating event handlers
  • Using background worker objects
  • Mixing IPY with core Python

To download the source of the project, click here.