向python回调对象传递额外参数(win32com.client.dispatchWithEvents)
我正在使用win32com这个包来和一个Windows应用程序进行交互(具体的应用程序不重要)。
简单来说,我想要实现的是对一个会更新的表格进行订阅。
我已经成功地实现了一个回调函数,它可以在表格更新时接收返回的数据,但现在我需要对收到的数据进行处理。
如果我能在创建回调对象的时候传入额外的参数,这个问题就会变得很简单(见下面的代码),但我不知道该怎么做。
回调类:
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 API com object
self.app = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
# I would like to give the callback object extra arguments e.g. callBackEvents(params)
编辑 创建回调对象:
# 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>)
现在,当订阅信息到达回调对象时,我不知道是哪个com对象设置了这个订阅(我可以根据返回的信息进行猜测,但如果设置了相同的订阅,这会导致问题)。
2 个回答
1
我之前也遇到过同样的问题,最后我对DispatchWithEvents进行了改进。下面是我的解决方案(我觉得这样更优雅):
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)
你的处理类需要有一个__init__
函数,并且要准备好按顺序接收参数:
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)
然后你可以这样初始化它:
clsGED = classifier.PersonClassifier()
items = win32com.client.DispatchEx("Outlook.Application").GetNamespace("MAPI").Folders[<emailaddress>].Folders["Inbox"].Items
utilities.DispatchWithEvents(items, Handler_Class, [cls])
你可能已经猜到了,这里应用的场景是一个数据科学项目,目的是自动分类收到的电子邮件,但新的DispatchWithEvents方法非常通用,可以接受动态数量的参数。
2
因为你可能只有一个应用实例,所以也就只有一个 DispatchWithEvents
。你可以把参数直接放在这个类里面,变成类的成员:
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)
当然,你也可以把参数做成全局变量,但全局变量最好只在最后无奈的时候用,或者用来存放常量。