Reputation: 2903
I have a simple kivy app that I would like to generate an single exe for.
I generated a virtual env using virtualenv --python=C:\Python27\python.exe <path/to/new/virtualenv/>
. I then activate that environment.
Within the virtualenv, I then install the following modules:
python -m pip install --upgrade pip wheel setuptools
python -m pip install docutils pygments pypiwin32 kivy.deps.sdl2 kivy.deps.glew --extra-index-url https://kivy.org/downloads/packages/simple/
python -m pip install kivy
pip install pyinstaller
I then have the following files below.
The touch.py
file contains the code below:
import ctypes
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.core.window import Window
SIZE = 2
NUM_BOXES = SIZE * SIZE
class MyGrid(GridLayout):
def __init__(self, app, matrix, m_size, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.app = app
self.matrix = matrix
self.m_size = m_size
self.sq_size_x = Window.size[0] / m_size
self.sq_size_y = Window.size[1] / m_size
self.count = set()
def on_touch_down(self, touch):
pass
def on_touch_move(self, touch):
try:
mouse_x, mouse_y = touch.pos
section_x = int(mouse_x // self.sq_size_x)
section_y = int(mouse_y // self.sq_size_y)
self.matrix[section_y][section_x].background_color = (0, 1, 0, 1)
self.count.add(str(section_y) + "_" + str(section_x))
except:
## DONT KNOW WHY IT GETS OUT OF RANGE SOMETIMES
pass
## FULLY COLORED, CLOSE APP
if len(self.count) >= NUM_BOXES:
self.app.stop()
def on_touch_up(self, touch):
pass
class MyApp(App):
def populate_matrix(self):
matrix = [ [ Button(text="") for x in range(self.m_size) ] for y in range(self.m_size)]
return matrix
def populate_layout(self):
for y in range(self.m_size-1,-1,-1): ## FILL y BACKWARDS, SINCE BOTTOM IS 0
for x in range(self.m_size):
self.layout.add_widget(self.matrix[y][x])
def get_screen_size(self):
user32 = ctypes.windll.user32
screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
return screensize
def build(self):
screensize = self.get_screen_size()
Window.size = (screensize[0], screensize[1])
Window.fullscreen = 'auto' ## MAKES APP FULLSCREEN BUT HAS COORDINATE ISSUES (IF ONLY BYITSELF)
Window.borderless = True
self.m_size = SIZE
self.matrix = self.populate_matrix()
self.layout = MyGrid(self, self.matrix, self.m_size, cols=self.m_size)
self.populate_layout()
return self.layout
if __name__ == "__main__":
MyApp().run()
The touch.spec
file contains the code below:
# -*- mode: python -*-
block_cipher = None
from kivy_deps import sdl2, glew
a = Analysis(['touch.py'],
pathex=["C:\\Users\\XXX\\Desktop\\testingKivy"],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='touch',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=True )
coll = COLLECT(exe, Tree("C:\\Users\\XXX\\Desktop\\testingKivy"),
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
strip=False,
upx=True,
name='touchtracer')
I then run the command:
python -m PyInstaller --onefile touch.spec
When executing just pyinstaller --onefile touch.py
, the exe generated doesnt work. It seems like some of the dependencies under sld2 is not packaged correctly. Thus I ended up updating the .spec
file with the above.
When using the touch.spec file, it does generate a single exe, but that doesnt work by itself. Under the dist
folder, it generates a touch.exe (which doesnt work by itself), but under dist folder, it also generates a folder touchtracer
, which has a lot of files/dlls and also a touch.exe (which works fine). My question is, is there a way to package this into a single exe?
Somebody did say "make sure in the spec file there is no collect step:
"In one-file mode, there is no call to COLLECT, and the EXE instance receives all of the scripts, modules and binaries."" (link). But I dont know how to include the sdl2 dependencies in another way. Not sure how to use --add-data correctly in this case.
Upvotes: 3
Views: 1547
Reputation: 38822
Running Pyinstaller
with a .spec
file argument makes it ignore almost any options that you provide on the command line. I would recommend doing a pyi-makespec --onefile touch.py
to get a one file starting .spec
file. Then edit the touch.spec
file to add the sdl
stuff. Just add the same line (*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)]
) in the exe
portion just after the a.datas
line (just like you have it in the coll
section). Then just run python -m PyInstaller touch.spec
.
Upvotes: 4