Archive for November, 2006

Fear the disembodied floating head

Fear the disembodied floating head

No comments

Python and Com Components

There is some info out there on how to use com and activex components from python, but most of the walk throughs and howtos focus on using com to communicate with processes that reside in their own address space and don’t need to have an event loop. A good clue that you’ve got this sort of control is if you get a catastrophic failure exception when calling a method in the dll the first time.

One control that I’ve had to deal with needs to be instantiated into some sort of container that has an event loop. In the .NET world, that means inheriting from an AxHost and instantiating the control on a windows form. In the python world, that means either wxPython or WinPython. Since I’m integrating into an existing wxPython app, that’s my choice.

The first hurdle is figuring out the CLSID and actual name of the control. In WinPython, the tools menu has a makepy.py command. It will pop up a browse list of the human readable names of the COM objects that are installed on the system. that script will generate glue in the site-packages/win32com/gen-py directory, with the filename being the CLSID and version of the COM object. Open that file, in the top page there will be a CLSID formatted correctly, and near the bottom there will be a line that contains the Name of the control. In this case, it’s RANGER.RangerCtrl.1.

With that information, the minimal wxApp that worked with my control was this:


import win32com.client
from wxPython import wx 
import wx.activex
import wx.py

class evt:
  def OnTransportNewState(self, newState=None, oldState=None):
    print "Changing State, %s to %s" %(oldState, newState)
  # ...
class mainwindow(wx.Frame):
  def __init__(self):
    wx.Frame.__init__(self, None , wx.ID_ANY, "Test")

    # turns out that this isn't necessary   

    ctrl = wx.activex.ActiveXWindow(frame,

    # the dispatch to send messages to the control
    self.d = win32com.client.Dispatch("RANGER.RangerCtrl.1")
    # Event object with call backs to receive events
    self.e = win32com.client.WithEvents(self.d, evt)

    # this lets you poke around
    self.shell = wx.py.shell.ShellFrame()
    # and this shouldn't fail badly, at least for this control
    print self.d.GetVersion()

if __name__== '__main__':
  app = wx.PySimpleApp()

  frame = mainwindow()

The frame doesn’t need to be shown on screen, but it does need to be created to receive events. This control uses call backs to intercept events, so I’m using an eventbuilder dispatch class to catch those. Both the dispatch object and the ActiveX control are necessary, as it doesn’t appear that the activex control actually gets the methods that are supposed to be included. I’m not sure if this is actually common over controls of this sort, or if it’s a peculiarity of this particular control and it’s structure.

The handlers in the event class can be determined by looking in the makepy.py generated class. In this case, there was a list of about 20, of which I’ve intercepted about half.

The final wrinkle in this excercise is packaging using py2exe. Win32com needs to be added as a package in the setup.py, the MSVCP71.dll C++ runtime needs to be included in the base application directory, and wx.activex is really picky about the working directory when it’s imported. Even when that runtime dll is in the same directory as the C runtime, there are import errors unless I’ve chdir’d into that directory. Since this happend on import, it’s something that needs to happen really early in the application.

In the setup.py script for py2exe, I’ve subclassed the build_exe class following the lead of this page. I’m including the following method:

def plat_finalize(self, modules, py_files, extensions, dlls):
  BuildExe.plat_finalize(self, modules, py_files, extensions, dlls)
  # from http://www.py2exe.org/index.cgi/TixSetup
  cpdll = os.path.join(sys.prefix, 'MSVCP71.dll')
  self.dlls_in_exedir.append( 'msvcp71.dll')

And finally, before I import wx.activex,

if hasattr(sys, 'frozen'):
  ### when running from py2exe and we're run from a shortcut, then
  ### when we import the wx.activex, we need to be in the same directory
  ### as the msvcp71.dll
  if os.path.dirname(sys.executable) != os.path.abspath(os.path.curdir):
No comments

More Mindcamp

A couple of final things from Mindcamp —

  • Fidalgo Bay Coffee was there as a sponsor and at times, as a barrista. Christian brought a nice espresso maker along that was making some really nice shots around midnight. The ones in the morning weren’t as good, but that might have been a tired palate or tired brain.
  • There was a lot less in the way of user contributed snacks and caffeine, it was basically the left over soda from dinner and coffee for most of the night.
  • There was another Tom Bihn giveaway, and this time Rose won one. It’s a smaller one that matches the one I won last time. I won a pound of Fidalgo Bay coffee this time, so I’d say that 90% of winning something at mindcamp is showing up. Just wish that there were more ipods to give away.
No comments



Was using him as a guinea pig with the new lighting stuff, but he was moving fast enough to not actually be in the light when I took this.

No comments

The cat likes brussels sprouts

The cat likes brussels sprouts

No comments

Theocacao: Convert an NSImage to CIImage

Theocacao: Convert an NSImage to CIImage

I should see if this is useful in the context of my resizer, since I remember that there are some interesting gymnastics that I needed to go through to make it work.

No comments

The Make Plotter2

The Make Plotter
Using a lensbaby.

No comments

The Make Plotter

The Make Plotter
Usingd a lensbaby

No comments

Mindcamp 3.0

Got back from Werewolfcamp saturday night and slept for about 12 hours on Sunday night. I think i’m close to back to a normal sleep cycle, but I’m not so sure about the caffeine levels yet.

There was a werewolf game that ran from about 10pm till about 9:45am. I was ‘only’ in on it for 4 or 5 hours till I quit to sleep for a few hours. I joined in once in the morning, but my heart and brain weren’t really in it. I did manage to win a couple of times as one of two werewolves, and once as the seer for the village. Generally in the following games, I was a villager and got lynched early. But that happens.

I paid a short visit to the pre-coffee hacks coffee making session. We tried out some Yemen, but it wasn’t as impressive as it was in the coffee shop. The Espresso was great through, Christian was pulling straight shots of Fidalgo Bay coffee, and they were really nice. Smooth and not bitter. An excellent kick for a 11pm refresher.

Got to play with a lensbaby lens. I like, I want. It’s tilt-shift and soft focus, with strange color tweaking as well. It’s not a fidelity thing, it’s just kinda fun. I expect that this look will get old in a couple of years.

I didn’t wind up doing as much show and tell as I was expecting too, I think part of that was not actually getting out any of the toys till after midnight. I should have pulled out the Photobooth/Flickr thing at the signin, where we could have gotten a lot of people captured. i should have done a 5 minute demo or something.

I was up on stage for the Amazon Web Services talk, but I only talked for a minute or two, and spent the rest of the time handing the mic back and forth. Unfortunately, this talk conflicted with another one that I really wanted to go to — real world rails, or what happens after that first 15 minutes. I was disappointed that the UI review session didn’t happen for a lack of projector. I suspect that I could use the help.

No comments

Tonight’s Hack

So in preparation for the upcoming Mindcamp, I wanted to glue together the code I have to upload to flickr and Apple’s folder actions to make PhotoBooth automatically upload pictures to Flickr and put them in a PhotoBooth set.

To keep the applescript as small and troublefree as possible, I took an existing applescript folder action (in this case, copy to jpeg) and hacked it up to call a python shell script where I make a few calls using the FlickrAPI module.

-- the list of file types which will be processed 
-- eg: {"PICT", "JPEG", "TIFF", "GIFf"} 
property type_list : {"JPEG"}
-- since file types are optional in Mac OS X, 
-- check the name extension if there is no file type 
-- NOTE: do not use periods (.) with the items in the name extensions list 
-- eg: {"txt", "text"}, NOT: {".txt", ".text"} 
property extension_list : {"jpg", "jpeg"}

on adding folder items to this_folder after receiving these_items
    repeat with i from 1 to number of items in these_items
      set this_item to item i of these_items
      set the item_info to the info for this_item
      if (alias of the item_info is false and the file type 
          of the item_info is in the type_list) or 
          (the name extension of the item_info is in 
          the extension_list) then
        set theCmd to "/Users/erics/bin/pb_flickr.py " 
          & (quoted form of POSIX path of 
          (this_item as string)) & "&"
        --display dialog (theCmd as string)
        do shell script theCmd
      end if
    end repeat
  on error error_message number error_number
    if the error_number is not -128 then
      tell application "Finder"
        display dialog error_message buttons 
          {"Cancel"} default button 1 giving up after 120
      end tell
    end if
  end try
end adding folder items to

The python code is pretty straight forward, the only real tricks are getting api keys, secrets, and tokens. I’m not sure I remember what I did to get those, since I did it a year ago.


import os
import sys
from flickrapi import FlickrAPI

set_id='72157594368688426' # your set id here...

class flickrprefs:
  api_key = 'your key here'
  api_secret = 'your secret herer'
  auth_token = 'your token here'

if __name__=='__main__':
  argv = sys.argv
  if len(argv) < 2:
  pth = argv[1]
  prefs = flickrprefs()
    fapi = FlickrAPI(prefs.api_key, prefs.api_secret)
    rsp = fapi.auth_checkToken(api_key=prefs.api_key,
    if not rsp:
      #token isn't valid.
    rsp = fapi.upload(filename = pth, 
                      description = '', 
                      api_key= prefs.api_key,
                      auth_token = prefs.auth_token,
    rsp = fapi.photosets_addPhoto(auth_token=prefs.auth_token, 
                                   api_key= prefs.api_key,
  except Exception, msg:
    #print msg
    pass # I don't really care if I'm not connected to the net. 

Finally, it's a simple matter of dropping the applescript in the ~/Library/Scripts/Folder Action Scripts directory and enabling it with the Applescript Folder Actions Setup app.

No comments

« Previous PageNext Page »