PyInstaller, как включить файлы данных из внешнего пакета, который был установлен pip?
я пытаюсь использовать PyInstaller для создания приложения для внутреннего использования в своей компании. Скрипт прекрасно работает из рабочей среды Python, но теряет что-то при переводе в пакет.
я знаю, как включать и ссылаться на файлы данных, которые мне самому нужны в моем пакете, но у меня возникли проблемы с включением или ссылкой на файлы, которые должны поступать при импорте.
я использую пакет pip-installable называется tk-tools, который включает в себя некоторые хорошие изображения для панельных дисплеев (выглядит как светодиоды). Проблема в том, что когда я создаю скрипт 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_commdisplay.py", line 206, in add
File "aspen_commdisplay.py", line 121, in add
File "aspen_commdisplay.py", line 271, in __init__
File "aspen_commdisplay.py", line 311, in __init__
File "libsite-packagestk_toolsvisual.py", line 277, in __init__
File "libsite-packagestk_toolsvisual.py", line 289, in to_grey
File "libsite-packagestk_toolsvisual.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:_codetoolspythonaspen_commdistaspen_commtk_toolsimg/led-grey.png": no such file or directory
я посмотрел в этом каталоге в последней строке - где находится мой дистрибутив - и обнаружил, что нет tk_tools
представляем каталог.
вопрос
как мне получить pyinstaller для сбора файлы данных, импортированных пакетов?
Spec File
в настоящее время, мой datas
- это пустое. Spec-файл, созданный с pyinstaller -n aspen_comm aspen_comm/__main__.py
:
# -*- mode: python -*-
block_cipher = None
a = Analysis(['aspen_comm__main__.py'],
pathex=['C:_codetoolspythonaspen_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
, Я не могу найти ссылку на него в файловой структуре.
я также пробовал С теми же результатами.
Частичное Решение
если я "вручную" добавлю путь к файлу спецификации с помощью datas = [('C:_virtualenvaspenLibsite-packagestk_toolsimg', 'tk_toolsimg')]
и datas=datas
на Analysis
, тогда все работает так, как ожидалось. Это будет работать, но я бы предпочел PyInstaller найти данные пакета, так как он четко установлен. Я буду продолжать искать решение, но на данный момент - Я, вероятно, буду использовать этот неидеальный обходной путь.
если у вас есть контроль над пакетом...
затем вы можете использовать преобразовать в строки в подпакете, но это работает только в том случае, если это ваш собственный пакет.
3 ответов
следующий код поместит все файлы PNG в вашем каталоге в папку на верхнем уровне прилагаемого приложения под названием imgs
:
datas=[("C:\_code\tools\python\aspen_comm\dist\aspen_comm\tk_tools\img\*.png", "imgs")],
затем вы можете ссылаться на них с помощью os.path.join("imgs", "your_image.png")
в коде.
редактировать, чтобы добавить
чтобы решить эту проблему более постоянно, я создал пакет pip-installable под названием stringify
который возьмет файл или каталог и преобразует его в строку python, чтобы пакеты, такие как pyinstaller, распознали их как собственные файлы python.
Проверьте страница проекта обратная связь добро пожаловать!
Оригинальный Ответ
ответ запутанно и сделок с 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
автоматически создается при каждом запуске сценария установки (во время создания и установки колеса).
я могу улучшить эту технику в ближайшем будущем. Спасибо всем за помощь,
j
я решил это, воспользовавшись тем, что файл спецификации-это код Python, который выполняется. Вы можете получить корень пакета динамически на этапе сборки PyInstaller и использовать это значение в datas
список. В моем случае у меня есть что-то подобное в мой :
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
.