PyGobject(一百一十一)使用Pyinstaller打包成APP和EXE
来源:互联网 发布:淘宝上的游侠9账号 编辑:程序博客网 时间:2024/05/17 22:09
- Mac Pyinstaller安装与使用
- 安装
- 使用
- 修改pyinstaller hooks
- 添加gir路径
- 添加iconthemegtksourceview-30
- 再次打包
- 添加图标和版本信息支持Retina屏
- Windows Pyinstaller安装与使用
- 安装Python34
- 安装pygboject
- 安装pyinstaller
- 修改hooks
- 添加gir路径
- 开始打包
- 添加图标
- 添加版本信息
- 命令使用
- Theming部分demo运行异常
- 打包64位应用程序
- 隐藏源代码
在上一节PyGobject(一百一十)代码整合及GtkSource安装使用中,对代码进行了整合,在自己电脑上或者别人安装好同样环境的电脑上才可以运行,如何将代码打包成App或者exe等可独立运行的程序,在没有装过Python和PyGobject上的电脑上运行,就显得比较重要,本文就是基于这个原因而诞生的。
首先我试过py2app,这个打包一般的应用比较好用,可惜的是目前还不支持PyGobject的打包,遂放弃了。不知py2exe能不能行。
接着尝试使用Pyinstaller,官网上介绍说支持PyGobject。接下来详细介绍~
Mac Pyinstaller安装与使用
安装
安装比较简单
pip3 install pyinstaller
使用
cd /Applications/Project/Python/project/PYGUI/pygtk3pyinstaller --onedir -y -w gtk-demo.py
当然,没有那么简单,打包过程中会有很多错误,gtk-demo.app打不开,打开dist/gtk-demo目录下的gtk-demo,报错
/Applications/Project/Python/project/PYGUI/pygtk3/dist/gtk-demo/gi/module.py:178: Warning: cannot register existing type ‘gchar’
**
GLib-GObject:ERROR:gvaluetypes.c:455:_g_value_types_init: assertion failed: (type == G_TYPE_CHAR)
Abort trap: 6
我们还需要做一些工作,见下
修改pyinstaller hooks
现在直接使用Pyinstaller打包我的代码还有点问题,因为我有用到GtkSource,但是它没有添加到Pyinstaller的hooks中
- 添加hooks/hook-gi.repository.GtkSource.py文件
/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/PyInstaller/hooks/hook-gi.repository.GtkSource.py
# -----------------------------------------------------------------------------# Copyright (c) 2005-2016, PyInstaller Development Team.## Distributed under the terms of the GNU General Public License with exception# for distributing bootloader.## The full license is in the file COPYING.txt, distributed with this software.# -----------------------------------------------------------------------------"""Import hook for PyGObject https://wiki.gnome.org/PyGObject"""from PyInstaller.utils.hooks import get_gi_typelibs, collect_glib_share_filesbinaries, datas, hiddenimports = get_gi_typelibs('GtkSource', '3.0')datas += collect_glib_share_files('gtksourceview-3.0')
- 添加hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py 文件
/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py
#-----------------------------------------------------------------------------# Copyright (c) 2005-2016, PyInstaller Development Team.## Distributed under the terms of the GNU General Public License with exception# for distributing bootloader.## The full license is in the file COPYING.txt, distributed with this software.#-----------------------------------------------------------------------------def pre_safe_import_module(api): # PyGObject modules loaded through the gi repository are marked as # MissingModules by modulegraph so we convert them to # RuntimeModules so their hooks are loaded and run. api.add_runtime_module(api.module_name)
- 修改hooks/hook-gi.repository.cairo.py
添加下面一句
hiddenimports += ["cairo"]
- 修改hooks/hook-gi.repository.Gtk.py
添加datas += collect_glib_share_files(‘mime’)
datas += collect_glib_share_files('fontconfig')datas += collect_glib_share_files('icons')datas += collect_glib_share_files('themes')datas += collect_glib_translations('gtk30')datas += collect_glib_share_files('mime')
添加gir路径
打包的时候有报错
Unable to find gir directory: /share/gir-1.0.
解决办法:
将$USER/gtk/inst/share/gir-1.0 复制到/share 目录下
添加icon,theme,gtksourceview-3.0
Pyinstaller打包貌似对软连接的支持不太好,将
/usr/share/icon;/usr/share/themes;/usr/share/gtksourceview-3.0
中的软连接全部删掉,将$USER/gtk/inst/share目录下的相关内容拷贝到/usr/share对应的文件下
再次打包
pyinstaller --onedir -y -w gtk-demo.py
生成的gtk-demo.app仍然打不开,打开dist/gtk-demo目录下的gtk-demo,报错
GLib.Error: g-file-error-quark:
打开文件“/Applications/Project/Python/project/PYGUI/pygtk3/dist/gtk-demo/demos/Data/demo.gresource”失败:open() 失败:No such file or directory
看起来是文件没有打包进应用程序
运行上面的命令会生成一个gtk-demo.spec文件,
开始它是这样的
# -*- mode: python -*-block_cipher = Nonea = Analysis(['gtk-demo.py'], pathex=['/Applications/Project/Python/project/PYGUI/pygtk3'], binaries=None, datas=None, hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher)pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)exe = EXE(pyz, a.scripts, exclude_binaries=True, name='gtk-demo', debug=False, strip=False, upx=True, console=False )coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='gtk-demo')app = BUNDLE(coll, name='gtk-demo.app', icon=None, bundle_identifier=None)
现在我们需要对它做一点修改
# -*- mode: python -*-block_cipher = NoneAPP_NAME='gtk-demo'added_files = [( 'demos', '' ),]binaries_files=[]a = Analysis(['gtk-demo.py'], pathex=['/Applications/Project/Python/project/PYGUI/pygtk3'], binaries=binaries_files, datas=added_files, hiddenimports=["pygtkcompat"], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher)pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)exe = EXE(pyz, a.scripts, exclude_binaries=True, name='gtk-demo', debug=False, strip=False, upx=True, console=False )coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='gtk-demo')app = BUNDLE(coll, name='gtk-demo.app', icon=None, bundle_identifier=None)
主要是讲demos文件夹打包进应用程序
gtk-demo.py也要做一点改变
if getattr(sys, 'frozen', False): # we are running in a bundle DEMOROOTDIR = sys._MEIPASSelse: # we are running in a normal Python environment DEMOROOTDIR = os.path.abspath(os.path.dirname(__file__))
这个时候就需要使用这个配置文件来打包应用程序了,
注意:打包命令不一样了
pyinstaller --onedir -y gtk-demo.spec
打包完成后,打开gtk-demo.app
结果是这样的,如下图,很模糊
解决办法,spec文件中添加
‘NSHighResolutionCapable’: ‘True’,具体见下
添加图标和版本信息,支持Retina屏
图标后缀为icns,可以到这里下载
修改gtk-demo.spec文件
app中内容改成如下
app = BUNDLE(coll, name='gtk-demo.app', icon='gtk-demo.icns', bundle_identifier=None, info_plist={ 'CFBundleName': APP_NAME, 'CFBundleDisplayName': APP_NAME, 'CFBundleGetInfoString': "Making gtk-demo", 'CFBundleIdentifier': "tk.xiaosanyu.gtk-demo", 'CFBundleVersion': "0.1.0", 'CFBundleShortVersionString': "0.1.0", 'NSHumanReadableCopyright': "Copyright © 2016, Xiaosanyu, All Rights Reserved", 'NSHighResolutionCapable': 'True', })
再次运行上面的命令,打开新生成的app
熟悉的画面出现了,泪奔~~~
Windows Pyinstaller安装与使用
安装Python3.4
windows下的pygobject目前最高只支持Python3.4,所以先安装个Python3.4 32位的,下载地址https://www.python.org/ftp/python/3.4.4/python-3.4.4.msi
安装完毕后,
安装pygboject
下载地址https://sourceforge.net/projects/pygobjectwin32/
下载得到文件pygi-aio-3.18.2_rev8-setup.exe
运行程序
- 选择Python3.4安装路径
- 勾选要安装的库
我这个demo,我另外勾选了以下四个
如果你的项目需要其它的库,如Gstreamer,可自行安装
- 下一步,勾选gir,如果需要glade,可勾选
点击完成,完成安装。
安装pyinstaller
pip3 install pyinstaller
同MAC上一样,需要对pyinstaller做一些修改
修改hooks
- 添加hooks/hook-gi.repository.GtkSource.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\hook-gi.repository.GtkSource.py
from PyInstaller.utils.hooks import get_gi_typelibs, collect_glib_share_filesbinaries, datas, hiddenimports = get_gi_typelibs('GtkSource', '3.0')datas += collect_glib_share_files('gtksourceview-3.0')
- 添加hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py 文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\
pre_safe_import_module\hook-gi.repository.GtkSource.py
def pre_safe_import_module(api): # PyGObject modules loaded through the gi repository are marked as # MissingModules by modulegraph so we convert them to # RuntimeModules so their hooks are loaded and run. api.add_runtime_module(api.module_name)
- 添加hooks/hook-gi.repository.PangoFT2.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\hook-gi.repository.PangoFT2.py
from PyInstaller.utils.hooks import get_gi_typelibs, collect_glib_share_filesbinaries, datas, hiddenimports = get_gi_typelibs('PangoFT2', '1.0')
添加hooks/pre_safe_import_module/hook-gi.repository.PangoFT2.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\
pre_safe_import_module\hook-gi.repository.PangoFT2.py
内容同
hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py添加hooks/hook-gi.repository.fontconfig.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\hook-gi.repository.fontconfig.py
from PyInstaller.utils.hooks import get_gi_typelibs, collect_glib_share_filesbinaries, datas, hiddenimports = get_gi_typelibs('fontconfig', '2.0')
添加hooks/pre_safe_import_module/hook-gi.repository.fontconfig.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\
pre_safe_import_module\hook-gi.repository.fontconfig.py
内容同
hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py添加hooks/hook-gi.repository.freetype2.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\hook-gi.repository.freetype2.py
from PyInstaller.utils.hooks import get_gi_typelibs, collect_glib_share_filesbinaries, datas, hiddenimports = get_gi_typelibs('freetype2', '2.0')
添加hooks/pre_safe_import_module/hook-gi.repository.freetype2.py文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\hooks\
pre_safe_import_module\hook-gi.repository.freetype2.py
内容同
hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py修改hooks/hook-gi.repository.cairo.py
添加下面一句
hiddenimports += ["cairo"]
- 修改hooks/hook-gi.repository.GdkPixbuf.py
由于windows下没有gdk-pixbuf-query-loaders命令,导致GdkPixbuf hook失败,在这个文件最后添加
from PyInstaller.utils.hooks import get_gi_typelibsbinaries, datas, hiddenimports = get_gi_typelibs('GdkPixbuf', '2.0')datas += collect_glib_translations('gdk-pixbuf')
添加gir路径
将C:\Python34_32bit\Lib\site-packages\gnome\share\gir-1.0目录复制到C:\Python34_32bit\share\gir-1.0
开始打包
把在MAC环境下的gtk-demo.spec文件拷贝过来,改一下
pathex=[‘c:\pygtk3’],
# -*- mode: python -*-block_cipher = NoneAPP_NAME='gtk-demo'added_files = [( 'demos', '' ),]binaries_files=[]a = Analysis(['gtk-demo.py'], pathex=['c:\\pygtk3'], binaries=binaries_files, datas=added_files, hiddenimports=["pygtkcompat"], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher)pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)exe = EXE(pyz, a.scripts, exclude_binaries=True, name='gtk-demo', debug=False, strip=False, upx=True, console=False )coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='gtk-demo')app = BUNDLE(coll, name='gtk-demo.app', icon='gtk-demo.icns', bundle_identifier=None, info_plist={ 'CFBundleName': APP_NAME, 'CFBundleDisplayName': APP_NAME, 'CFBundleGetInfoString': "Making gtk-demo", 'CFBundleIdentifier': "tk.xiaosanyu.gtk-demo", 'CFBundleVersion': "0.1.0", 'CFBundleShortVersionString': "0.1.0", 'NSHumanReadableCopyright': "Copyright © 2016, Xiaosanyu, All Rights Reserved", 'NSHighResolutionCapable': 'True', })
开始运行
cd c:\pygtk3C:\Python34_32bit\Scripts\pyinstaller.exe -y --onedir gtk-demo.spec
打开报错
解决办法,添加图标后就好了
添加图标
图标后缀为ico,可以到这里下载
修改spec. EXE配置项中添加icon图标名
exe = EXE(pyz, a.scripts, exclude_binaries=True, name='gtk-demo', debug=False, strip=False, upx=True, console=False, icon="gtk-demo.ico" )
运行后打开dist/gtk-demo/gtk-demo.exe
添加版本信息
Windows下的pygobject及打包到目前为止都比Mac上简单N倍,特别是pygobject的的安装,简直简单到没朋友。但是接下来的这个问题就为难到我了,那就是给exe文件添加版本信息。
参阅pyinstaller官方文档,发下有两个命令用来获取和添加版本信息,那就是
- 获取exe文件版本信息
C:\Python34_32bit\Scripts\pyi-grab_version.exe
- 设置exe文件版本
C:\Python34_32bit\Scripts\pyi-set_version.exe
然而这两个命令不支持Python3.x,博主(a87b01c14)经过一番努力,将这两个命令做了一些修改,现在分享给大家
主要是修改了
C:\Python34_32bit\Lib\site-packages\PyInstaller\utils\win32\versioninfo.py这个文件
# -*- coding: utf-8 -*-#-----------------------------------------------------------------------------# Copyright (c) 2013-2016, PyInstaller Development Team.## Distributed under the terms of the GNU General Public License with exception# for distributing bootloader.## The full license is in the file COPYING.txt, distributed with this software.#-----------------------------------------------------------------------------import codecsimport structimport pywintypesimport win32apiimport pefileimport sys# TODO implement read/write version information with pefile library.# PE version info doc: http://msdn.microsoft.com/en-us/library/ms646981.aspxdef pefile_read_version(filename): """ Return structure like: { # Translation independent information. # VS_FIXEDFILEINFO - Contains version information about a file. This information is language and code page independent. u'FileVersion': (1, 2, 3, 4), u'ProductVersion': (9, 10, 11, 12), # PE files might contain several translations of version information. # VS_VERSIONINFO - Depicts the organization of data in a file-version resource. It is the root structure that contains all other file-version information structures. u'translations': { 'lang_id1' : { u'Comments': u'日本語, Unicode 対応.', u'CompanyName': u'your company.', u'FileDescription': u'your file desc.', u'FileVersion': u'1, 2, 3, 4', u'InternalName': u'your internal name.', u'LegalCopyright': u'your legal copyright.', u'LegalTrademarks': u'your legal trademarks.', u'OriginalFilename': u'your original filename.', u'PrivateBuild': u'5, 6, 7, 8', u'ProductName': u'your product name', u'ProductVersion': u'9, 10, 11, 12', u'SpecialBuild': u'13, 14, 15, 16', }, 'lang_id2' : { ... } } } Version info can contain multiple languages. """ # TODO vers = { 'FileVersion': (0, 0, 0, 0), 'ProductVersion': (0, 0, 0, 0), 'translations': { 'lang_id1': { 'Comments': '', 'CompanyName': '', 'FileDescription': '', 'FileVersion': '', 'InternalName': '', 'LegalCopyright': '', 'LegalTrademarks': '', 'OriginalFilename': '', 'PrivateBuild': '', 'ProductName': '', 'ProductVersion': '', 'SpecialBuild': '', } } } pe = pefile.PE(filename) #ffi = pe.VS_FIXEDFILEINFO #vers['FileVersion'] = (ffi.FileVersionMS >> 16, ffi.FileVersionMS & 0xFFFF, ffi.FileVersionLS >> 16, ffi.FileVersionLS & 0xFFFF) #vers['ProductVersion'] = (ffi.ProductVersionMS >> 16, ffi.ProductVersionMS & 0xFFFF, ffi.ProductVersionLS >> 16, ffi.ProductVersionLS & 0xFFFF) #print(pe.VS_FIXEDFILEINFO.FileVersionMS) # TODO Only first available language is used for now. #vers = pe.FileInfo[0].StringTable[0].entries from pprint import pprint pprint(pe.VS_FIXEDFILEINFO) print(dir(pe.VS_FIXEDFILEINFO)) print(repr(pe.VS_FIXEDFILEINFO)) print(pe.dump_info()) return vers# Ensures no code from the executable is executed.LOAD_LIBRARY_AS_DATAFILE = 2STRINGTYPE = type(u'')def getRaw(o): return o.encode('UTF-16LE')def decode(pathnm): h = win32api.LoadLibraryEx(pathnm, 0, LOAD_LIBRARY_AS_DATAFILE) if not nm : print(pathnm+" don't have the version information") win32api.FreeLibrary(h) return else: nm=nm[0] data = win32api.LoadResource(h, pefile.RESOURCE_TYPE['RT_VERSION'], nm) vs = VSVersionInfo() j = vs.fromRaw(data) win32api.FreeLibrary(h) return vsclass VSVersionInfo: """ WORD wLength; // length of the VS_VERSION_INFO structure WORD wValueLength; // length of the Value member WORD wType; // 1 means text, 0 means binary WCHAR szKey[]; // Contains the Unicode string "VS_VERSION_INFO". WORD Padding1[]; VS_FIXEDFILEINFO Value; WORD Padding2[]; WORD Children[]; // zero or more StringFileInfo or VarFileInfo // structures (or both) that are children of the // current version structure. """ def __init__(self, ffi=None, kids=None): self.ffi = ffi self.kids = kids or [] def fromRaw(self, data): i, (sublen, vallen, wType, nm) = parseCommon(data) #vallen is length of the ffi, typ is 0, nm is 'VS_VERSION_INFO'. i = int((i + 3) / 4) * 4 # Now a VS_FIXEDFILEINFO self.ffi = FixedFileInfo() j = self.ffi.fromRaw(data, i) i = j while i < sublen: j = i i, (csublen, cvallen, ctyp, nm) = parseCommon(data, i) if nm.strip() == 'StringFileInfo': sfi = StringFileInfo() k = sfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen) self.kids.append(sfi) i = k else: vfi = VarFileInfo() k = vfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen) self.kids.append(vfi) i = k i = j + csublen i = int((i + 3) / 4) * 4 return i def toRaw(self): nm = 'VS_VERSION_INFO' rawffi = self.ffi.toRaw() vallen = len(rawffi) typ = 0 sublen = 6 + 2*len(nm) + 2 pad = b'' if sublen % 4: pad = b'\000\000' sublen = sublen + len(pad) + vallen pad2 = b'' if sublen % 4: pad2 = b'\000\000' tmp=b'' for kid in self.kids: tmp+=kid.toRaw() sublen = sublen + len(pad2) + len(tmp) result=(struct.pack('hhh', sublen, vallen, typ) + getRaw(nm) + b'\000\000' + pad + rawffi + pad2 + tmp) return result def __str__(self, indent=u''): indent = indent + u' ' tmp = [kid.__str__(indent+u' ') for kid in self.kids] tmp = u', \n'.join(tmp) return (u"""# UTF-8## For more details about fixed file info 'ffi' see:# http://msdn.microsoft.com/en-us/library/ms646997.aspxVSVersionInfo(%sffi=%s,%skids=[%s%s])""" % (indent, self.ffi.__str__(indent), indent, tmp, indent))def parseCommon(data, start=0): i = start + 6 (wLength, wValueLength, wType) = struct.unpack('3h', data[start:i]) i, text = parseUString(data, i, i+wLength) return i, (wLength, wValueLength, wType, text)def parseUString(data, start, limit): i = start while i < limit: if data[i:i+2] == b'\000\000': break i += 2 if sys.version_info < (3, 0): text = unicode(data[start:i], 'UTF-16LE') else: text = data[start:i].decode("UTF-16LE", "ignore") i += 2 return i, textclass FixedFileInfo: """ DWORD dwSignature; //Contains the value 0xFEEFO4BD DWORD dwStrucVersion; // binary version number of this structure. // The high-order word of this member contains // the major version number, and the low-order // word contains the minor version number. DWORD dwFileVersionMS; // most significant 32 bits of the file's binary // version number DWORD dwFileVersionLS; // DWORD dwProductVersionMS; // most significant 32 bits of the binary version // number of the product with which this file was // distributed DWORD dwProductVersionLS; // DWORD dwFileFlagsMask; // bitmask that specifies the valid bits in // dwFileFlags. A bit is valid only if it was // defined when the file was created. DWORD dwFileFlags; // VS_FF_DEBUG, VS_FF_PATCHED etc. DWORD dwFileOS; // VOS_NT, VOS_WINDOWS32 etc. DWORD dwFileType; // VFT_APP etc. DWORD dwFileSubtype; // 0 unless VFT_DRV or VFT_FONT or VFT_VXD DWORD dwFileDateMS; DWORD dwFileDateLS; """ def __init__(self, filevers=(0, 0, 0, 0), prodvers=(0, 0, 0, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, subtype=0x0, date=(0, 0)): self.sig = 0xfeef04bd self.strucVersion = 0x10000 self.fileVersionMS = (filevers[0] << 16) | (filevers[1] & 0xffff) self.fileVersionLS = (filevers[2] << 16) | (filevers[3] & 0xffff) self.productVersionMS = (prodvers[0] << 16) | (prodvers[1] & 0xffff) self.productVersionLS = (prodvers[2] << 16) | (prodvers[3] & 0xffff) self.fileFlagsMask = mask self.fileFlags = flags self.fileOS = OS self.fileType = fileType self.fileSubtype = subtype self.fileDateMS = date[0] self.fileDateLS = date[1] def fromRaw(self, data, i): (self.sig, self.strucVersion, self.fileVersionMS, self.fileVersionLS, self.productVersionMS, self.productVersionLS, self.fileFlagsMask, self.fileFlags, self.fileOS, self.fileType, self.fileSubtype, self.fileDateMS, self.fileDateLS) = struct.unpack('13l', data[i:i+52]) return i+52 def toRaw(self): return struct.pack('L12l', self.sig, self.strucVersion, self.fileVersionMS, self.fileVersionLS, self.productVersionMS, self.productVersionLS, self.fileFlagsMask, self.fileFlags, self.fileOS, self.fileType, self.fileSubtype, self.fileDateMS, self.fileDateLS) def __str__(self, indent=u''): fv = (self.fileVersionMS >> 16, self.fileVersionMS & 0xffff, self.fileVersionLS >> 16, self.fileVersionLS & 0xFFFF) pv = (self.productVersionMS >> 16, self.productVersionMS & 0xffff, self.productVersionLS >> 16, self.productVersionLS & 0xFFFF) fd = (self.fileDateMS, self.fileDateLS) tmp = [u'FixedFileInfo(', u'# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)', u'# Set not needed items to zero 0.', u'filevers=%s,' % str(fv), u'prodvers=%s,' % str(pv), u"# Contains a bitmask that specifies the valid bits 'flags'r", u'mask=%s,' % hex(self.fileFlagsMask), u'# Contains a bitmask that specifies the Boolean attributes of the file.', u'flags=%s,' % hex(self.fileFlags), u'# The operating system for which this file was designed.', u'# 0x4 - NT and there is no need to change it.', u'OS=%s,' % hex(self.fileOS), u'# The general type of file.', u'# 0x1 - the file is an application.', u'fileType=%s,' % hex(self.fileType), u'# The function of the file.', u'# 0x0 - the function is not defined for this fileType', u'subtype=%s,' % hex(self.fileSubtype), u'# Creation date and time stamp.', u'date=%s' % str(fd), u')' ] return (u'\n'+indent+u' ').join(tmp)class StringFileInfo(object): """ WORD wLength; // length of the version resource WORD wValueLength; // length of the Value member in the current // VS_VERSION_INFO structure WORD wType; // 1 means text, 0 means binary WCHAR szKey[]; // Contains the Unicode string 'StringFileInfo'. WORD Padding[]; StringTable Children[]; // list of zero or more String structures """ def __init__(self, kids=None): self.name = u'StringFileInfo' self.kids = kids or [] def fromRaw(self, sublen, vallen, name, data, i, limit): self.name = name while i < limit: st = StringTable() j = st.fromRaw(data, i, limit) self.kids.append(st) i = j return i def toRaw(self): vallen = 0 typ = 1 sublen = 6 + 2*len(self.name) + 2 pad = b'' if sublen % 4: pad = b'\000\000' tmp=b'' for kid in self.kids: tmp+=kid.toRaw() sublen = sublen + len(pad) + len(tmp) if tmp[-2:] == '\000\000': sublen = sublen - 2 result=(struct.pack('hhh', sublen, vallen, typ) + getRaw(self.name) + b'\000\000' + pad + tmp) return result def __str__(self, indent=u''): newindent = indent + u' ' tmp = [kid.__str__(newindent) for kid in self.kids] tmp = u', \n'.join(tmp) return (u'%sStringFileInfo(\n%s[\n%s\n%s])' % (indent, newindent, tmp, newindent))class StringTable: """ WORD wLength; WORD wValueLength; WORD wType; WCHAR szKey[]; String Children[]; // list of zero or more String structures. """ def __init__(self, name=None, kids=None): self.name = name or u'' self.kids = kids or [] def fromRaw(self, data, i, limit): i, (cpsublen, cpwValueLength, cpwType, self.name) = parseCodePage(data, i, limit) # should be code page junk #i = ((i + 3) / 4) * 4 while i < limit: ss = StringStruct() j = ss.fromRaw(data, i, limit) i = j self.kids.append(ss) i = int((i + 3) / 4) * 4 return i def toRaw(self): vallen = 0 typ = 1 sublen = 6 + 2*len(self.name) + 2 tmp = b'' for kid in self.kids: raw = kid.toRaw() if len(raw) % 4: raw = raw + b'\000\000' tmp+=raw sublen += len(tmp) if tmp[-2:] == '\000\000': sublen -= 2 result= (struct.pack('hhh', sublen, vallen, typ) + getRaw(self.name) + b'\000\000' + tmp) return result def __str__(self, indent=u''): newindent = indent + u' ' tmp = map(str, self.kids) tmp = (u',\n%s' % newindent).join(tmp) return (u"%sStringTable(\n%su'%s',\n%s[%s])" % (indent, newindent, self.name, newindent, tmp))class StringStruct: """ WORD wLength; WORD wValueLength; WORD wType; WCHAR szKey[]; WORD Padding[]; String Value[]; """ def __init__(self, name=None, val=None): self.name = name or u'' self.val = val or u'' def fromRaw(self, data, i, limit): i, (sublen, vallen, typ, self.name) = parseCommon(data, i) limit = i + sublen i = int((i + 3) / 4) * 4 i, self.val = parseUString(data, i, limit) return i def toRaw(self): if type(self.name) is STRINGTYPE: # Convert unicode object to byte string. raw_name = self.name.encode('UTF-16LE') if type(self.val) is STRINGTYPE: # Convert unicode object to byte string. raw_val = self.val.encode('UTF-16LE') # TODO document the size of vallen and sublen. vallen = len(raw_val) + 2 typ = 1 sublen = 6 + len(raw_name) + 2 pad = b'' if sublen % 4: pad = b'\000\000' sublen = sublen + len(pad) + vallen abcd = (struct.pack('hhh', sublen, vallen, typ) + raw_name + b'\000\000' + pad + raw_val + b'\000\000') return abcd def __str__(self, indent=''): return u"StringStruct(u'%s', u'%s')" % (self.name, self.val) def parseCodePage(data, i, limit): i, (sublen, wValueLength, wType, nm) = parseCommon(data, i) return i, (sublen, wValueLength, wType, nm)class VarFileInfo: """ WORD wLength; // length of the version resource WORD wValueLength; // length of the Value member in the current // VS_VERSION_INFO structure WORD wType; // 1 means text, 0 means binary WCHAR szKey[]; // Contains the Unicode string 'VarFileInfo'. WORD Padding[]; Var Children[]; // list of zero or more Var structures """ def __init__(self, kids=None): self.kids = kids or [] def fromRaw(self, sublen, vallen, name, data, i, limit): self.sublen = sublen self.vallen = vallen self.name = name i = int((i + 3) / 4) * 4 while i < limit: vs = VarStruct() j = vs.fromRaw(data, i, limit) self.kids.append(vs) i = j return i def toRaw(self): self.vallen = 0 self.wType = 1 self.name = 'VarFileInfo' sublen = 6 + 2*len(self.name) + 2 pad = b'' if sublen % 4: pad = b'\000\000' tmp=b'' for kid in self.kids: tmp+=kid.toRaw() self.sublen = sublen + len(pad) + len(tmp) result= (struct.pack('hhh', self.sublen, self.vallen, self.wType) + getRaw(self.name) + b'\000\000' + pad + tmp) return result def __str__(self, indent=''): tmp = map(str, self.kids) return "%sVarFileInfo([%s])" % (indent, ', '.join(tmp))class VarStruct: """ WORD wLength; // length of the version resource WORD wValueLength; // length of the Value member in the current // VS_VERSION_INFO structure WORD wType; // 1 means text, 0 means binary WCHAR szKey[]; // Contains the Unicode string 'Translation' // or a user-defined key string value WORD Padding[]; // WORD Value[]; // list of one or more values that are language // and code-page identifiers """ def __init__(self, name=None, kids=None): self.name = name or u'' self.kids = kids or [] def fromRaw(self, data, i, limit): i, (self.sublen, self.wValueLength, self.wType, self.name) = parseCommon(data, i) i = int((i + 3) / 4) * 4 for j in range(int(self.wValueLength/2)): kid = struct.unpack('h', data[i:i+2])[0] self.kids.append(kid) i += 2 return i def toRaw(self): self.wValueLength = len(self.kids) * 2 self.wType = 0 sublen = 6 + 2*len(self.name) + 2 pad = b'' if sublen % 4: pad =b'\000\000' self.sublen = sublen + len(pad) + self.wValueLength tmp=b'' for kid in self.kids: tmp+=struct.pack('h', kid) return (struct.pack('hhh', self.sublen, self.wValueLength, self.wType) + getRaw(self.name) + b'\000\000' + pad + tmp) def __str__(self, indent=u''): return u"VarStruct(u'%s', %r)" % (self.name, self.kids)def SetVersion(exenm, versionfile): if isinstance(versionfile, VSVersionInfo): vs = versionfile else: fp = codecs.open(versionfile, 'rU', 'utf-8') txt=fp.read() fp.close() vs = eval(txt) data=vs.toRaw() hdst = win32api.BeginUpdateResource(exenm, 0) win32api.UpdateResource(hdst, pefile.RESOURCE_TYPE['RT_VERSION'], 1, data) win32api.EndUpdateResource (hdst, 0)
还要修改一个文件
C:\Python34_32bit\Lib\site-packages\PyInstaller\utils\cliutil
s\grab_version.py
try: vs = PyInstaller.utils.win32.versioninfo.decode(args.exe_file) if vs is None: return fp = codecs.open(args.out_filename, 'w', 'utf-8') fp.write(str(vs)) fp.close() print(('Version info written to: %s' % args.out_filename)) except KeyboardInterrupt: raise SystemExit("Aborted by user request.")
命令使用
pyi-grab_version 后面参数为一个带有版本信息的exe程序,这里我选用java.exe来做测试,
C:\Python34_32bit\Scripts\pyi-grab_version.exe "C:\Program Files\Java\jre6\bin\java.exe" version.txt
version.txt用来存储获取到的信息,不添加此参数,则默认写到file_version_info.txt文件中
version.txt文件中获取到的内容为
# UTF-8## For more details about fixed file info 'ffi' see:# http://msdn.microsoft.com/en-us/library/ms646997.aspxVSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. filevers=(6, 0, 200, 2), prodvers=(6, 0, 200, 2), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. flags=0x0, # The operating system for which this file was designed. # 0x4 - NT and there is no need to change it. OS=0x4, # The general type of file. # 0x1 - the file is an application. fileType=0x1, # The function of the file. # 0x0 - the function is not defined for this fileType subtype=0x0, # Creation date and time stamp. date=(0, 0) ), kids=[ StringFileInfo( [ StringTable( u'000004b0', [StringStruct(u'CompanyName', u'Sun Microsystems, Inc.'), StringStruct(u'FileDescription', u'Java(TM) Platform SE binary'), StringStruct(u'FileVersion', u'6.0.200.2'), StringStruct(u'Full Version', u'1.6.0_20-b02'), StringStruct(u'InternalName', u'java'), StringStruct(u'LegalCopyright', u'Copyright © 2004'), StringStruct(u'OriginalFilename', u'java.exe'), StringStruct(u'ProductName', u'Java(TM) Platform SE 6 U20'), StringStruct(u'ProductVersion', u'6.0.200.2')]) ]), VarFileInfo([VarStruct(u'Translation', [0, 1200])]) ])
然后我们可以修改一下里面的内容
我修改后的内容保存在file_version_info.txt文件中,文件要是utf8格式的,内容如下
# UTF-8## For more details about fixed file info 'ffi' see:# http://msdn.microsoft.com/en-us/library/ms646997.aspxVSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. filevers=(1, 0, 0, 0), prodvers=(1, 0, 0, 0), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. flags=0x0, # The operating system for which this file was designed. # 0x4 - NT and there is no need to change it. OS=0x40004, # The general type of file. # 0x1 - the file is an application. fileType=0x1, # The function of the file. # 0x0 - the function is not defined for this fileType subtype=0x0, # Creation date and time stamp. date=(0, 0) ), kids=[ StringFileInfo( [ StringTable( '040904E4', [StringStruct('CompanyName', 'xiaosanyu'), StringStruct('FileDescription', 'Gtk demo'), StringStruct('FileVersion', '1.0.0.0'), StringStruct('LegalCopyright', 'Copyright © 2016 GNOME Software, Inc.'), StringStruct('LegalTrademarks', 'gtk-demo ® is a registered trademark of GNOME Software, Inc.'), StringStruct('OriginalFilename', 'gtk-demo.exe'), StringStruct('ProductName', 'Gtk-demo'), StringStruct('ProductVersion', '1.0'), StringStruct('Comments', 'gtk-demo 1'), StringStruct('Subversion Revision', '0'), StringStruct('CompileDate', 'Monday, August 15, 2016 9:44 AM')]) ]), VarFileInfo([VarStruct('Translation', [1033, 1252])]) ])
现在使用pyi-set_version.exe命令将file_version_info.txt中版本信息写入到get-demo.exe中
C:\Python34_32bit\Scripts\pyi-set_version.exe file_version_info.txt dist\gtk-demo\gtk-demo.exe
查看结果
看来版本信息是写入了,可是悲剧的事情发生了,打开应用程序,报错
*
Fata Error! can’t open self C:\pygtk3\dist\gtk-demo\gtk-demo.exe or archive C:\pygtk3\dist\gtk-demo\gtk-demo.pkg*
只能另寻它法了
尝试过 打包的时候添加
--version-file file_version_info.txt
没有效果
最后在
C:\Users\xiaosanyu>C:\Python34_32bit\Lib\site-packages\PyInstaller\building\api.py文件中找到了答案
在spec配置文件的EXE项中添加version参数
exe = EXE(pyz, a.scripts, exclude_binaries=True, name='gtk-demo', debug=False, strip=False, upx=True, console=False, icon="gtk-demo.ico", version="file_version_info.txt" )
再次运行
C:\Python34_32bit\Scripts\pyinstaller.exe -y --onedir gtk-demo.spec
打包成功,并且也有版本信息。
Theming部分demo运行异常
CSS文件中,“button”改成”.button”号;“window”前面加“.”号没有效果,只能将“window”改成“#window”,
然后在py文件中 添加window.set_name("window")
最后使用glib-compile-resources.exe重新编译资源文件
打包64位应用程序
要下载Python3.4 64位,然后安装pyinstaller,然后安装pygobject,其它都同32位的打包
隐藏源代码
看pyinstaller介绍是要使用Cython,暂时还没有研究,后面空了研究透了,再放上来
代码下载地址:http://download.csdn.net/detail/a87b01c14/9594728
- PyGobject(一百一十一)使用Pyinstaller打包成APP和EXE
- 使用Pyinstaller打包exe
- pyinstaller打包python成exe
- 将Python文件打包成exe的两种方法(pyinstaller和py2exe)之pyinstaller篇
- 使用Pyinstaller将python脚本打包成exe文件
- 使用pyinstaller打包python为exe文件
- 使用pyinstaller打包python为exe文件
- 使用pyinstaller打包python为exe文件
- Python 使用pyinstaller打包exe程序
- PyGobject(一百一十)代码整合及GtkSource安装使用
- pyinstaller--将python打包成exe
- Python之用pyinstaller打包成exe
- python-利用pyinstaller打包成exe
- pyinstaller打包exe错误
- pyinstaller打包exe文件
- Pyinstaller打包exe
- pyinstaller打包exe---打包实践
- 将Python文件打包成exe的两种方法(pyinstaller和py2exe)之py2exe篇
- POJ3187 Backward Digit Sums
- SuperMap iObject常见问题解答集锦(四)
- 单例模式
- java命名规范
- 排列组合专题
- PyGobject(一百一十一)使用Pyinstaller打包成APP和EXE
- Java MD5加密算法
- error LNK2001: unresolved external symbol "public: __thiscall Annotation::Annotation(void)" (??0Anno
- 遇见,控件----Datepicker&TimePicker
- Java多线程小抄集(三)
- 多种DOM事件处理程序分析及跨浏览器的事件解决方案
- 计算时间差的方法
- mt7620a上带机量的提高(一)
- try-catch