2017-09-28 11 views
12

Vấn đề

Tôi đang cố sử dụng PyInstaller để tạo ứng dụng để sử dụng nội bộ trong công ty của tôi. Kịch bản hoạt động rất tốt từ môi trường python đang hoạt động, nhưng mất một thứ gì đó khi được dịch sang một gói.PyInstaller, cách bao gồm các tệp dữ liệu từ gói bên ngoài được cài đặt bằng pip?

Tôi biết cách bao gồm và tham chiếu các tệp dữ liệu mà bản thân tôi cần trong gói của mình, nhưng tôi gặp sự cố bao gồm hoặc tham chiếu các tệp cần nhập khi được nhập.

Tôi đang sử dụng gói có thể cài đặt pip được gọi là tk-tools, bao gồm một số hình ảnh đẹp cho màn hình giống bảng điều khiển (trông giống như đèn LED). Vấn đề là khi tôi tạo ra một kịch bản PyInstaller, bất cứ lúc nào mà một trong những hình ảnh được tham chiếu, tôi nhận được một lỗi:

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 

Tôi nhìn vào trong thư mục đó ở dòng cuối cùng - đó là nơi phân phối của tôi nằm - và thấy rằng không có thư mục tk_tools.

Câu hỏi

Làm thế nào để tôi lấy trình cài đặt python để thu thập các tệp dữ liệu của các gói đã nhập?

Tệp cụ thể

Hiện tại, datas của tôi trống. file spec, tạo ra với 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') 

Khi tôi nhìn vào bên trong /build/aspen_comm/out00-Analysis.toc/build/aspen_comm/out00-PYZ.toc, tôi tìm thấy một mục có vẻ như nó tìm thấy gói tk_tools. Ngoài ra, có các tính năng của gói tk_tools hoạt động hoàn hảo trước khi đến điểm tìm tệp dữ liệu, vì vậy tôi biết rằng nó đang được nhập ở đâu đó, tôi chỉ không biết ở đâu. Khi tôi tìm kiếm tk_tools, tôi không thể tìm thấy tham chiếu đến nó trong cấu trúc tệp.

Tôi cũng đã thử tùy chọn --hidden-imports với cùng kết quả.

Giải pháp phần

Nếu tôi 'bằng tay' thêm đường dẫn đến file spec sử dụng datas = [('C:\\_virtualenv\\aspen\\Lib\\site-packages\\tk_tools\\img\\', 'tk_tools\\img\\')]datas=datas trong Analysis, sau đó tất cả các công trình như mong đợi. Điều này sẽ làm việc, nhưng tôi muốn PyInstaller tìm thấy dữ liệu gói vì nó được cài đặt rõ ràng. Tôi sẽ tiếp tục tìm kiếm một giải pháp, nhưng - cho thời điểm này - tôi có lẽ sẽ sử dụng cách giải quyết không lý tưởng này.

Nếu bạn có quyền kiểm soát của gói ...

Sau đó, bạn có thể sử dụng stringify trên gói con, nhưng chỉ có các công trình này nếu nó là gói của riêng bạn.

+0

nếu tôi hiểu chính xác, những gì bạn muốn ngoài giải pháp như https://stackoverflow.com/a/31966932/4724196 này là phương pháp khám phá tự động cho các tệp tĩnh phụ thuộc 'tk_tools', có đúng không? – HassenPy

+0

@HassenPy Có, nhưng tôi nghi ngờ rằng khám phá tự động đã là một phần của gói PyInstaller, tôi chưa tìm thấy cú pháp chính xác cho nó. Giải pháp mà bạn liên kết vẫn có đường dẫn tuyệt đối cho các tệp tĩnh được mã hóa cứng vào tệp '.spec'. Tôi đã ấn tượng rằng các gói 'pip installed' nên được kéo vào PyInstaller với ít nỗ lực, nhưng điều đó dường như không phải như vậy. Cám ơn sự chú ý của các bạn! – slightlynybbled

Trả lời

1

Edited để Thêm

Để giải quyết vấn đề này vĩnh viễn hơn, tôi đã tạo ra một gói pip có thể cài đặt gọi stringify mà sẽ mất một tập tin hoặc thư mục và chuyển đổi nó thành một chuỗi python để gói như PyInstaller sẽ nhận ra chúng như tệp python gốc.

Kiểm tra project page, phản hồi được hoan nghênh!


gốc trả lời

Câu trả lời là một vòng xoay bit và giao dịch với cách mà tk_tools được đóng gói chứ không phải PyInstaller.

Có người thời gian gần đây khiến tôi nhận thức của một kỹ thuật trong đó dữ liệu nhị phân - chẳng hạn như dữ liệu hình ảnh - có thể được lưu trữ như là một chuỗi base64:

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

Chuỗi mã hóa thực sự lưu trữ các dữ liệu. Nếu gói ban đầu chỉ lưu trữ các tệp gói dưới dạng chuỗi thay vì tệp hình ảnh và tạo tệp python với dữ liệu có thể truy cập dưới dạng biến chuỗi, thì có thể chỉ cần bao gồm dữ liệu nhị phân trong gói theo dạng mà pyinstaller sẽ tìm thấy và phát hiện mà không cần can thiệp.

Hãy xem xét các chức năng dưới đây:

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) 

Nếu archive_image_files() được đặt bên trong file cài đặt, sau đó các <package_name>/images.py được tự động tạo ra bất cứ lúc nào kịch bản thiết lập được điều hành (trong quá trình tạo bánh xe và lắp đặt).

Tôi có thể cải thiện kỹ thuật này trong tương lai gần. Cảm ơn tất cả vì sự giúp đỡ của bạn,

j

1

Đoạn mã dưới đây sẽ đưa tất cả các file PNG trong thư mục của bạn vào một thư mục ở cấp cao nhất của ứng dụng kèm gọi imgs:

datas=[("C:\\_code\\tools\\python\\aspen_comm\\dist\\aspen_comm\\tk_tools\\img\\*.png", "imgs")], 

Sau đó bạn có thể tham khảo chúng với os.path.join("imgs", "your_image.png") trong mã của bạn.

+0

Đường dẫn 'C: \ _ code \ tools \ python \ aspen_comm \ dist \ aspen_comm' tồn tại, nhưng không có thư mục nào bên trong nó được gọi là' tk_tools', vì vậy giải pháp này không khả thi tại thời điểm này. Cảm ơn bạn đã xem! – slightlynybbled

+0

Tôi phải hiểu lầm. Vậy hình ảnh của bạn đâu rồi? – Steampunkery

+0

Các hình ảnh được cài đặt khi 'tk_tools' được cài đặt với pip, do đó chúng được đặt trong môi trường ảo python.Tôi đã hy vọng rằng tính năng tự động phát hiện sẽ phát hiện ra rằng các tập tin nằm trong 'tk_tools', sau đó kéo chúng vào. – slightlynybbled

0

Tôi giải quyết điều này bằng cách tận dụng thực tế là tệp spec là mã Python được thực thi. Bạn có thể lấy gốc của gói đó trong giai đoạn xây dựng PyInstaller và sử dụng giá trị đó trong danh sách datas. Trong trường hợp của tôi, tôi có một cái gì đó như thế này trong tập tin .spec tôi:

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) 

Và sử dụng các kết quả danh sách datas như một tham số để Analysis.

Các vấn đề liên quan