views:

243

answers:

2

I need to dynamically create com objects from an activex dll and each of the objects can raise events which should be handled with event handlers.

I can do this easily with win32com.client.Dispatch and win32com.client.WithEvents and associate a "separate" class of event handlers with each of the objects. Like so:

class evt_1:
    def OnEvent(self):
        print "got event from object 1"

class evt_2:
    def OnEvent(self):
        print "got event from object 2"

obj_1 = win32com.client.Dispatch("mycom")
ev_1  = win32com.client.WithEvents(obj_1, evt_1)

obj_2 = win32com.client.Dispatch("mycom")
ev_1  = win32com.client.WithEvents(obj_2, evt_2)

But if I dynamically create the objects, lets say in a list:

listOfObjects = []
for i in range(10):
    obj = win32com.client.Dispatch("mycom")
    listOfObjects.append(obj)
    ev = win32com.client.WithEvents(obj, MyEventHandlerClass)

I want to code the event handlers only once, since I don't know how many objects I would be creating until run time. And I don't know how to get the object that raised the event from inside the event handler.

In VB 6, I've used the activex control using control arrays, and the event handlers simply get an "index" value of the control that raised the event.

Do you think something similar can be done in python ?
I am not sure what python decorators work for, but can they be used to "decorate" the MyEventHandlerClass for each index of the com object?

A: 

Control Arrays were removed in VB.NET so I don't think they would be supported in the win32com. Not sure if this would work for you but can you pass the index to the EventHandler class?

class MyEventHandler:
    def __init__(self, index):
        self.obj_index = index

    def OnEvent(self):
        print "got event from object %d" % self.obj_index

listOfObjects = []
for i in range(10):
    obj = win32com.client.Dispatch("mycom")
    listOfObjects.append(obj)
    ev = win32com.client.WithEvents(obj, MyEventHandlerClass(i))

If the event needs access to all of the controls in the array (not just the index), you can mimic a control array by looping through your listOfObjects in the EventHandler, and determining which object raised the Event... so for example, a RadioButton_CheckChanged() event would look like:

def RadioButton_CheckChanged():
    for radiobutton in listOfRadioButtons:
        if radiobutton.Checked:
            # this is the one that was clicked on
jcoon
The great flaw of win32com for events is that you need to pass in a callable, and not an instance. However, you can pass in a wrapped "factory" function that will return a class instance with the necessary state.
Ryan Ginstrom
@coonj: Unfortunately.. The WithEvents() function requires the name of a class instance, not an object reference so that didn't work. But I think your idea should work if we can pass in a new class having an index value in an `obj_index` variable. Is that possible? I remember reading somewhere that classes are objects in python too so I'm thinking they could be created but I'm not sure what the syntax would be..
sarshad
A: 

It's a major flaw of win32com's event handling that you have to pass in a class object rather than a class instance.

You can attach state to your classes using win32com, however, by creating classes dynamically using new.classobj:

from win32com.client import Dispatch, WithEvents
from new import classobj

class MyEventHandler(object):
    def OnVisible(self, visible):
        print "got event from object %d" % self.obj_index

listOfObjects = []
for i in range(3):
    handler = classobj('Handler_%s' % i,(MyEventHandler,),{})
    handler.obj_index = i
    ie = Dispatch("InternetExplorer.Application")
    listOfObjects.append(ie)
    WithEvents(ie, handler)

listOfObjects[0].Visible = 1
listOfObjects[2].Visible = 1

Output:

got event from object 0
got event from object 2

You might want to look into the comtypes module (see event handling) if you want to do this in a saner way.

Ryan Ginstrom
Yes! this works.. thanks RyanI will also look into the comtypes module.. looks a little different from win32com ..
sarshad