I have made something with Kivy, here is the core code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
class UI(Widget):
pass
class UIApp(App):
def build(self):
Window.bind(on_key_down=self.key_action)
return UI()
def key_action(self, *args):
self.process()
def on_start(self, **kwargs):
pass
def on_stop(self):
raise SystemExit(0)
def process(self):
OtherFile.test()
if __name__ == "__main__":
Builder.load_file('UI.kv')
from kivy.core.window import Window
UIApp().run()
As you see, I need the window import to get my keyboardhook.
OtherFile.test() however uses multi-threading. When that happens, new Kivy windows pop up non-stop. But it only happens if I package it into an exe, now while the script is executed normally. I have followed this guide and have the folowing spec file:
# -*- mode: python -*-
import sys
from os import path
site_packages = next(p for p in sys.path if 'site-packages' in p)
from kivy.deps import sdl2, glew
block_cipher = None
a = Analysis(['Main.py'],
pathex=['C:\\Users\\GFuel\\PycharmProjects\\proj'],
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,
[],
exclude_binaries=True,
name='AppName',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe, Tree('C:\\Users\\GFuel\\PycharmProjects\\proj'),
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
strip=False,
upx=True,
name='AppName')
I compile with PyInstaller AppName.spec -windowed -singlefile
. The window itself works fine:
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Text ] Provider: sdl2
[INFO ] [GL ] NPOT texture support is available
[INFO ] [Base ] Start application main loop
But as soon as I press a key more and more windows start opening. My guess would be that for some reason __name__ is __main__ even when it shouldn't be (during multiprocessing) because of PyInstaller, but I don't know how to fix it.
Edit: I have proven this with:
if __name__ == "__main__":
print("YAY")
from kivy.lang import Builder
from kivy.core.window import Window
Builder.load_file('UI.kv')
UIApp().run()
YAY appears several times after I press a key. So in short, is there a way to make the packaged script work correctly?
if __name__ == "__main__":
multiprocessing.freeze_support()
These have to be the first lines that are run in the main script. The script still uses all the threads it should, but works properly with an exe.
Related
I'm trying to generating a .exe of my kivymd code. I have coded a really simple code, because i was trying to learn how to do this.
My code is as follows:
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import Screen
class Demo(MDApp):
def build(self):
self.screen = Screen()
self.l1 = MDLabel(
text = "My Label"
)
self.screen.add_widget(self.l1)
return self.screen
if __name__ == "__main__":
Demo().run()
Really simple.
So i'm using a .spec like this:
# -*- mode: python ; coding: utf-8 -*-
import os
from kivy_deps import sdl2, glew
block_cipher = None
from kivymd import hooks_path as kivymd_hooks_path
current_path = os.path.abspath('.')
icon_path = os.path.join(current_path, 'imagens', 'icone.ico')
a = Analysis(['main.py'],
pathex=[current_path],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[kivymd_hooks_path],
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,
[],
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
name='Eaton',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
icon=icon_path,
console=False )
My code file name is main.py and my .spec is main.spec.
So my problem is, when I use the console=True, this code and .spec works fine and create a good .exe, but when I use console=False the aplication returns error.
If someone can help me I will be very thankful.
I am attempting to open a second GUI using PyQt5 and a second .py file - it works in the IDE but not when compiled as a single .exe. The first GUI opens, the second does not. I receive ModuleNotFoundError.
Traceback (most recent call last):
File "C:\Users\Me\PycharmProjects\ProjectName\dist\firstwindow\secondwindow.py", line 1, in <module>
import mysql.connector
ModuleNotFoundError: No module named 'mysql'
I have tried modifying the spec file in so many ways now that I can't even recall. From what I can tell, Pyinstaller has no issues importing modules from the first .py file, but when it attempts a second level import or hidden import on the secondwindow.py - it has issues.
I've tried pointing to the sql module in the pathex, datas and hidden imports fields of the spec file. I am entering text such as 'C:\\Users\\Me\\PycharmProjects\\ProjectName\\venv\\Lib\\site-packages\\mysql' into all three of these fields and still receiving the same error.
I have tried using --hiddenimport=mysql as in: pyinstaller --onefile --hiddenimport=mysql test.spec
In the example below, I have imported mysql.connector and arbitrarily used it in the code. This is to demonstrate the error I am receiving in my actual program relating to mysql. If I remove mysql.connector or attempt to import PyQt5 beforehand, I still have errors relating to PyQt5.
Minimum Reproducible Code:
firstwindow.py
import mysql.connector
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
import subprocess
import sys
db = mysql.connector.connect()
class FirstWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.setCentralWidget(self.button)
def show_new_window(self, checked):
subprocess.Popen("secondwindow.py", shell=True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = FirstWindow()
window.show()
sys.exit(app.exec_())
secondwindow.py
import mysql.connector
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
import sys
db = mysql.connector.connect()
class NewWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button = QPushButton("Second Window Opened")
self.setCentralWidget(self.button)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = NewWindow()
window.show()
sys.exit(app.exec_())
test.spec
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['firstwindow.py', 'secondwindow.py'],
pathex=['C:\\Users\\Me\\PycharmProjects\\ProjectName', 'C:\\Users\\Me\\PycharmProjects\\ProjectName\\venv\\Lib\\site-packages\\mysql'],
binaries=[],
datas=[('firstwindow.py', '.'), ('secondwindow.py', '.'), ('C:\\Users\\Me\\PycharmProjects\\ProjectName\\venv\\Lib\\site-packages\\mysql', '.')],
hiddenimports=['C:\\Users\\Me\\PycharmProjects\\ProjectName\\venv\\Lib\\site-packages\\mysql'],
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,
[],
exclude_binaries=True,
name='firstwindow',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='firstwindow')
followed by: pyinstaller --onefile test.spec
I am trying unsuccessfully to wrap up my kivy GUI application into a single executable with pyinstaller. Whey I run PyInstaller, it creates a single executable, but when I run it I get a "fatal error" saying "Failed to execute script myApp". I am using python 2.7 on windows. Here is my directory setup:
GUI_DEV\
-myApp_style.kv (this is my kivy file)
-myApp.py (this is my main script)
-ico.ico (this is my icon file)
-myImage.png (this is an image used by myApp.py)
rel\
-myApp.spec (this is the spec file I am using with pyinstaller)
Here is my myApp.py code:
import kivy
#kivy.require("1.9.0")
from kivy.core.window import Window
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.lang import Builder
import sys, os
def resourcePath():
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS)
return os.path.join(os.path.abspath("."))
Builder.load_file('myApp.kv')
class designerLayout(GridLayout):
part = "123"
def update(self):
self.part = value
self.id_test.text = "hello world: "+str(self.part)
return
def spinner_clicked(self, value):
self.part = value
self.id_test.text = "hello world: "+str(self.part)
class myApp(App):
def build(self):
Window.clearcolor = (1,1,1,1)
Window.size = (930,780)
return designerLayout()
if __name__ == '__main__':
kivy.resources.resource_add_path(resourcePath()) # add this line
calcApp = myApp()
calcApp = myApp()
calcApp.run()
Here is my myApp.spec file:
# -*- mode: python -*-
from kivy.deps import sdl2, glew
block_cipher = None
a = Analysis(['..\\myApp.py'],
pathex=['C:\\GUI_DEV\\rel\\MyHiddenImports'],
binaries=None,
datas=None,
hiddenimports=['MyHiddenImports'],
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)
a.datas += [('myApp_style.kv', '../myApp_style.kv', 'DATA')]
exe = EXE(pyz, Tree('C:\\GUI_DEV\\','Data'),
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
name='myApp',
debug=False,
strip=False,
upx=True,
console=False, icon='C:\\GUI_DEV\\ico.ico' )
I am then running the command:
python -m PyInstaller myApp.spec
from the GUI_DEV\rel directory.
Any ideas as to why this is not working? I am not getting any errors that point me to any specific problems.
I have been fighting PyInstaller for the past week trying to get my application to compile into a single executable.
I've tried several different implementations with the .spec file, and of the many methods I try, I can either get it to compile into a single executable that crashes immediately on launch, doesn't launch at all, or it runs, but is extremely slow. Nothing compared to that of when I run it out of PyCharm.
I'm unsure if the slow run speeds is because of the compiler or what, but it takes roughly 1-2 seconds for the execution task to run when I run it through PyCharm, however when it is ran from the executable it takes about 30-35 seconds and the application hangs.
My application essentially takes some text from TextInput boxes, grabs the text values from them, does some SQL querying and then submits proper information to update / add entry information into an access database.
My latest .spec file is as follows:
# -*- mode: python -*-
import pyodbc
from datetime import date
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.deps import sdl2, glew
block_cipher = None
a = Analysis(['DBInterfaceAssistant.py'],
pathex=['C:\\Python36-32'],
binaries=[],
datas=[],
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,
a.binaries,
a.zipfiles,
a.datas,
name='DBInterfaceAssistant',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False )
The product of this is an application that doesn't launch, it attempts to load the application but crashes immediately.
---EDIT---
My Current build script is as follows:
# -*- mode: python -*-
import pyodbc
from datetime import date
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ObjectProperty
from kivy.deps import sdl2, glew
from kivy.tools.packaging.pyinstaller_hooks import get_deps_minimal, get_deps_all, hookspath, runtime_hooks
block_cipher = None
a = Analysis(['DBInterfaceAssistant.py'],
pathex=['C:\\Python36-32'],
binaries=[],
datas=[],
hookspath=hookspath(),
runtime_hooks=runtime_hooks(),
** get_deps_all())
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
name='DBInterfaceAssistant',
debug=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False )
You may use the example from the Kivy docs:
from kivy.tools.packaging.pyinstaller_hooks import get_deps_minimal, get_deps_all, hookspath, runtime_hooks
a = Analysis(['examples-path\\demo\\touchtracer\\main.py'],
...
hookspath=hookspath(),
runtime_hooks=runtime_hooks(),
...
**get_deps_all())
coll = COLLECT(exe, Tree('examples-path\\demo\\touchtracer\\'),
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
strip=False,
upx=True,
name='touchtracer')
https://kivy.org/docs/guide/packaging-windows.html#overwrite-win-hook
"main.py"
from kivy.app import App
class WeatherApp(App):
pass
if __name__="__main__":
WeatherApp().run()
"weather.kv"
Label:
text: "hello world"
i am expecting a window with a black background and the words "hello world"
in the middle
For me I managed to solve
Editing the spec file like so:
# -*- mode: python ; coding: utf-8 -*-
from kivy_deps import sdl2, glew
block_cipher = None
a = Analysis(['hello.py'],
pathex=['C:\\Users\\<username>\\PycharmProjects\\<project_dir>'],
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,
[],
exclude_binaries=True,
name='hello',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
Tree('C:\\Users\\<username>\\PycharmProjects\\<project_dir>'),
a.binaries,
a.zipfiles,
a.datas,
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
strip=False,
upx=True,
upx_exclude=[],
name='hello')
by copying the kivymd dir C:\Users<username>\Anaconda3\envs<project_name>\Lib\site-packages\kivymd to your dist folder
Roboto-Regular.ttfs Not Found - Windows 8.1
Currently, in the Kivy config file, the default fonts used for widgets displaying any text defaults to [‘Roboto’, ‘data/fonts/Roboto-Regular.ttf’, ‘data/fonts/Roboto-Italic.ttf’, ‘data/fonts/Roboto-Bold.ttf’, ‘data/fonts/Roboto-BoldItalic.ttf’].
The latest version of Roboto can be found at https://github.com/google/roboto/releases
There is a typo error and indentation problem in your app. After fixing the problems, your app should works as shown in the example below.
Replace:
if __name__="__main__":
with:
if __name__ == "__main__":
Example
main.py
from kivy.app import App
class WeatherApp(App):
pass
if __name__ == "__main__":
WeatherApp().run()
weather.kv
#:kivy 1.10.0
Label:
text: "hello world"
Output