py2exe/pyinstaller и DispatchWithEvents
У меня есть программа, которая использует библиотеку win32com для управления iTunes, но возникли некоторые проблемы с ее компиляцией в исполняемый файл. Проблема, похоже, вращается вокруг использования DispatchWithEvents
вместо Dispatch
. Я создал очень простую программу, чтобы проиллюстрировать свою проблему:
import win32com.client
win32com.client.gencache.is_readonly = False #From py2exe wiki
class ITunesEvents(object):
def __init__(self): self.comEnabled = True
def OnCOMCallsDisabledEvent(self, reason): self.comEnabled = False
def OnCOMCallsEnabledEvent(self): self.comEnabled = True
# The first line works in the exe, the second doesn't.
itunes = win32com.client.Dispatch("iTunes.Application")
#itunes = win32com.client.DispatchWithEvents("iTunes.Application", ITunesEvents)
lib = getattr(itunes, "LibraryPlaylist")
src = getattr(lib, "Source")
playlists = getattr(src, "Playlists")
print "Found %i playlists." % getattr(playlists, "Count")
Используя Dispatch
, программа компилируется и выполняется корректно. Используя DispatchWithEvents
, программа работает нормально при вызове из командной строки, но выдает следующую ошибку при запуске exe:
Traceback (most recent call last):
File "sandbox.py", line 16, in <module>
itunes = win32com.client.DispatchWithEvents("iTunes.Application", ITunesEvents)
File "win32comclient__init__.pyc", line 252, in DispatchWithEvents
File "win32comclientgencache.pyc", line 520, in EnsureModule
File "win32comclientgencache.pyc", line 287, in MakeModuleForTypelib
File "win32comclientmakepy.pyc", line 259, in GenerateFromTypeLibSpec
File "win32comclientgencache.pyc", line 141, in GetGeneratePath
IOError: [Errno 2] No such file or directory: '[distDir]\library.zip\win32com\gen_py\__init__.py'
Я также пробовал использовать PyInstaller, который выдает аналогичную ошибку:
File "<string>", line 16, in <module>
File "[outDir]/win32com.client", line 252, in DispatchWithEvents
File "[outDir]/win32com.client.gencache", line 520, in EnsureModule
File "[outDir]/win32com.client.gencache", line 287, in MakeModuleForTypelib
File "[outDir]/win32com.client.makepy", line 286, in GenerateFromTypeLibSpec
File "[outDir]/win32com.client.gencache", line 550, in AddModuleToCache
File "[outDir]/win32com.client.gencache", line 629, in _GetModule
File "[pyinstallerDir]iu.py", line 455, in importHook
raise ImportError, "No module named %s" % fqname
ImportError: No module named win32com.gen_py.9E93C96F-CF0D-43F6-8BA8-B807A3370712x0x1x13
Я знаю, что могу вручную добавить typelib в мой файл setup.py
, но я хотел бы запустить код на компьютерах с различными версиями iTunes без перекомпиляции, поэтому я предпочел бы динамически создавать его. Если нет способа сделать это с помощью setup / spec, может быть, есть другой способ загрузить события? Спасибо.
Добавление:
Благодаря Райану, я обнаружил, что могу взять сгенерировал файл py и, немного покопавшись, смог придумать следующее.
Возьмите сгенерированный файл py (из makepy.py
) и переименуйте его в cominterface.py
. Затем вам нужно будет сделать следующее, чтобы фактически создать COM-объект с обработчиком событий.
import cominterface
from types import ClassType
from win32com.client import EventsProxy, _event_setattr_
class ItunesEvents:
'''iTunes events class. See cominterface for details.'''
def OnPlayerPlayEvent(self, t):print "Playing..."
def OnPlayerStopEvent(self, t): print "Stopping..."
itunes = cominterface.iTunesApp()
rClass = ClassType("COMEventClass", (itunes.__class__, itunes.default_source, ItunesEvents), {'__setattr__': _event_setattr_})
instance = rClass(itunes._oleobj_)
itunes.default_source.__init__(instance, instance)
#ItunesEvents.__init__(instance) #Uncomment this line if your events class has __init__.
itunes = EventsProxy(instance)
Тогда вы можете заняться своими делами.3 ответа:
Вместо того, чтобы зависеть от кэша, я бы рекомендовал перейти в каталог локального кэша, скопировать созданный файл в файл локального проекта и назвать его как-то так: ITunesInterface.py, и призывая к этому явно. Это заставит py2exe тянуть его в ваше скомпилированное приложение.
Я испытывал точно такую же ошибку. Это звено направило меня в нужное русло ...> http://www.py2exe.org/index.cgi/UsingEnsureDispatch однако в нем упоминается, что : NB вы должны убедиться, что питон...\win32com.клиент.gen_py dir не существует разрешить создание кэша в %temp% Что немного сбивало с толку. Что решило ее для меня, так это переименование ". C:\Python26\Lib\site-packages\win32com\gen_py-чтобы ...C:\Python26\Lib\site-packages\win32com\gen_pybak" (при запуске py2exe)
Это официальный способ сделать это.
(правка: скопирован дословный пример из этой ссылки выше)
import win32com.client if win32com.client.gencache.is_readonly == True: #allow gencache to create the cached wrapper objects win32com.client.gencache.is_readonly = False # under p2exe the call in gencache to __init__() does not happen # so we use Rebuild() to force the creation of the gen_py folder win32com.client.gencache.Rebuild() # NB You must ensure that the python...\win32com.client.gen_py dir does not exist # to allow creation of the cache in %temp% # Use SAPI speech through IDispatch from win32com.client.gencache import EnsureDispatch from win32com.client import constants voice = EnsureDispatch("Sapi.SpVoice", bForDemand=0) voice.Speak( "Hello World.", constants.SVSFlagsAsync )