I'm trying to export my .py script to .exe using PyInstaller, which has dependencies on .ui files which were created using Qt Designer.
I can confirm that my .py script works just fine when running it through PyCharm - I'm able to see the GUI I've created with the .ui files.
However, when I export my .py script to .exe and launch it, I recieve the following errors in the command line:
C:\Users\giranm>"C:\Users\giranm\PycharmProjects\PyQt Tutorial\dist\secSearch_demo.exe"
Traceback (most recent call last):
File "secSearch_demo.py", line 13, in <module>
File "site-packages\PyQt4\uic\__init__.py", line 208, in loadUiType
File "site-packages\PyQt4\uic\Compiler\compiler.py", line 140, in compileUi
File "site-packages\PyQt4\uic\uiparser.py", line 974, in parse
File "xml\etree\ElementTree.py", line 1186, in parse
File "xml\etree\ElementTree.py", line 587, in parse
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\giranm\\securitySearchForm.ui'
Failed to execute script secSearch_demo
For some reason, the .exe file is looking for the .ui file within the path - C:\Users\giranm\
However, having done some research already, I was told that I needed to use os.getcwd() and ensure that I have the full path in my script. Even with the code below, I still get errors trying to locate the .ui files.
PyInstaller: IOError: [Errno 2] No such file or directory:
# import relevant modules etc...
cwd = os.getcwd()
securitySearchForm = os.path.join(cwd, "securitySearchForm.ui")
popboxForm = os.path.join(cwd, "popbox.ui")
Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)
# remainder of code below.
I'm aware that one can convert .ui files to .py and import them into the main routine using pyuic4. However, I will be making multiple edits to the .ui files
and thus it is not feasible for me to keep converting them.
Is there anyway to fix this so that I can create a standalone .exe?
I'm fairly new to using PyQT4 and PyInstaller - any help would be much appreciated!
After scratching my head all weekend and looking further on SO, I managed to compile the standalone .exe as expected using the UI files.
Firstly, I defined the following function using this answer
Bundling data files with PyInstaller (--onefile)
# Define function to import external files when using PyInstaller.
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
Next I imported the .UI files using this function and variables for the required classes.
# Import .ui forms for the GUI using function resource_path()
securitySearchForm = resource_path("securitySearchForm.ui")
popboxForm = resource_path("popbox.ui")
Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)
I then had to create a resource file (.qrc) using Qt Designer and embed images/icons using this resource file. Once done, I used pyrcc4 to convert the .qrc file to .py file, which would be imported in the main script.
Terminal
C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyrcc4 -py3 resources.qrc -o resources_rc.py
Python
import resources_rc
Once I have confirmed the main .py script works, I then created a .spec file using PyInstaller.
Terminal
C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyi-makespec --noconsole --onefile secSearch_demo.py
As per PyInstaller's guide, I've added data files by modifying the above .spec file.
https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files
Finally, I then compiled the .exe using the .spec file from above.
You can simply use:
uic.loadUi(r'E:\Development\Python\your_ui.ui', self)
Use the full path, and use pyinstaller with standard arguments, and it works fine. The r prefix makes sure the backslashes are interpreted literally.
Another method, tested on Ubuntu 20.04 is to add the .ui file to the data section in the spec file. First generate a spec file with pyinstaller --onefile hello.py. Then update the spec file and run pyinstaller hello.spec.
a = Analysis(['hello.py'],
...
datas=[('mainwindow.ui', '.')],
...
The next step is to update the current directory in your Python file. To do this, the os.chdir(sys._MEIPASS) command has to be used. Wrap it in a try-catch for development use when _MEIPASS is not set.
import os
import sys
# Needed for Wayland applications
os.environ["QT_QPA_PLATFORM"] = "xcb"
# Change the current dir to the temporary one created by PyInstaller
try:
os.chdir(sys._MEIPASS)
print(sys._MEIPASS)
except:
pass
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile, QIODevice
if __name__ == "__main__":
app = QApplication(sys.argv)
ui_file_name = "mainwindow.ui"
ui_file = QFile(ui_file_name)
if not ui_file.open(QIODevice.ReadOnly):
print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
sys.exit(-1)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
if not window:
print(loader.errorString())
sys.exit(-1)
window.show()
sys.exit(app.exec_())
Related
I get an error when i'm compiling with pyinstaller it works on my pc not on a different pc.
error i get
Traceback (most recent call last):
File "app.py", line 30, in <module>
password = parser.get('settings', 'password')
File "configparser.py", line 781, in get
File "configparser.py", line 1152, in _unify_values
configparser.NoSectionError: No section: 'settings'
[9464] Failed to execute script 'app' due to unhandled exception!
Here is my code
parser = ConfigParser()
parser.read('C:\\Users\\abc\Desktop\\Maker\\settings.ini')
I followed some solutions but still no luck is solving this can anyone please help?
I tried this solution as well with no luck
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
parser = ConfigParser()
settings_file = resource_path('settings.ini')
You have to make sure you are bundling your ini file with your application. This is a little more difficult if you are using UPX to compress all files into one executable file. I never saw the point in this because it seems like a false savings. Every time you run the EXE it will decompress all files into a temporary folder. Then your application now consumes all of the uncompressed space plus all of the compressed space. Bleah.
I would recommend you use a Spec file to specify all of the additional
files you want to include with your application.
For debugging purposes, you can put this in your program to make sure the ini file is where you think it is (you can delete it or comment it out once you verify the file is there)
print(os.listdir(<path>))
or
print(os.listdir(os.getcwd()))
where getcwd stands for 'get current working directory' which should be the directory where your EXE resides.
If you don't want to mess with a spec file, you can pass the --add-data parameter to pyinstaller and pass your ini file.
I`m trying to make an executable from my python script called ProyectoNew.py. It works with a folder called "Imagenes" and another called "ModulosExternos", and a PyQT5's .ui file like this:
Here`s the Code posted in GitHub: https://github.com/TheFlosh/ProyectoSoftware.git
I`ve tried to use Pyinstaller, Py2Exe and CXFreeze but it didn't worked. Using each one of these modules to create a .exe file I've got the same result when I tried to execute it in another PC, as shown here:
On the LEFT of the picture you can see what I get after using pyinstaller (or Py2Exe), on the right you can see what I need to show.
Here are the modules that I use in my code ("ModulosExternos" is the folder where I put some particular modules that my code needs):
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import pyttsx3
from pyttsx3.drivers import sapi5
import PyQt5
import sip
import os
from ModulosExternos import Boton_1,Boton_2,Boton_3,Boton_4,Boton_Final,Listas_Pictogramas, Frases_Completas
And here is the last part of it, to instantiate the GUI:
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
GUI = ProyectoNew()
GUI.show()
sys.exit(app.exec())
I`ve read a lot of posts on the internet that recommended creating Setup.Py file to initiate the process of creating an executable file of my project. These are some examples of what I did with two of them:
With CX_Freeze:
import sys
import os
from cx_Freeze import setup, Executable
files = ['icono.ico','/Imagenes']
target = Executable(
script = "/ProyectoNew.py",
base = 'Win32GUI',
)
#Setup
setup(
name = "Proyect2",
version = "1.6",
description = "Software",
author = "----",
options = {'build_exe': {'include_files' : files}},
executables = [target]
)
With Py2Exe:
from cx_Freeze import setup, Executable
setup(name = "Proyect2",
version="1.0",
description = "Software",
executables = [Executable("ProyectoNew.py")],)
With Pyinstaller I typed: python --nowindowed --onefile ProyectoNew.py but I constantly got the same result as shown before.
I think that when I execute Pyinstaller, the .exe file doesn`t load the modules and the images that I use. What am I missing while creating the file? What do I need to do to execute the .exe file in another PC?.
I would prefer to use pyinstaller,but using any one of these will help me.
What's is wrong with that? You get some kind of error? Please edit your question with that.
With pyinstaller and this command, you should be able easily build an executable file:
pyinstaller [FILE].py -w -F
This command generate a /dist folder with the neccesary files and .exe file to run
*-w param is for do not provide a console window for standard i/o
*-F param is for one-file bundled executable
You can check more params here
PD: Before, you need obviously install pyinstaller:
pip install pyinstaller
I'm fairly new with programming (and with Python) and the Stack Overflow question/response system allowed me to resolve all my problems until now. I didn't find any post directly addressing my current issue, but have to admit that I don't really know what's wrong. Let me explain.
I'm trying to make an executable file of a *.py script using PyInstaller. There's no problem doing it with a simple Python script (using --onefile), but it does not work when it comes to a more complex program that uses other *.py and *.txt files. I know that I need to modify the specification file and tried many alternatives - adding hidden files for instance.
Here are the files:
UpdatingStrategy.py (the target file to transform in executable)
LPRfunctions.py (UpdatingStrategy.py imports functions from this file)
The following *.txt files are read by UpdatingStrategy.py:
Strategy_Observ.txt
Strategy_Problems.txt
Updating_Observ1.txt
Updating_Observ2.txt
Updating_Problems.txt
I'm using Python 3.5 and Windows 10. Tell me if you need extra information.
How do I use the specification file properly and modify it in order to make an executable file of UpdatingStrategy.py?
I have read the PyInstaller documentation, but I lack many key principles and I couldn't make it work.
After the line
a = Analysis( ... )
add
a.datas += [
("/absolute/path/to/some.txt","txt_files/some.txt","DATA"),
("/absolute/path/to/some2.txt","txt_files/some2.txt","DATA"),
("/absolute/path/to/some3.txt","txt_files/some3.txt","DATA"),
]
Then in your program use the following to get the resource path of your .txt files.
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.environ.get("_MEIPASS2",os.path.abspath("."))
return os.path.join(base_path, relative_path)
...
txt_data = open(resource_path("txt_files/some.txt")).read()
Make sure you build it like python -m PyInstaller my_target.spec ... do not call PyInstaller directly against your .py file after you have edited your specification file or it will overwrite your edited file...
Anyone reading this in 2021... I've just run into similar issue with Pyinstaller. Needed to add a text file to my python code:
pyinstaller --add-data "/my/path/to/mytextfile.txt:/path/mytextfile.txt" mypython.py --onedir or --onefile
No matter if onedir or onefile, my code just never found the text file. Infact it threw the error:
IsADirectoryError: [Errno 21] Is a directory: /path/mytextfile.txt
Which didn't make sense to me because that's a file not a folder. Only when I checked this with the --onedir flag and I followed the path, I realized that pyinstaller would not only create a folder path, but also folder mytextfile.txt and put mytextfile.txt in there... so really the --add-data flag only wants a destination folder not the path to the file.
pyinstaller --add-data "/my/path/to/mytextfile.txt:path" mypython.py
or in the spec file i.e mypython.spec
datas=[('/my/path/to/mytextfile.txt', 'path')]
Should fix this. Note also the "DATA" configuration in the spec file is gone.
I've been working on getting a python executable working for OSX El Capitan, and i successfully get the executable built using both Pyinstaller and cx_Freeze, the issue comes when i actually run the executable on another mac. The error i get is about not being able to find the .db file referenced in my main script, so i looked at documentation for both programs and came across sys.MEIPASS (Pyinstaller) and sys.executable (cx_Freeze) to include data files in the --onefile app. This is the code i used in my main script:
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys._MEIPASS) #in cx_Freeze this is sys.executable
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = ospath.abspath(os.pardir)
return os.path.join(datadir, filename)
#This is how im using the "find_data_file" function in my code.
dbpath = find_data_file('PubData.db')
conn = lite.connect(dbpath)
ive changed it a bit in the else statement to match the layout of my project directories, and it works perfectly fine when running an unfrozen application.
However when i try to run using the built executable it gives me an error about not being able to find the .db file, which i thought referencing sys.MEIPASS or sys.executable would fix.
Error:
Traceback (most recent call last):
File "interface/GUI.py", line 673, in <module>
File "interface/GUI.py", line 82, in __init__
File "interface/GUI.py", line 212, in getServerNames
sqlite3.OperationalError: no such table: servernames
This is how my file tree looks:
PubData-master ##(Project Root Directory)
Interface ##(Directory)
GUI.py ##(Main Script, this is where i reference 'PubData.db')
GUI.spec ##(Pyinstaller spec file)
PubData.db ## This is my database file, in the PubData-master Directory
If anyone could tell me what i am doing wrong, or give me a solution, i would be extremely grateful!
if you are trying to access any data file you specified in the .spec file, in your code, you must use Pyinstaller's _MEIPASS folder to reference your file. Here is how i did so with the file named Data.db:
import sys
import os.path
if hasattr(sys, "_MEIPASS"):
datadir = os.path.join(sys._MEIPASS, 'Data.db')
else:
datadir = 'Data.db'
conn = lite.connect(datadir)
this replaced the following single line:
conn = lite.connect("Data.db")
This link explained it very well:
https://irwinkwan.com/2013/04/29/python-executables-pyinstaller-and-a-48-hour-game-design-compo/
I have converted a python game I designed into an exe. Running the exe itself causes it to flash and then close, meaning an error has occured. Running it from the Command Prompt causes the error as well, but documents it:
Cannot load image: Playfield.png
Couldn't open images\Playfield.png
This is telling me that the load_image block is failing. I have encountered this before when I did not have an images directory.
I attempted to move the images folder to the dist directory. This is the error that shows up:
Traceback (most recent call last):
File "Table_Wars.py", line 728, in <module>
File "Table_Wars.py", line 51, in main
File "Table_Wars.py", line 236, in __init__
File "pygame\__init__.pyc", line 70, in __getattr__
NotImplementedError: font module not available
(ImportError: DLL load failed: The specified module could not be found.)
This is my first time with py2exe, so I'm not really sure what is happening. The raw python file itself, Table_Wars.py, runs as expected.
If it helps, the location for the entire Table_Wars folder is inside a folder called Games, located on my Desktop (C:\Users\Oventoaster\Desktop\Games\Table_Wars). I am running Windows 7 32 bit OS.
On request, here is the output.txt I have generated:
Folder PATH listing for volume OS
Volume serial number is 7659-4C9C
C:\USERS\OVENTOASTER\DESKTOP\GAMES\TABLE_WARS
build
bdist.win32
winexe
bundle-2.7
collect-2.7
ctypes
distutils
email
mime
encodings
logging
multiprocessing
dummy
pygame
threads
unittest
xml
parsers
temp
dist
images
Here is the setup.py I used to convert the file:
from distutils.core import setup
import py2exe
setup(console=['Table_Wars.py'])
EDIT: I have attempted to use the full py2exe example. This will create the exe, but gives the same Cannot load image error. Attempting to put the images folder in the same folder as the exe creates a Runtime Error: The application requested the runtime to terminate it in an unusual way.
The shortened form of the code Slace Diamond suggested prevents py2exe from finding Table_Wars.py:
from cmd:
running py2exe
*** searching for required modules ***
error: Table_Wars.py: No such file or directory.
setup and Table_Wars are in the same directory. If it help, I input the full path to python.exe and setup.py.
EDIT: I seem to be getting closer. I put the images directory within self.extra_datas, and now I am getting this:
Fatal Python error: (segmentation fault)
This application has requested the runtime to terminate it in an unusual way. Please contact the application's suppourt team for more information
When you build a distributable package with py2exe (and py2app for that matter), part of the package environment is to point to a local resource location for files. In your plain unpackaged version, you are referring to a relative "images/" location. For the packaged version, you need to configure your setup.py to include the resources in its own location.
Refer to this doc for very specific info about how to set the data_files option of your package: http://www.py2exe.org/index.cgi/data_files
That page has multiple examples to show both very simple paths, and also a helper function for finding the data and building the data_files list for you.
Here is an example of the simple snippet:
from distutils.core import setup
import py2exe
Mydata_files = [('images', ['c:/path/to/image/image.png'])]
setup(
console=['trypyglet.py.py']
data_files = Mydata_files
options={
"py2exe":{
"unbuffered": True,
"optimize": 2,
"excludes": ["email"]
}
}
)
This closely matches what you are trying to achieve. It is saying that the "image.png" source file should be placed into the "images" directory at the root of the resources location inside the package. This resource root will be your current directory from your python scripts, so you can continue to refer to it as a relative sub directory.
It looks like you've already fixed the image problem by moving the folder into dist. The missing font module, on the other hand, is a known problem between pygame and py2exe. Py2exe doesn't copy some necessary DLLs, so you have to override py2exe's isSystemDLL method, forcing it to include audio and font related DLLs.
If Table_Wars.py is the only module in your project, try running this script with python setup.py py2exe:
from os.path import basename
from distutils.core import setup
import py2exe
origIsSystemDLL = py2exe.build_exe.isSystemDLL
def isSystemDLL(pathname):
if basename(pathname).lower() in ("libogg-0.dll", "sdl_ttf.dll"):
return 0
return origIsSystemDLL(pathname)
py2exe.build_exe.isSystemDLL = isSystemDLL
setup(windows=[{"script": "Table_Wars.py"}],
options={"py2exe": {"dist_dir": "dist"}})
You could also try the example py2exe setup file on the pygame wiki. If neither of them are working, please add the error messages to your question.
I tried running py2exe on a sample project, and it also breaks for me when I use the default pygame font. If you're using the default font, try putting a ttf file in the root of your project and also in the dist folder. You'll have to change the call to pygame.Font in your script as well:
font = pygame.font.Font("SomeFont.ttf", 28)