Передача дополнительных аргументов в объект обратного вызова 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>)

Теперь, когда информация о подписке попадает в объект обратного вызова, я понятия не имею, какой ком-объект установил подписку (я мог бы догадаться на основе возвращаемой информации, но это вызовет проблемы, когда настроены идентичные подписки)


person JosephHughes    schedule 28.04.2014    source источник


Ответы (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)

Конечно, вы можете сделать параметры глобальными, но использовать глобальные значения следует только в крайнем случае или для констант.

person Oliver    schedule 28.04.2014
comment
Спасибо за ваш ответ @Schollii, извините, я должен был быть более описательным (мой первый вопрос SO): я создаю два (или более) экземпляра DispatchWithEvents, у меня есть класс, который создает экземпляр и обрабатывает входные данные к оконному приложению. Где я борюсь, как создать экземпляр объекта CallBack с информацией о том, какой экземпляр приложения создал его? -- Огромное спасибо - person JosephHughes; 30.04.2014
comment
@JosephHughes Вы не можете. Расширьте свой вопрос, чтобы показать, что вы на самом деле пытаетесь сделать (два экземпляра DispatchWithEvents), и я уверен, что мы сможем найти альтернативу. - person Oliver; 02.05.2014
comment
Мне сообщили, что несколько подписок вызовут странное поведение в приложении, которое я тестирую. Поэтому я отметил ваш ответ как принятый. Я также расширим свой вопрос, чтобы попытаться предоставить больше информации о том, чего я пытался достичь, на случай, если кто-то в будущем будет бороться с тем же. - person JosephHughes; 09.05.2014

У меня была такая же проблема, и в итоге я добавил 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 является очень универсальным и принимает динамическое количество аргументов.

person Vlad    schedule 14.12.2016