I just found an MS Agent characters I haven't seen before, and though to share how to control them from within Python
I think this article was driven by the fact that this April, Microsoft discontinued Windows XP. And why is that related? Because if you remember Windows XP, somehow I think you will remember --maybe in annoyance-- this cute little dog called Rover from Windows XP Find Files bar:
Howdy, Rover? Not even you, now your master was also dead. So sad..
Long story short, Microsoft did not gain success with this MS Agent stuff. Without even realizing it, one of the first thing I do when re-installing Windows XP is ... get rid of Rover from Windows Explorer! And yeah, unfortunately, I am not the only one.
But still, in this article we are going to use it as a study case on how to properly use PyWin32, an amazing Python package from Mark Hammond, that let your Python application integrate tightly with Windows. For example, do you know that Dropbox client application was built with Python?
Great, lets dig more on this matter!
Three Different Routes for Windows Integration
There are three different routes you can take to integrate Python application seamlessly to Windows environment, which are:
- Using PyWin32 package from Mark Hammond.
PS: I love seeing people use Python in many diverse computing environment like that!
- Using comtypes package from Thomas Heller
When you want to automate particular COM Objects that are derived from IUnknown instead of IDispatch, this will be the time for you to use comtypes. It really is amazing knowing what you can do with comtypes. For example, do you know that DropBox client application is a Python application? This article from DropBox Tech Blog may shed some light for you! Or, this codeproject article that demonstrate how to work with custom COM interface, will help you in your next step in Windows integration.
- Using ctypes package, also authored by Thomas Heller
When your Windows integration necessity are beyond custom COM interface, in which you need to call specific DLLs either as part of Windows distribution (such as those reside in kernel32.dll) or wrap your own C function, you will going to need ctypes.
Quick note regarding COM/Active-X: Although it's not dead, COM (Component Object Model) is technically superseded by .NET Framework. In its essence, COM is a software component architecture that let diverse application reuse components from each others to build a working application. As you know that Windows exposed its functionality through Windows API (e.g creating system tray application using Shell_NotifyIcon function), it also exposed other parts of its system using COM object (e.g. INetCfg is an IUnknown based COM Object that let you configure Windows networking configuration). Later on .NET Framework was introduced and at the current moment a new API is come to live: Windows RT. I love hearing those jargon!
In this article, we are going to look on how to create a better Python application that integrate well in Windows, using PyWin32 packages, specifically its win32com package to automate Microsoft Agent component. We are going to make a talking application that will obediently sit low and still in System Tray, and will speak up the time every each hour. Lets get down to it!
Installing Microsoft Agent
I you are still using Windows XP (woops), then nothing else you should do. MS Agents was right there alright. But if you happen to use Windows 7, go ahead to this page from Microsoft and install a hot-fix to bring back MS Agent. Users who use Windows 8/8.1 may find this support page or this page useful. Another interesting collection of MS Agent characters can be found here.
Through out the rest of the article, I am using James characters as depicted in the above figure. You may want to use another characters of your liking. Be sure to check their supported animation though! For example, James supported animation can be found here.
PS: Just found out this open source replacement for MS Agent also support Windows 7 (not sure about Windows 8 though). You may try it, although currently I am using that MS Agent Hot-Fix from Microsoft.
The next question is, "This MS Agent things is a COM object, right? But how do I use it in my Python application?". Thanks to great work by Mark Hammond, we got this awesome PyWin32 package that let us use native Windows API and COM services from within Python environment. Download an install an appropriate distribution for your Python environment from this Sourceforge page.
Let's test our newly installed James agents, and see if we truly bring it to life:
1 2 3 4 5 6
import win32com.client ag=win32com.client.Dispatch('Agent.Control') ag.Connected=1 ag.Characters.Load('James') ag.Characters('James').Show() ag.Characters('James').Speak('Hi there Pythonist. I see that you brought me back to this world. Thank you!')
A warning though: don't try to save the above code into a
*.py file and run it with either
python command line or your favorit e IDE. Why? It's because the main thread that start your application will exit immediately and the character won't have time to show itself. Paste the above code into an interactive
python REPL session or even
ipython if you like, and you'll see a character pop up and speak using a digital speech synthesizer. Pretty cool for a thrown away product, right?
Make it always run: stick it in a System Tray application
This is another example of how awesome PyWin32 package is: create a system tray icon for Python application. As the above code need a main thread that will keep the character showing, we are going to create a Python application that put an icon in the system tray. For the System Tray functionality, I am going to use Simon Brunning's SysTrayIcon class, which is a rip off from Mark Hammond's win32gui_taskbar.py and win32gui_menu.py demos from PyWin32. In this article, I use it without changes. So, credit goes to those guys..
Below is a new Python class that wrapped a single MS Agent characters of your liking. Observe that the code is pretty much straightforward to understand.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
__author__ = 'Eko Wibowo' import systray import win32com.client import datetime import threading class MsAgent(object): ''' Construct an MS Agent object, display it and let it says the time exactly every hour ''' def __init__(self): self.agent=win32com.client.Dispatch('Agent.Control') self.charId = 'James' self.agent.Connected=1 self.agent.Characters.Load(self.charId) def say_the_time(self, sysTrayIcon): ''' Speak up the time! ''' now = datetime.datetime.now() str_now = '%s:%s:%s' % (now.hour, now.minute, now.second) self.agent.Characters(self.charId).Show() self.agent.Characters(self.charId).Speak('The time is %s' % str_now) self.agent.Characters(self.charId).Hide() def bye(self, sysTrayIcon): ''' Unload msagent object from memory ''' self.agent.Characters.Unload(self.charId) self.thread.cancel() if __name__ == '__main__': import itertools, glob icons = itertools.cycle(glob.glob('*.ico')) hover_text = "What can I do for you Sir?" agent = MsAgent() menu_options = (('Say the time', icons.next(), agent.say_the_time),) systray.SysTrayIcon(icons.next(), hover_text, menu_options, on_quit=agent.bye, default_menu_index=1)
Put an *.ico file in the same folder as the application (of course, together with systray.py module). Run it, and you will a see a new icon in the Windows System tray. Right click it, and choose "Say the time". James will obediently follow your command :)
How to Run This Application?
I saved the main application in a file named
oclockreminder.pyw. By using this extension, if this file was double clicked, it will be executed by
pythonw.exe, making a non-console application (it's similar to
javaw.exe). You can later create a shortcut for this file in Windows Start Menu, and having it run automatically. Actually, the best way would be to prepare an *.exe installer for this application. We are going to explore this option later on this blog.
Realizing that Python can integrate well in a particular Operating System, bring a window to a whole new level of possibilities. The topic discuss in this article still only touch the surface of what you can do with Python. But it I hope it gives enough foundation to get you started.
Or, follow its Github Repository: pythonthusiast/oclockreminder.
Stay tuned for my next article!