Estou tentando construir um EXE de um arquivo com PyInstaller que deve incluir uma imagem e um ícone. Eu não posso por minha vida fazê-lo funcionar --onefile
.
Se eu fizer --onedir
isso, tudo funcionará muito bem. Quando eu uso --onefile
, ele não consegue encontrar os arquivos adicionais referenciados (ao executar o EXE compilado). Ele encontra as DLLs e tudo o mais em ordem, mas não as duas imagens.
Eu olhei no diretório temporário gerado ao executar o EXE ( \Temp\_MEI95642\
por exemplo) e os arquivos estão realmente lá. Quando eu solto o EXE nesse diretório temporário, ele os encontra. Muito desconcertante.
Isso é o que eu adicionei ao .spec
arquivo
a.datas += [('images/icon.ico', 'D:\\[workspace]\\App\\src\\images\\icon.ico', 'DATA'),
('images/loaderani.gif','D:\\[workspace]\\App\\src\\images\\loaderani.gif','DATA')]
Devo acrescentar que tentei não colocá-los em subpastas também, não fez diferença.
Editar: a resposta mais recente marcada como correta devido à atualização do PyInstaller.
fonte
a.datas += ...
) realmente me ajudou agora. a documentação do pyinstaller fala sobre o uso,COLLECT
mas não consegue colocar os arquivos no binário ao usar--onefile
Respostas:
As versões mais recentes do PyInstaller não definem mais a
env
variável, portanto, a excelente resposta de Shish não funcionará. Agora o caminho é definido comosys._MEIPASS
:fonte
_MEIPASS2
no envs, massys._MEIPASS
funciona bem, então +1. Eu sugiro:path = getattr(sys, '_MEIPASS', os.getcwd())
AttributeError
vez do mais geralException
. Eu tive problemas em que estava faltando a importação do sistema e o caminho silenciosamente padronizadoos.path.abspath
.pyinstaller descompacta seus dados em uma pasta temporária e armazena esse caminho de diretório na
_MEIPASS2
variável de ambiente. Para obter o_MEIPASS2
dir no modo compactado e usar o diretório local no modo descompactado (desenvolvimento), eu uso o seguinte:Resultado:
fonte
resource_path("file_to_be_accessed.mp3")
. Tenha cuidado ao usar max 'answer para a versão atual do PyInstaller.--one-file
opção?Todas as outras respostas usam o diretório de trabalho atual no caso em que o aplicativo não é PyInstalled (ou
sys._MEIPASS
seja, não está definido). Isso está errado, pois impede que você execute seu aplicativo a partir de um diretório diferente daquele onde está seu script.Uma solução melhor:
fonte
os.env['_MEIPASS2']
(usando o ambiente do sistema operacional) era um método antigo de obter o diretório. AFAIKsys._MEIPASS
é o único método correto agora.resource_path()
está no script principal. Existe uma maneira de fazer funcionar quando é escrito em um módulo? A partir de agora, ele tentará obter recursos na pasta onde está o módulo, não na principal.__file__
. Se você quiser que um módulo seja capaz de acessar recursos fora de seu próprio escopo, você terá que implementar que o código de importação forneça um caminho para eles para seu módulo usar, por exemplo, pelo módulo fornecendo uma chamada "set_resource_location ()" que o chamadas do importador - não pense que isso é diferente de como você deveria trabalhar quando não está usando o pyinstaller.Talvez eu tenha perdido uma etapa ou feito algo errado, mas os métodos acima não agrupam os arquivos de dados com o PyInstaller em um arquivo exe. Deixe-me compartilhar os passos que fiz.
etapa: Grave um dos métodos acima em seu arquivo py com a importação dos módulos sys e os. Eu tentei os dois. O último é:
etapa: Grave, pyi-makespec file.py , no console, para criar um arquivo file.spec.
passo: Abra, file.spec com Notepad ++ para adicionar os arquivos de dados como abaixo:
etapa: Eu segui as etapas acima e salvei o arquivo de especificações. Por fim, abra o console e escreva, pyinstaller file.spec (no meu caso, arquivo = Converter-GUI).
Conclusão: ainda há mais de um arquivo na pasta dist.
Observação: estou usando Python 3.5.
EDIT: Finalmente funciona com o método de Jonathan Reinhart.
etapa: Adicione os códigos abaixo ao seu arquivo python com a importação de sys e os.
etapa: Chame a função acima adicionando o caminho do seu arquivo:
etapa: Escreva a variável acima que chama a função para onde seus códigos precisam do caminho. No meu caso é:
passo: Abra o console no mesmo diretório do seu arquivo python, escreva os códigos como abaixo:
passo: Salve e saia do arquivo de caminho. Vá para a pasta que inclui o arquivo spec e py. Abra novamente a janela do console e digite o comando abaixo:
Após a etapa 6., seu único arquivo está pronto para uso.
fonte
a.datas
matriz da etapa 5 use tuplas de strings, consulte pythonhosted.org/PyInstaller/spec-files.html#adding-data-files para referência.Em vez de reescrever todo o meu path code conforme sugerido, alterei o diretório de trabalho:
Basta adicionar essas duas linhas no início de seu código, você pode deixar o resto como está.
fonte
Ligeira modificação na resposta aceita.
fonte
A reclamação / pergunta mais comum que vi no PyInstaller é "meu código não consegue encontrar um arquivo de dados que definitivamente incluí no pacote, onde está?", E não é fácil ver qual / onde seu código está procurando porque o código extraído está em um local temporário e é removido ao sair. Adicione este pedaço de código para ver o que está incluído em seu arquivo on-line e onde ele está, usando @Jonathon Reinhart
resource_path()
fonte
Achei as respostas existentes confusas e levei muito tempo para descobrir onde estava o problema. Aqui está uma compilação de tudo que encontrei.
Quando executo meu aplicativo, recebo um erro
Failed to execute script foo
(sefoo.py
for o arquivo principal). Para solucionar isso, não execute o PyInstaller com--noconsole
(ou editemain.spec
para alterarconsole=False
=>console=True
). Com isso, execute o executável a partir de uma linha de comando e você verá a falha.A primeira coisa a verificar é se ele está empacotando seus arquivos extras corretamente. Você deve adicionar tuplas,
('x', 'x')
se quiser que a pastax
seja incluída.Depois que ele travar, não clique em OK. Se você estiver no Windows, poderá usar Pesquisar tudo . Procure um de seus arquivos (por exemplo
sword.png
). Você deve encontrar o caminho temporário onde descompactou os arquivos (por exemplo.C:\Users\ashes999\AppData\Local\Temp\_MEI157682\images\sword.png
). Você pode navegar neste diretório e certificar-se de que incluiu tudo. Se você não conseguir encontrar desta forma, procure algo comomain.exe.manifest
(Windows) oupython35.dll
(se estiver usando Python 3.5).Se o instalador incluir tudo, o próximo problema provável é a E / S do arquivo: seu código Python está procurando no diretório do executável, em vez do diretório temporário, por arquivos.
Para consertar isso, qualquer uma das respostas nesta pergunta funciona. Pessoalmente, descobri que uma mistura de todos eles funcionam: mude o diretório condicionalmente a primeira coisa em seu arquivo de ponto de entrada principal e todo o resto funcionará como está:
if hasattr(sys, '_MEIPASS'): os.chdir(sys._MEIPASS)
fonte
Se você ainda está tentando colocar arquivos relativos ao seu executável em vez de no diretório temporário, você precisa copiá-lo você mesmo. Foi assim que acabei fazendo.
https://stackoverflow.com/a/59415662/999943
Você adiciona uma etapa no arquivo spec que faz uma cópia do sistema de arquivos para a variável DISTPATH.
Espero que ajude.
fonte
Tenho lidado com esse problema há muito (bem, muito tempo) tempo. Procurei quase todas as fontes, mas as coisas não estavam entrando em um padrão na minha cabeça.
Finalmente, acho que descobri as etapas exatas a seguir e gostaria de compartilhar.
Note que minha resposta usa informações sobre as respostas de outras pessoas sobre esta questão.
Como criar um executável autônomo de um projeto Python.
Suponha que temos um project_folder e a árvore de arquivos é a seguinte:
Em primeiro lugar, digamos que você tenha definido seus caminhos para
sound/
eimg/
pastas em variáveissound_dir
e daimg_dir
seguinte forma:Você deve alterá-los da seguinte forma:
Onde,
resource_path()
é definido no início do seu script como:Ative o env virtual se estiver usando um venv,
Instale pyinstaller se você fez não ainda, por:
pip3 install pyinstaller
.Executar:
pyi-makespec --onefile main.py
para criar o arquivo de especificações para o processo de compilação e construção.Isso mudará a hierarquia do arquivo para:
Aberto (com um edior)
main.spec
:No topo, insira:
Em seguida, altere a linha de
datas=[],
paradatas=added_files,
Para mais detalhes sobre as operações realizadas,
main.spec
consulte aqui.Corre
pyinstaller --onefile main.spec
E isso é tudo, você pode executar
main
emproject_folder/dist
qualquer lugar, sem ter qualquer outra coisa em sua pasta. Você pode distribuir apenas essemain
arquivo. É agora, um verdadeiro autônomo .fonte
Usando a excelente resposta de Max e Este post sobre adicionar arquivos de dados extras como imagens ou som e minha própria pesquisa / teste, descobri o que acredito ser a maneira mais fácil de adicionar esses arquivos.
Se você gostaria de ver um exemplo ao vivo, meu repositório está aqui no GitHub.
Nota: isso é para compilar usando o comando
--onefile
ou-F
com pyinstaller.Meu ambiente é o seguinte.
Resolvendo o problema em 2 etapas
Para resolver o problema, precisamos informar especificamente ao Pyinstaller que temos arquivos extras que precisam ser "agrupados" com o aplicativo.
Também precisamos usar um caminho 'relativo' , para que o aplicativo possa ser executado corretamente quando estiver sendo executado como um script Python ou um EXE congelado.
Com isso dito, precisamos de uma função que nos permita ter caminhos relativos. Usando a função que Max Postou , podemos resolver facilmente o caminho relativo.
Usaríamos a função acima assim, para que o ícone do aplicativo apareça quando o aplicativo estiver sendo executado como um Script OU EXE congelado.
A próxima etapa é que precisamos instruir o Pyinstaller sobre onde encontrar os arquivos extras durante a compilação para que, quando o aplicativo for executado, eles sejam criados no diretório temporário.
Podemos resolver esse problema de duas maneiras, conforme mostrado na documentação , mas eu pessoalmente prefiro gerenciar meu próprio arquivo .spec, então é assim que faremos.
Primeiro, você já deve ter um arquivo .spec. No meu caso, consegui criar o que precisava executando
pyinstaller
com args extras. Você pode encontrar args extras aqui . Por causa disso, meu arquivo de especificações pode parecer um pouco diferente do seu, mas estou postando tudo para referência depois de explicar as partes importantes.add_files é essencialmente uma lista contendo tuplas, no meu caso eu só quero adicionar uma ÚNICA imagem, mas você pode adicionar vários ico's, png's ou jpg's usando
('app/img/*.ico', 'app/img')
Você também pode criar outra tuplaadded_files = [ (), (), ()]
para ter várias importaçõesA primeira parte da tupla define que arquivo ou que tipo de arquivo você gostaria de adicionar, bem como onde encontrá-los. Pense nisso como CTRL + C
A segunda parte da tupla diz ao Pyinstaller para tornar o caminho 'app / img /' e colocar os arquivos nesse diretório RELATIVO a qualquer diretório temporário criado quando você executa o .exe. Pense nisso como CTRL + V
Em
a = Analysis([main...
, eu configureidatas=added_files
, originalmente costumava ser,datas=[]
mas queremos que a lista de importações seja, bem, importada, então passamos nossas importações personalizadas.Você não precisa fazer isso, a menos que queira um ícone específico para o EXE; na parte inferior do arquivo de especificações, estou dizendo ao Pyinstaller para definir o ícone do meu aplicativo para o exe com a opção
icon='app\\img\\app_icon.ico'
.Compilando para EXE
Sou muito preguiçoso; Não gosto de digitar coisas mais do que preciso. Criei um arquivo .bat no qual posso apenas clicar. Você não precisa fazer isso, este código será executado em um shell de prompt de comando sem ele.
Uma vez que o arquivo .spec contém todas as nossas configurações de compilação e args (opções também), só temos que fornecer esse arquivo .spec para o Pyinstaller.
fonte
Outra solução é fazer um gancho de tempo de execução, que irá copiar (ou mover) seus dados (arquivos / pastas) para o diretório no qual o executável está armazenado. O gancho é um arquivo python simples que pode fazer quase tudo, logo antes da execução do seu aplicativo. Para configurá-lo, você deve usar a
--runtime-hook=my_hook.py
opção de pyinstaller. Portanto, caso seus dados sejam uma pasta de imagens , você deve executar o comando:O cp_images_hook.py poderia ser algo assim:
Antes de cada execução, a pasta de imagens é movida para o diretório atual (da pasta _MEIPASS), para que o executável sempre tenha acesso a ela. Dessa forma, não há necessidade de modificar o código do seu projeto.
Segunda Solução
Você pode aproveitar as vantagens do mecanismo runtime-hook e alterar o diretório atual, o que não é uma boa prática de acordo com alguns desenvolvedores, mas funciona bem.
O código do gancho pode ser encontrado abaixo:
fonte