Another key point is that the mouse messages all report the position in "Screen Coordinates" (i.e., relative to the top-left corner of the screen) rather than in "Client Coordinates" (i.e., relative to the top-left corner of our window). You use a member function
PyCWnd.ScreenToClient() to transform these coordinates.
The final step to complete the
View is to draw all your strokes whenever the window requires updating. This code is simple: you iterate over the list of strokes for the document, drawing lines between the coordinates. To obtain this behavior, use the code:
def OnDraw(self, dc):
# All we need to is get the strokes, and paint them.
doc = self.GetDocument()
for startPos, endPos in doc.GetStrokes():
And that's it! You now have a fully functional drawing application, capable of loading and saving itself from disk.
Creating the Application Object
The simplest way to create an application object for Scribble is to subclass one of the standard application objects. The PythonWin application object is implemented in
pywin.framework.intpyapp, and it derives from the
CApp class in
pywin.framework.app. This base class provides much of the functionality for an application, so we too will derive our application from
This makes the application object small and simple. You obviously may need to enhance certain aspects; in this case, you should use the
pywin.framework modules as a guide. The minimal application object looks like:
# The application object for Python.
from pywin.framework.app import CApp
# All we need do is call the base class,
# then import our document template.
# And create our application object.
To run this, use the following command line:
C:\Scripts> start pythonwin /app scribbleApp.py
An instance of Pythonwin.exe now starts, but uses the application object you defined. Therefore, there'a no Interactive Window, the application doesn't offer to open .py files, etc. The Scribble application should look like Figure 20-5.
There is also a technique to avoid this command line, but you need a copy of a resource editor (such as Microsoft Visual C++). You can take a copy of Pythonwin.exe (name it something suitable for your application), then open the .exe in the resource editor and locate an entry in the string table with the value
pywin.framework.startup. This is the name of a module executed to boot the PythonWin application; the default script parses the "/app" off the command line and loads that application. You can change this to any value you like, and PythonWin then loads your startup script. See startup.py in the PythonWin distribution for an example of a startup script.
PythonWin and Resources
As we've discussed, MFC provides a framework architecture, and much of this architecture is tied together by resource IDs, integers that identify Windows resources in a DLL or executable.
For example, when you define a
DocumentTemplate, you specify a resource ID. The previous example doesn't specify a resource ID, so the default of
win32ui.IDR_PYTHONTYPE is used. When a document is created, MFC uses the resource ID in the following ways:
- The menu with the ID is loaded and used for the document's frame. This allows each document supported in an application to have a unique set of menus as is common in MDI applications.
- The icon with the ID is loaded and used for the document's frame.
- The accelerator with that ID is loaded, providing document-specific shortcut keys to many of the menu functions.
Another example of the reliance on resource IDs is in the processing and definition of menu and toolbar items. Each command in the application is assigned an ID. When you define menu or toolbar items, you specify the menu text (or toolbar bitmap) and the command ID. When MFC displays a menu item, it uses a string defined with the same ID and places this text automatically in the application's status bar. When the mouse hovers over a toolbar item, MFC again looks for a string with the specified ID and uses it for the tooltip-text for the button. This architecture has a number of advantages:
- Hooking the various pieces of your application together becomes simple. You define an icon, menu, accelerators, strings, and so forth with the same resource ID, and MFC automatically ties all these together for your application.
- If you are working with an existing MFC or C++ application, there's a good chance you already use resources in a similar fashion, so PythonWin often fits naturally when embedded in these applications.
- When you need to respond to a GUI command, specify the command ID. The same code then handles the command regardless of whether it was sourced from the keyboard, toolbar or menu.
- Localizing your application for other languages becomes simpler. You define new resources in the new language but use the same IDs, and the application still works regardless of the particular resources in use at the time.
However, it also has a number of disadvantages:
- Python doesn't have a technique for defining resources, such as dialogs, menus, toolbars, or strings. So while this scheme works well using MFC from Microsoft Visual C++ (which does provide this facility), it doesn't work as well from Python.
- It's a pain to move beyond the MFC-provided framework. As soon as you begin manually defining and managing these resources, you aren't much better off than if you simply had used the raw Windows GUI API.
PythonWin can use resources from arbitrary DLLs, thus you can define your own DLL containing only resources. PythonWin makes it easy to use these resources; just load the DLL (using
win32ui.LoadLibrary()), and PythonWin locates and uses the resources in this DLL.
If you are writing a large application, you'll probably find it worthwhile to define your own resource DLL when using PythonWin. The benefits offered by the framework make it worth the pain of initially setting everything up. On the other hand, it does make PythonWin somewhat cumbersome for defining these applications purely from Python code.
For the vast majority of Python users in Windows, PythonWin will never be more than an interesting IDE environment for developing Python applications. But many other Windows developers are beginning to use PythonWin to develop Windows applications. When comparing the three GUI toolkits available in this book, you will probably come to the conclusion that PythonWin is the least suitable for simple, small GUI applications written in Python, and this would be fair. However, depending on your particular circumstances (usually either because you have an existing MFC investment or it's important to use some user-interface features offered only by PythonWin), it may be a good choice.
PythonWin suffers from a lack of decent documentation. A Windows help file is included that contains a reference guide for all of the objects and methods exposed by PythonWin, but PythonWin doesn't include a comprehensive overview of the MFC framework. There are many good MFC books available, so a specific recommendation is impossible. Information from Microsoft on MFC can be found at http://msdn.microsoft.com/visualc/.
Mark Hammond is an independent Microsoft Windows consultant working out of Melbourne, Australia.
Andy Robinson is a London-based consultant specializing in business analysis, object-oriented design, and Windows development.
Discuss this article in the O'Reilly Network Python Forum.
Return to the Python DevCenter.