2017-09-28 74 views
12

問題

我正在嘗試使用PyInstaller創建一個應用程序供我公司內部使用。該腳本在工作的python環境下工作良好,但在翻譯爲包時丟失了某些內容。PyInstaller,如何包含由pip安裝的外部包中的數據文件?

我知道如何在我的包中包含和引用自己需要的數據文件,但是在導入時遇到包含或引用應該引入的文件時遇到問題。

我正在使用名爲tk-tools的可安裝pip軟件包,其中包含一些用於面板狀顯示器(看起來像LED)的漂亮圖像。問題是,當我創建一個pyinstaller腳本,這些圖像中的一個引用的任何時候,我得到一個錯誤:

DEBUG:aspen_comm.display:COM23 19200 
INFO:aspen_comm.display:adding pump 1 to the pump list: [1] 
DEBUG:aspen_comm.display:updating interrogation list: [1] 
Exception in Tkinter callback 
Traceback (most recent call last): 
    File "tkinter\__init__.py", line 1550, in __call__ 
    File "aspen_comm\display.py", line 206, in add 
    File "aspen_comm\display.py", line 121, in add 
    File "aspen_comm\display.py", line 271, in __init__ 
    File "aspen_comm\display.py", line 311, in __init__ 
    File "lib\site-packages\tk_tools\visual.py", line 277, in __init__ 
    File "lib\site-packages\tk_tools\visual.py", line 289, in to_grey 
    File "lib\site-packages\tk_tools\visual.py", line 284, in _load_new 
    File "tkinter\__init__.py", line 3394, in __init__ 
    File "tkinter\__init__.py", line 3350, in __init__ 
_tkinter.TclError: couldn't open "C:\_code\tools\python\aspen_comm\dist\aspen_comm\tk_tools\img/led-grey.png": no such file or directory 

我看着在最後一行在該目錄中 - 這是我的分佈位於 - 並發現目前沒有tk_tools目錄。

問題

如何讓pyinstaller收集導入的包的數據文件?

SPEC文件

目前,我datas是空白。規格文件,用pyinstaller -n aspen_comm aspen_comm/__main__.py創建:

# -*- mode: python -*- 

block_cipher = None 


a = Analysis(['aspen_comm\\__main__.py'], 
      pathex=['C:\\_code\\tools\\python\\aspen_comm'], 
      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='aspen_comm', 
      debug=False, 
      strip=False, 
      upx=True, 
      console=True) 

coll = COLLECT(exe, 
       a.binaries, 
       a.zipfiles, 
       a.datas, 
       strip=False, 
       upx=True, 
       name='aspen_comm') 

當我看着內/build/aspen_comm/out00-Analysis.toc/build/aspen_comm/out00-PYZ.toc,我發現,看起來像是找到了tk_tools包的條目。此外,還有tk_tools包的功能在找到數據文件之前完美工作,所以我知道它正在導入某處,我只是不知道在哪裏。當我搜索tk_tools時,在文件結構中找不到它。

我也嘗試了--hidden-imports選項,結果相同。

部分解決

如果我「手動」在Analysis使用datas = [('C:\\_virtualenv\\aspen\\Lib\\site-packages\\tk_tools\\img\\', 'tk_tools\\img\\')]datas=datas,那麼所有作品添加路徑規範文件的預期。這將起作用,但我寧願PyInstaller查找包數據,因爲它明顯安裝。我會繼續尋找解決方案,但是 - 目前 - 我可能會使用這種非理想的解決方法。

如果你有包的控制權...

然後你就可以在分裝使用stringify,但這只是工作,如果它是你自己的包。

+0

如果我理解正確,除了像這樣的解決方案之外你還需要什麼https://stackoverflow.com/a/31966932/4724196是'tk_tools'依賴靜態文件的自動發現方法,是否正確? – HassenPy

+0

@HassenPy是的,但我懷疑自動發現已經是PyInstaller包的一部分,我只是沒有找到它的正確語法。鏈接的解決方案仍然具有將靜態文件硬編碼到'.spec'文件中的絕對路徑。我的印象是,安裝pip的軟件包應該毫不費力地插入PyInstaller,但顯然並非如此。感謝您的關注! – slightlynybbled

回答

1

編輯補充

更長久地解決這個問題,我創建了一個點子 - 安裝的軟件包叫做stringify這將需要一個文件或目錄,並把它轉換成一個Python字符串,以便包如pyinstaller所認識作爲本地python文件。

查看project page,歡迎反饋!


原來的答案

答案是有點迂迴,並處理與tk_tools包裝,而不是pyinstaller的方式。

最近,有人讓我瞭解到了其中的二進制數據的技術 - 如圖像數據 - 可以存儲爲一個字符串base64

with open(img_path, 'rb') as f: 
    encoded_string = base64.encode(f.read()) 

編碼字符串實際存儲的數據。如果原始包簡單地將包文件存儲爲字符串而不是圖像文件,並創建一個包含可作爲字符串變量訪問的數據的python文件,那麼可以簡單地將二進制數據以pyinstaller將找到的形式包含在包中並沒有干預地檢測。

考慮以下功能:

def create_image_string(img_path): 
    """ 
    creates the base64 encoded string from the image path 
    and returns the (filename, data) as a tuple 
    """ 

    with open(img_path, 'rb') as f: 
     encoded_string = base64.b64encode(f.read()) 

    file_name = os.path.basename(img_path).split('.')[0] 
    file_name = file_name.replace('-', '_') 

    return file_name, encoded_string 


def archive_image_files(): 
    """ 
    Reads all files in 'images' directory and saves them as 
    encoded strings accessible as python variables. The image 
    at images/my_image.png can now be found in tk_tools/images.py 
    with a variable name of my_image 
    """ 

    destination_path = "tk_tools" 
    py_file = '' 

    for root, dirs, files in os.walk("images"): 
     for name in files: 
      img_path = os.path.join(root, name) 
      file_name, file_string = create_image_string(img_path) 

      py_file += '{} = {}\n'.format(file_name, file_string) 

    py_file += '\n' 

    with open(os.path.join(destination_path, 'images.py'), 'w') as f: 
     f.write(py_file) 

如果archive_image_files()放在安裝文件中,則<package_name>/images.py會自動創建隨時安裝腳本運行(輪創建和安裝過程中)。

我可能會在不久的將來改進這項技術。謝謝大家的幫助,

Ĵ

1

將下面的代碼把所有的PNG文件目錄到文件夾中名爲imgs捆綁的應用程序的頂層:

​​

您可以os.path.join("imgs", "your_image.png")在你的代碼中引用它們。

+0

存在路徑'C:\ _code \ tools \ python \ aspen_comm \ dist \ aspen_comm',但其中沒有名爲'tk_tools'的目錄,所以此解決方案目前不可行。感謝您看看! – slightlynybbled

+0

我一定誤會了。你的圖像在哪裏呢? – Steampunkery

+0

當'tk_tools'與pip一起安裝時,圖像被安裝,因此它們位於python虛擬環境中。我希望自動發現功能會發現文件位於'tk_tools'內,然後將它們拉入。 – slightlynybbled

0

我解決了這個採取的事實,即規範文件是被執行Python代碼。您可以在PyInstaller構建階段動態獲取程序包的根目錄,並在datas列表中使用該值。在我來說,我在我的.spec文件是這樣的:

import os 
import importlib 

package_imports = [['package_name', ['file0', 'file1']] 

datas = [] 
for package, files in package_imports: 
    proot = os.path.dirname(importlib.import_module(package).__file__) 
    datas.extend((os.path.join(proot, f), package) for f in files) 

,並使用所產生的datas列表作爲參數Analysis