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 "win32com\client\__init__.pyc", line 252, in DispatchWithEvents
File "win32com\client\gencache.pyc", line 520, in EnsureModule
File "win32com\client\gencache.pyc", line 287, in MakeModuleForTypelib
File "win32com\client\makepy.pyc", line 259, in GenerateFromTypeLibSpec
File "win32com\client\gencache.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
我知道我可以手动在我的setup.py
文件中添加类型库,但我希望能在不同版本的iTunes的电脑上运行这段代码,而不需要重新编译,所以我更希望能动态创建它。如果在setup/spec中没有办法做到这一点,或许还有其他方法可以加载事件?谢谢。
补充:
感谢Ryan的帮助,我发现我可以拿到生成的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 个回答
(编辑:从上面的链接复制的示例)
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 )
我遇到了完全一样的错误。 这个链接让我找到了正确的方向 --> http://www.py2exe.org/index.cgi/UsingEnsureDispatch 不过它提到: 注意,你必须确保python...\win32com.client.gen_py这个文件夹不存在,这样才能在%temp%目录下创建缓存。 这让我有点困惑。 最后解决我问题的方法是把"C:\Python26\Lib\site-packages\win32com\gen_py"这个文件夹改名为"C:\Python26\Lib\site-packages\win32com\gen_pybak"(在运行py2exe的时候)。
与其依赖缓存,我建议你直接去本地缓存目录,把生成的文件复制到你的项目文件夹里,并把它命名为类似 ITunesInterface.py 这样的名字,然后明确地调用这个文件。这样做会让 py2exe 把它包含到你编译的应用程序里。