Estoy tratando de crear un archivo EXE de un solo archivo con PyInstaller que debe incluir una imagen y un icono. No puedo por mi vida hacer que funcione --onefile
.
Si lo hago --onedir
, todo funciona muy bien. Cuando lo uso --onefile
, no puede encontrar los archivos adicionales a los que se hace referencia (al ejecutar el EXE compilado). Encuentra las DLL y todo lo demás bien, pero no las dos imágenes.
He mirado en el directorio temporal generado al ejecutar el EXE ( \Temp\_MEI95642\
por ejemplo) y los archivos están ahí. Cuando coloco el EXE en ese directorio temporal, los encuentra. Muy desconcertante.
Esto es lo que agregué al .spec
archivo
a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico', 'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]
Debo agregar que he intentado no ponerlos en subcarpetas también, no marcó la diferencia.
Editar: la respuesta más reciente marcada como correcta debido a la actualización de PyInstaller.
fuente
a.datas += ...
) realmente me ayudó en este momento. la documentación de pyinstaller habla sobre el uso,COLLECT
pero eso no logra poner archivos en el binario cuando se usa--onefile
Respuestas:
Las versiones más nuevas de PyInstaller ya no establecen la
env
variable, por lo que la excelente respuesta de Shish no funcionará. Ahora la ruta se establece comosys._MEIPASS
:fuente
_MEIPASS2
envs, perosys._MEIPASS
funciona bien, así que +1. Sugiero:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
lugar del más generalException
. Me encontré con problemas en los que me faltaba la importación del sistema y la ruta se predeterminaba silenciosamenteos.path.abspath
.pyinstaller descomprime sus datos en una carpeta temporal y almacena esta ruta de directorio en la
_MEIPASS2
variable de entorno. Para obtener el_MEIPASS2
directorio en modo empaquetado y usar el directorio local en modo desempaquetado (desarrollo), uso esto:Salida:
fuente
resource_path("file_to_be_accessed.mp3")
. Tenga cuidado de que debería usar max 'answer para la versión actual de PyInstaller.--one-file
opción?Todas las otras respuestas usan el directorio de trabajo actual en el caso de que la aplicación no esté PyInstalled (
sys._MEIPASS
es decir, no esté configurada). Eso es incorrecto, ya que le impide ejecutar su aplicación desde un directorio que no sea aquel donde está su script.Una mejor solucion:
fuente
os.env['_MEIPASS2']
(usar el entorno del sistema operativo) era un método antiguo para obtener el directorio. AFAIKsys._MEIPASS
es el único método correcto ahora.resource_path()
está en el script principal. ¿Hay alguna forma de hacer que funcione cuando está escrito en un módulo? A partir de ahora, intentará obtener recursos en la carpeta donde está el módulo, no en la principal.__file__
. Si desea que un módulo pueda acceder a recursos fuera de su propio alcance, tendrá que implementar que el código de importación proporcione una ruta para que su módulo lo use, por ejemplo, mediante el módulo que proporciona una llamada "set_resource_location ()" que el llamadas de importador: no crea que esto sea diferente de cómo debería trabajar cuando no usa pyinstaller.Quizás me perdí un paso o hice algo mal, pero los métodos anteriores no agruparon archivos de datos con PyInstaller en un archivo exe. Permítanme compartirles los pasos que he realizado.
paso: Escriba uno de los métodos anteriores en su archivo py con la importación de módulos sys y os. Probé los dos. El ultimo es:
paso: Escriba, pyi-makespec file.py , en la consola, para crear un archivo file.spec.
paso: Abra, file.spec con Notepad ++ para agregar los archivos de datos como se muestra a continuación:
paso: Seguí los pasos anteriores y luego guardé el archivo de especificaciones. Por fin abrí la consola y escribí, pyinstaller file.spec (en mi caso, file = Converter-GUI).
Conclusión: todavía hay más de un archivo en la carpeta dist.
Nota: estoy usando Python 3.5.
EDITAR: Finalmente funciona con el método de Jonathan Reinhart.
paso: agregue los siguientes códigos a su archivo de Python con la importación de sys y os.
paso: Llame a la función anterior agregando la ruta de su archivo:
paso: Escriba la variable anterior que llama a la función donde sus códigos necesitan la ruta. En mi caso es:
paso: Abra la consola en el mismo directorio de su archivo python, escriba los códigos como se muestra a continuación:
paso: guarde y salga del archivo de ruta. Vaya a su carpeta que incluye el archivo de especificaciones y py. Abra de nuevo la ventana de la consola y escriba el siguiente comando:
Después del 6. paso, su archivo está listo para usar.
fuente
a.datas
matriz del paso 5 tome tuplas de cadenas, consulte pythonhosted.org/PyInstaller/spec-files.html#adding-data-files como referencia.En lugar de reescribir todo mi código de ruta como se sugirió, cambié el directorio de trabajo:
Simplemente agregue esas dos líneas al comienzo de su código, puede dejar el resto como está.
fuente
Ligera modificación de la respuesta aceptada.
fuente
La queja / pregunta más común que he visto en PyInstaller es "mi código no puede encontrar un archivo de datos que definitivamente incluí en el paquete, ¿dónde está?", Y no es fácil ver qué / dónde su código está buscando porque el código extraído está en una ubicación temporal y se elimina cuando sale. Agregue este fragmento de código para ver qué se incluye en su archivo único y dónde está, usando @Jonathon Reinhart's
resource_path()
fuente
Encontré las respuestas existentes confusas y me tomó mucho tiempo averiguar dónde está el problema. Aquí hay una recopilación de todo lo que encontré.
Cuando ejecuto mi aplicación, aparece un error
Failed to execute script foo
(sifoo.py
es el archivo principal). Para solucionar este problema, no ejecute PyInstaller con--noconsole
(o editemain.spec
para cambiarconsole=False
=>console=True
). Con esto, ejecute el ejecutable desde una línea de comandos y verá el error.Lo primero que debe verificar es que está empaquetando correctamente sus archivos adicionales. Debe agregar tuplas como
('x', 'x')
si deseax
que se incluya la carpeta .Después de que se bloquee, no haga clic en Aceptar. Si está en Windows, puede usar Buscar todo . Busque uno de sus archivos (p. Ej.
sword.png
). Debería encontrar la ruta temporal donde descomprimió los archivos (por ejemploC:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png
). Puede navegar por este directorio y asegurarse de que incluye todo. Si no puede encontrarlo de esta manera, busque algo comomain.exe.manifest
(Windows) opython35.dll
(si está usando Python 3.5).Si el instalador incluye todo, el siguiente problema probable es la E / S de archivos: su código Python busca archivos en el directorio del ejecutable, en lugar del directorio temporal.
Para solucionarlo, cualquiera de las respuestas a esta pregunta funciona. Personalmente, encontré una combinación de todos ellos para funcionar: cambie el directorio condicionalmente primero en su archivo de punto de entrada principal, y todo lo demás funciona como está:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
fuente
Si todavía está intentando colocar archivos relacionados con su ejecutable en lugar de en el directorio temporal, debe copiarlo usted mismo. Así es como terminé haciéndolo.
https://stackoverflow.com/a/59415662/999943
Agrega un paso en el archivo de especificaciones que hace una copia del sistema de archivos a la variable DISTPATH.
Espero que ayude.
fuente
He estado tratando con este problema desde hace mucho (bueno, muy largo tiempo). He buscado en casi todas las fuentes, pero las cosas no estaban siguiendo un patrón en mi cabeza.
Finalmente, creo que he descubierto los pasos exactos a seguir, quería compartirlos.
Tenga en cuenta que mi respuesta utiliza información sobre las respuestas de otros en esta pregunta.
Cómo crear un ejecutable independiente de un proyecto de Python.
Supongamos que tenemos un project_folder y el árbol de archivos es el siguiente:
En primer lugar, digamos que ha definido sus rutas
sound/
yimg/
carpetas en variablessound_dir
y de laimg_dir
siguiente manera:Tienes que cambiarlos, de la siguiente manera:
Donde,
resource_path()
se define en la parte superior de su secuencia de comandos como:Active env virtual si usa un venv,
Instalar PyInstaller si lo hizo, no todavía, por:
pip3 install pyinstaller
.Ejecutar:
pyi-makespec --onefile main.py
para crear el archivo de especificaciones para el proceso de compilación y construcción.Esto cambiará la jerarquía de archivos a:
Abierto (con un edior)
main.spec
:En la parte superior, inserte:
Luego, cambie la línea de
datas=[],
adatas=added_files,
Para conocer los detalles de las operaciones realizadas,
main.spec
consulte aquí.correr
pyinstaller --onefile main.spec
Y eso es todo, puede ejecutar
main
enproject_folder/dist
desde cualquier lugar, sin tener otra cosa en su carpeta. Solo puede distribuir esemain
archivo. Ahora es un verdadero independiente .fuente
Usando la excelente respuesta de Max y esta publicación sobre agregar archivos de datos adicionales como imágenes o sonido y mi propia investigación / prueba, descubrí lo que creo que es la forma más fácil de agregar dichos archivos.
Si desea ver un ejemplo en vivo, mi repositorio está aquí en GitHub.
Nota: esto es para compilar usando el comando
--onefile
o-F
con pyinstaller.Mi entorno es el siguiente.
Resolviendo el problema en 2 pasos
Para resolver el problema, necesitamos decirle específicamente a Pyinstaller que tenemos archivos adicionales que necesitan ser "empaquetados" con la aplicación.
También necesitamos usar una ruta 'relativa' , para que la aplicación pueda ejecutarse correctamente cuando se ejecuta como un script de Python o un EXE congelado.
Dicho esto, necesitamos una función que nos permita tener rutas relativas. Usando la función que Max Posted podemos resolver fácilmente la ruta relativa.
Usaríamos la función anterior de esta manera para que el ícono de la aplicación aparezca cuando la aplicación se esté ejecutando como Script O Frozen EXE.
El siguiente paso es que necesitamos indicarle a Pyinstaller dónde encontrar los archivos adicionales cuando se está compilando para que cuando se ejecute la aplicación, se creen en el directorio temporal.
Podemos resolver este problema de dos maneras, como se muestra en la documentación , pero personalmente prefiero administrar mi propio archivo .spec, así es como lo haremos.
Primero, ya debe tener un archivo .spec. En mi caso, pude crear lo que necesitaba ejecutando
pyinstaller
con argumentos adicionales, puede encontrar argumentos adicionales aquí . Debido a esto, mi archivo de especificaciones puede verse un poco diferente al suyo, pero lo publicaré como referencia después de explicar los puntos importantes.added_files es esencialmente una lista que contiene tuplas, en mi caso solo quiero agregar una imagen ÚNICA, pero puede agregar múltiples ico, png o jpg usando
('app/img/*.ico', 'app/img')
También puede crear otra tupla comoadded_files = [ (), (), ()]
para tener múltiples importacionesLa primera parte de la tupla define qué archivo o qué tipo de archivo le gustaría agregar, así como dónde encontrarlos. Piense en esto como CTRL + C
La segunda parte de la tupla le dice a Pyinstaller que haga la ruta 'app / img /' y coloque los archivos en ese directorio RELATIVOS a cualquier directorio temporal que se cree cuando ejecuta el .exe. Piense en esto como CTRL + V
Debajo
a = Analysis([main...
, configurédatas=added_files
, originalmente solía ser,datas=[]
pero queremos que la lista de importaciones sea, bueno, importada, así que pasamos nuestras importaciones personalizadas.No necesita hacer esto a menos que desee un ícono específico para el EXE, en la parte inferior del archivo de especificaciones le digo a Pyinstaller que configure el ícono de mi aplicación para el exe con la opción
icon='app\\img\\app_icon.ico'
.Compilar a EXE
Soy muy flojo; No me gusta escribir cosas más de lo necesario. He creado un archivo .bat en el que puedo hacer clic. No tiene que hacer esto, este código se ejecutará en un shell del símbolo del sistema perfectamente sin él.
Dado que el archivo .spec contiene todas nuestras configuraciones y argumentos de compilación (también conocidos como opciones), solo tenemos que darle ese archivo .spec a Pyinstaller.
fuente
Otra solución es crear un enlace de tiempo de ejecución, que copiará (o moverá) sus datos (archivos / carpetas) al directorio en el que está almacenado el ejecutable. El gancho es un archivo Python simple que puede hacer casi cualquier cosa, justo antes de la ejecución de su aplicación. Para configurarlo, debe usar la
--runtime-hook=my_hook.py
opción de pyinstaller. Entonces, en caso de que sus datos sean una carpeta de imágenes , debe ejecutar el comando:El cp_images_hook.py podría ser algo como esto:
Antes de cada ejecución, la carpeta de imágenes se mueve al directorio actual (desde la carpeta _MEIPASS), por lo que el ejecutable siempre tendrá acceso a ella. De esa forma, no es necesario modificar el código de su proyecto.
Segunda solución
Puede aprovechar el mecanismo de enlace en tiempo de ejecución y cambiar el directorio actual, lo que no es una buena práctica según algunos desarrolladores, pero funciona bien.
El código de gancho se puede encontrar a continuación:
fuente