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

0 0
原创粉丝点击