Reputation: 1318
I've seen some related questions here and tried every single solution I found like this and this for example, but I couldn't make that work. So the question is simple - after making single exe file from kivy it throws a FileNotFoundError: [Errno 2] No such file or directory: 'main2.kv'
. Exe file with related files works good but I don't know what am I doing wrong while building single exe.
My spec file:
# -*- mode: python ; coding: utf-8 -*-
from kivymd.tools.packaging.pyinstaller import hooks_path as kivymd_hooks_path
from kivy_deps import sdl2, glew
...
a = Analysis(['main.py'],
...
# I tried to write here absolute path, relative path and this method
datas=[('*.kv', '.')],
hiddenimports=[],
hookspath=[kivymd_hooks_path],
...
)
# I also tried to put import here - didn't help (when I did that I also tried to change the path in my .py file to 'Data\main2.kv')
# a.datas += [('main2.kv', 'D:\\prog\\Lotto\\main2.kv', 'DATA')]
...
# tried with Tree and without Tree
coll = COLLECT(exe, Tree('D:\\prog\\Lotto','Data'),
...
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
...
In .py file I tried to add
if getattr(sys, 'frozen', False):
kivy.resources.resource_add_path(sys._MEIPASS)
And
def resourcePath():
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS)
return os.path.join(os.path.abspath("."))
...
if __name__ == "__main__":
kivy.resources.resource_add_path(resourcePath())
MainApp().run()
And tried both of above methods together - nothing. Also tried to change PyInstaller 3.6 to 3.5 version. I don't know what's the problem here, it's my first time trying to make exe file from Kivy.
Upvotes: 1
Views: 1197
Reputation: 197
I would like to put here my experience and how I could solve it. Following is my directory structure.
AppDir
|_ src
|_ *.kv, *.py
|_ images
|_ *.png
As suggested in above answer I did not do resource_add_path
in my main (It did not work for me), instead just prepend sys._MEIPASS
to kv file for Builder.load_file
. But then you would face a problem, you will not be able to run it directly as a python script. I solved it by conditionally prepending path based on packaged binary or python script.
This is how I did it, in a common_defines.py
file I defined KV_DIRECTORY
# common_defines.py
import os, sys
# Assuming all .kv files are in same directory as this file
KV_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
# If the script is bundled with PyInstaller, override the path with _MEIPASS
if getattr(sys, 'frozen', False):
# Bundled with PyInstaller
KV_DIRECTORY = sys._MEIPASS
And wherever you want to load the kv file, load with file with KV_DIRECTORY
prepended to file name.
For example
# some_file.py
import os
from kivy.lang import Builder
import common_defines
Builder.load_file(os.path.join(common_defines.KV_DIRECTORY, 'some_file.kv'))
This way kv file path for both scenarios will be set correctly.
Then you can just run following command from AppDir
to generate executable.
pyinstaller --onefile -w --add-data "src\*.kv;." --add-data "images\*.png;images" src\main.py
Upvotes: 0
Reputation: 1318
Few hours later finally I got that. I open my kv file as file
first and then pass it to Builder
to be able to set the encoding. In that case I just needed to define the path of kv
file manually as sys._MEIPASS + 'main2.kv'
and then it worked.
UPD: To make that work make few imports first:
import os, sys
from kivy.resources import resource_add_path
and then at the end of your app just before MainApp.run() add this:
if __name__ == "__main__":
# these lines should be added
if hasattr(sys, '_MEIPASS'):
resource_add_path(os.path.join(sys._MEIPASS))
###
MainApp().run()
When we build single .exe file it holds all files inside of it, so it unpacks it to some random temporary folder while running. sys._MEIPASS
here is the path of that temp folder.
So that should work. If it still can't find files, you can try to change file path like this: for example, instead of 'example.kv'
write sys._MEIPASS + '/' + 'example.kv'
.
Also make sure you've done everything right in the .spec file, so you should add your files to datas
list in Analysis
like this:
a = Analysis(['main.py'],
...
datas=[('main.kv', '.'), ('bg.png', '.'), ('CenturyGothic.ttf', '.'), ('finish.mp3', '.'), ('Logo.png', '.'),],
So if you have only kv
file to add it would be:
datas=[('main.kv', '.')],
Upvotes: 1