1. 程式人生 > 其它 >每日一練(41):Excel表列名稱

每日一練(41):Excel表列名稱

 

轉載於 YanHua_jake  於 2018-09-02 18:46:30 釋出:https://blog.csdn.net/weixin_42052836/article/details/82315118


使用Pyinstaller打包Python專案包含了大量的坑,這篇文章總結實踐得到的Pyinstaller打包經驗。本文的例子為Python3.6程式碼,Pyinstaller3.4,在windows下打包為64位和32位版本。

目錄

Pyinstaller基本使用方法

Python專案的打包方法

1.spec檔案生成

2.spec檔案配置

3.使用spec執行打包命令

Visual C++ run-time .dlls包含

Python模組的打包問題

凍結打包路徑

其它問題

Pyinstaller基本使用方法

Python專案的打包方法

1.spec檔案生成

2.spec檔案配置

3.使用spec執行打包命令

Python模組的打包問題

凍結打包路徑

其它問題

Pyinstaller基本使用方法
Pyinstaller可以通過簡單的命令進行python程式碼的打包工作,其基本的命令為:

pyinstaller -option xxx.py
options的詳情可參考官方幫助文件https://pyinstaller.readthedocs.io/en/stable/usage.html

這邊只介紹用到的option:-d生成一個檔案目錄包含可執行檔案和相關動態連結庫和資原始檔等;-f僅生成一個可執行檔案

-D, --onedir Create a one-folder bundle containing an executable (default)
-F, --onefile Create a one-file bundled executable.
對於打包結果較大的專案,選用-d生成目錄相比單可執行檔案的打包方式,執行速度更快,但包含更加多的檔案。本文的例子選中-D方式打包。

Python專案的打包方法
以一個多檔案和目錄的Python專案為例,專案檔案包含:1.Python原始碼檔案;2.圖示資原始檔;3.其它資原始檔

以圖中專案為例,Python原始碼檔案在多個目錄下:bin, lib\app, lib\models, lib\views;圖示資原始檔在lib\icon目錄下;其它資原始檔在data目錄下,包括文字檔案,視訊檔案等等。

 

1.spec檔案生成
為了進行自定義配置的打包,首先需要編寫打包的配置檔案.spec檔案。當使用pyinstaller -d xxx.py時候會生成預設的xxx.spec檔案進行預設的打包配置。通過配置spec指令碼,並執行pyinstaller -d xxx.spec完成自定義的打包。

通過生成spec檔案的命令,針對程式碼的主程式檔案生成打包對應的spec檔案

pyi-makespec -w xxx.py
開啟生成的spec檔案,修改其預設指令碼,完成自定義打包需要的配置。spec檔案是一個python指令碼,其預設的結構如下例所示

# -*- mode: python -*-

block_cipher = None


a = Analysis(['fastplot.py'],
pathex=['D:\\install_test\\DAGUI-0.1\\bin'],
binaries=[],
datas=[],
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='fastplot',
debug=False,
strip=False,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='fastplot')
spec檔案中主要包含4個class: Analysis, PYZ, EXE和COLLECT.

Analysis以py檔案為輸入,它會分析py檔案的依賴模組,並生成相應的資訊

PYZ是一個.pyz的壓縮包,包含程式執行需要的所有依賴

EXE根據上面兩項生成

COLLECT生成其他部分的輸出資料夾,COLLECT也可以沒有

2.spec檔案配置
首先給出舉例python專案的spec檔案配置

# -*- mode: python -*-
import sys
import os.path as osp
sys.setrecursionlimit(5000)

block_cipher = None


SETUP_DIR = 'D:\\install_test\\FASTPLOT\\'

a = Analysis(['fastplot.py',
'frozen_dir.py',
'D:\\install_test\\FASTPLOT\\lib\\app\\start.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\analysis_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\datafile_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\data_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\figure_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\time_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\mathematics_model.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\constant.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\custom_dialog.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_dict_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_process_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_sift_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\mathematics_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\para_temp_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\mainwindow.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\paralist_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\plot_window.py'],
pathex=['D:\\install_test\\FASTPLOT'],
binaries=[],
datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')],
hiddenimports=['pandas','pandas._libs','pandas._libs.tslibs.np_datetime','pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.nattype', 'pandas._libs.skiplist','scipy._lib','scipy._lib.messagestream'],
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='fastplot',
debug=False,
strip=False,
upx=True,
console=True)
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='fastplot')
a) py檔案打包配置

針對多目錄多檔案的python專案,打包時候需要將所有相關的py檔案輸入到Analysis類裡。Analysis類中的pathex定義了打包的主目錄,對於在此目錄下的py檔案可以只寫檔名不寫路徑。如上的spec指令碼,將所有專案中的py檔案路徑以列表形式寫入Analysis,這裡為了說明混合使用了絕對路徑和相對路徑。

b) 資原始檔打包配置

資原始檔包括打包的python專案使用的相關檔案,如圖示檔案,文字檔案等。對於此類資原始檔的打包需要設定Analysis的datas,如例子所示datas接收元組:datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')]。元組的組成為(原專案中資原始檔路徑,打包後路徑),例子中的(SETUP_DIR+'lib\\icon','lib\\icon')表示從D:\\install_test\\FASTPLOT\\lib\\icon下的圖示檔案打包後放入打包結果路徑下的lib\\icon目錄。

c)Hidden import配置

pyinstaller在進行打包時,會解析打包的python檔案,自動尋找py原始檔的依賴模組。但是pyinstaller解析模組時可能會遺漏某些模組(not visible to the analysis phase),造成打包後執行程式時出現類似No Module named xxx。這時我們就需要在Analysis下hiddenimports中加入遺漏的模組,如例子中所示。

d)遞迴深度設定

在打包匯入某些模組時,常會出現"RecursionError: maximum recursion depth exceeded"的錯誤,這可能是打包時出現了大量的遞迴超出了python預設的遞迴深度。因此需要在spec檔案上新增遞迴深度的設定,設定一個足夠大的值來保證打包的進行,即

import sys
sys.setrecursionlimit(5000)
e)去除不必要的模組import

有時需要讓pyinstaller不打包某些用不到的模組,可通過在excludes=[]中新增此模組實現,如

excludes=['zmq']
3.使用spec執行打包命令
pyinstaller -D xxx.spec
打包生成兩個檔案目錄build和dist,build為臨時檔案目錄完成打包後可以刪除;dist中存放打包的結果,可執行檔案和其它程式執行的關聯檔案都在這個目錄下。

 

Visual C++ run-time .dlls包含
針對在Windows<10釋出使用,且Python>=3.5的情況,Pyinstaller打包的程式可能會出現不包含Visual C++ run-time .dlls的情況,Python>=3.5需要使用Visual Studio 2015 run-time,也就是Universal CRT,這些runtime在Win10本身或Win7到Win8.1版本的更新包裡,但程式打包後使用的系統裡並不一定安裝了。因此需要參考Universal CRT的建議,應用以下的方法解決這個問題:

Build on Windows 7 which has been reported to work.

Include one of the VCRedist packages (the redistributable package files) into your application’s installer. This is Microsoft’s recommended way, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, numbers 2 and 3.

Install the Windows Software Development Kit (SDK) for Windows 10 and expand the .spec-file to include the required DLLs, see “Distributing Software that uses the Universal CRT“ in the above-mentioned link, number 6.

 

Python模組的打包問題
程式呼叫的很多包,在打包時候可能會出現一些問題,針對這寫問題需要做一些處理才能保證打包的程式正常執行。

1.PyQt plugins缺失

使用PyQt編寫UI互動介面的python程式碼在進行打包時可能會出現一些特別的問題。

執行使用了PyQt的打包程式,常會出現這樣的錯誤,提示缺少Qt platfrom plugin “windows”,如下圖

 

打包後程序執行後,使用png格式的圖示可以正常顯示,但使用的ico格式圖示不顯示(對於所有圖示和關聯檔案都無法使用的情況涉及到路徑問題,後文會另外解釋)。

這兩個錯誤產生的問題都是因為打包時沒有將PyQt相關的動態連結庫目錄生成到打包目錄下,因此可以通過將這些需要的檔案目錄拷貝到打包生成目錄下,解決plugin缺失問題。以使用PyQt5編寫的python軟體打包為例,完成打包後的結果目錄下包含PyQt5資料夾,將PyQt5\Qt\plugins下的所有內容(如下圖)拷貝到打包結果目錄。這樣就可以解決PyQt plugins缺失的問題。

 

2.動態連結庫缺失問題

更一般的,打包後可能會缺失某些動態連結庫,造成執行程式出錯,如

ImportError: DLL load failed: 找不到指定的模組
在打包過程中一般會有與此相關的warning提示(lib not found)無法找到這些動態連結庫。例如在32位版本的打包中,可能會出現scipy模組相關的dll檔案無法找到。這時就需要在打包的spec檔案中指定動態連結庫路徑,使其關聯到打包後的路徑中。

binaries=[('C:\\Program Files\\Python36-32\\Lib\\site-packages\\scipy\\extra-dll','.')]
Analysis下的binaries是為打包檔案新增二進位制檔案,缺失的動態連結庫可以通過這種方式自動加入到打包路徑中。

3.窗體風格變化問題

在某些情況下,如在精簡環境下的python程式打包中,執行打包後的程式會出現窗體風格變為老式的win風格,這是由於打包時候PyQt的styles動態庫沒有找到。因此只需要在Python 目錄下找到 Lib\site-packages\PyQt5\Qt\plugins\styles,將styles整個目錄複製到打包結果目錄。

4.UnicodeDecodeError

當打包時出現類似錯誤時:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 122
可在打包的命令列中輸入chcp 65001設定命令列顯示utf-8字元,然後再執行打包命令。或者,修改pyinstaller包下的compat.py,根據報錯對應的行將

out = out.decode(encoding)
改為

out = out.decode(encoding, 'replace')
凍結打包路徑
執行打包後的程式,經常會出現程式使用的圖示無法顯示,程式使用的關聯檔案無法關聯。或者,在打包的本機上執行正常,但是將打包後的程式放到其它機器上就有問題。這些現象都很有可能是由程式使用的檔案路徑發生改變產生的,因此在打包時候我們需要根據執行路徑進行路徑“凍結”。

1.使用絕對路徑

在python程式碼中使用絕對路徑呼叫外部檔案可以保證打包時候路徑可追溯,因此在本機上執行打包後程序基本沒問題。但是當本機上對應路徑的資原始檔被改變,或者將打包程式應用到別的機器,都會出現搜尋不到資原始檔的問題。這種方式不是合適的打包釋出python軟體的方式。

2.使用凍結路徑

增加一個py檔案,例如叫frozen_dir.py

# -*- coding: utf-8 -*-
"""
Created on Sat Aug 25 22:41:09 2018
frozen dir
@author: yanhua
"""
import sys
import os

def app_path():
"""Returns the base application path."""
if hasattr(sys, 'frozen'):
# Handles PyInstaller
return os.path.dirname(sys.executable)
return os.path.dirname(__file__)
其中的app_path()函式返回一個程式的執行路徑,為了方便我們將此檔案放在專案檔案的根目錄,通過這種方式建立了相對路徑的關係。

原始碼中使用路徑時,以app_path()的返回值作為基準路徑,其它路徑都是其相對路徑。以本文中使用的python專案打包為例,如下所示

import frozen_dir
SETUP_DIR = frozen_dir.app_path()

FONT_MSYH = matplotlib.font_manager.FontProperties(
fname = SETUP_DIR + r'\data\fonts\msyh.ttf',
size = 8)

DIR_HELP_DOC = SETUP_DIR + r'\data\docs'
DIR_HELP_VIDEO = SETUP_DIR + r'\data\videos'
通過凍結路徑,使用了基準目錄下的data目錄下的fonts, docs, videos。

主程式中也做了類似的調整,改變其設定路徑方法

import frozen_dir

SETUP_DIR = frozen_dir.app_path()+r'\lib'
sys.path.append(SETUP_DIR)
使用這樣的方法進行打包,打包後的可執行程式就可以在其它機器上執行。

其它問題
由於作業系統和執行環境的不同,pyinstaller打包中還可能遇到很多其它問題,最後總結一些我在打包中遇到的其它坑:

1.許可權問題

通常時在打包時出現的某些檔案拒絕訪問或沒有許可權執行某些操作等。解決這個的方法一般有這幾個方面:

a)使用管理員許可權執行cmd或其它命令列視窗

b)關閉防毒軟體

c)使用完全許可權的管理員賬戶

2.中文路徑

pyinstaller打包後的路徑使用中文沒有問題,不過為了減少打包時候出錯的可能,儘量將打包使用的資原始檔和程式碼檔案路徑設定為英文。

3.打包後文件的大小

通常python打包為可執行檔案都會得到一個較大的包,這是無法避免的,但是我們還是可以通過一些方法來儘量精簡打包後的執行程式:

a)在程式碼中減少不必要的import,如from xxx import *

b)在精簡的執行環境(如原生python環境)下打包,缺什麼包就下什麼包,避免不必要的python包被打包入程式。尤其是anaconda這樣的整合環境下打包的結果會大很多。

c)使用UPX
————————————————
版權宣告:本文為CSDN博主「YanHua_jake」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/weixin_42052836/article/details/82315118