问题
I am struggling with callback objects, I am a newbie please be nice:
I am using the win32com package to interact with a windows application (The application is not important).
In short what I am trying to achieve is a subscription to a table that updates.
I have successfully implemented a callback that receives the returned data on an update to the table but what I need now is to act on the data received.
This problem would be very easy to solve if I could instantiate the callback object with additional arguments (see code below) But I am at a loss as to how to do this.
CallBack Class:
class callBackEvents(object):
""" Callback Object for win32com
"""
def OnNewData(self, XMLData):
logging.info("Subscription returned information")
print "HERE : {}".format(XMLData))
# Would like to use some argument to access logic
# For how to use the new data
def OnActionResult(self, job, msg):
return True
def OnServerDisconnect(self):
logging.debug("Server Disconnected")
def OnServerConnect(self):
logging.debug("Trader Connected To Server")
Instantiate the Callback Object:
# Instantiate API com object
self.app = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
# I would like to give the callback object extra arguments e.g. callBackEvents(params)
EDIT
Instantiate the Callback Objects:
# Instatiate two com objects
self.com1 = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
self.com2 = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
# Create multiple subscriptions (Note these are asynchronous)
# Pushing the subscribed info is not a problem and done elsewhere
self.com1.Subscribe(<subscription info>)
self.com2.Subscribe(<subscription info>)
Now when subscription info hits the callback object I have no idea which com object set up the subscription (I could guess based on the information returned but this is going to cause problems when identical subscriptions are setup)
回答1:
Since you likely have only one app instance and therefore one DispatchWithEvents
, you could simply make the params a member of the class:
class callBackEvents(object):
""" Callback Object for win32com
"""
params = None
def OnNewData(...
...
# populate the params field
callBackEvents.params = yourParams
self.app = win32com.client.DispatchWithEvents("WindowsApplication", callBackEvents)
You could of course make params a global but you should use globals only as a last resort or for constants.
回答2:
I had the same issue and ended up augmenting DispatchWithEvents. Please see below my solution (which I think is more elegant):
from win32com.client import Dispatch
from win32com.client import gencache
from win32com.client import getevents
from win32com.client import EventsProxy
import pythoncom
def _event_setattr_(self, attr, val):
try:
# Does the COM object have an attribute of this name?
self.__class__.__bases__[0].__setattr__(self, attr, val)
except AttributeError:
# Otherwise just stash it away in the instance.
self.__dict__[attr] = val
def DispatchWithEvents(clsid, user_event_class, arguments):
# Create/Get the object.
disp = Dispatch(clsid)
if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
# Get the class from the module.
disp_class = gencache.GetClassForProgID(str(disp_clsid))
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
else:
disp_class = disp.__class__
# If the clsid was an object, get the clsid
clsid = disp_class.CLSID
# Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
# XXX - we are still "classic style" classes in py2x, so we need can't yet
# use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
events_class = getevents(clsid)
if events_class is None:
raise ValueError("This COM object does not support events.")
result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
events_class.__init__(instance, instance)
args = [instance] + arguments
if hasattr(user_event_class, "__init__"):
user_event_class.__init__(*args)
return EventsProxy(instance)
Your handler class will have to have an init function and be ready to accept the arguments in order:
class Handler_Class():
def __init__(self, cls):
self.cls = cls
def OnItemAdd(self, mail):
#Check if the item is of the MailItem type
if mail.Class==43:
print("##########",inbox, "##########")
print(mail.Subject, " - ", mail.Parent.FolderPath)
label = cls.label_email(datetime.now(),mail)
print("=======>",label)
And you would initialize it as such:
clsGED = classifier.PersonClassifier()
items = win32com.client.DispatchEx("Outlook.Application").GetNamespace("MAPI").Folders[<emailaddress>].Folders["Inbox"].Items
utilities.DispatchWithEvents(items, Handler_Class, [cls])
As you might have guessed, the application here is for a data science project where incoming emails are automatically classified, but the new DispatchWithEvents method is very generic and accepts a dynamic number of arguments.
来源:https://stackoverflow.com/questions/23341675/passing-additional-arguments-to-python-callback-object-win32com-client-dispatch