You can convert a Python script into an executable (EXE) file using the PyInstaller library. Once your Python code has been tested and validated, you can share it as an executable desktop application from any computer. This is especially useful for graphical user interfaces (GUIs) such as this serial monitor developed in Python.
Project structure
To begin with, we need to structure our project properly. To do this, we use the following tree structure
app/
│
├── resources
| ├── logo.ico
| ├── logo.png
| ├── style.txt
├── app.py
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
└── tests.py
- app.py contains the main code
- logo.ico will be the icon file used after compilation
- logo.png is used in the software interface
- style.txt contains the CSS style definition
In this tutorial, we’ll create a simple interface using PySide (you can also use PyQt) containing a button to modify the messages in the debug bar.
#!/usr/bin/python # -*-coding:Utf-8 -* """ Created on Thu Nov 17 16:59:13 2022 @author: X.Wiedmer Dummy app Define a simple app to test PyInstaller """ import sys,os #from PyQt5.QtWidgets import * #from PyQt5.QtCore import * #from PyQt5.QtGui import * from PySide2.QtWidgets import * from PySide2.QtCore import * from PySide2.QtGui import * pyqtSignal=Signal #translate pyqt to Pyside # Generic app container 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) """ App configuration """ __title__="DummyApp" __version__="v0.1" style_path = resource_path("resources\\style.txt") __icon__=resource_path("resources/logo.png") class AcApp(QMainWindow): def __init__(self,title='DummyApp',mainFrame=QFrame): super().__init__() self.title=title self.mainFrame=mainFrame() self.initUI() def initUI(self): #mainframe self.setCentralWidget(self.mainFrame) centralLayout=QHBoxLayout(self.mainFrame) #widgets self.button = QPushButton("Start") self.button.clicked.connect(self.clicked) centralLayout.addWidget(self.button) #General configuration self.setWindowTitle(self.title) self.setGeometry(300, 300, 400, 200) self.setWindowIcon(QIcon(__icon__)) #Debug bar self.statusBar() self.statusBar().showMessage('Display debug messages') self.show() def debugMsg(self,val): self.statusBar().showMessage(val) def clicked(self): if self.button.text() == "Start": self.debugMsg("Process started") self.button.setText("Stop") else: self.debugMsg("Process stopped") self.button.setText("Start") def main(): app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(True) app.setStyleSheet(open(style_path).read()); #set style according to file ex = AcApp(__title__+__version__) app.quit() sys.exit(app.exec_()) if __name__ == '__main__': main()
N.B.: If you want files such as the icon to be included in the .EXE, you’ll need to specify the absolute path in the Python script.
To automatically generate absolute paths, you can use the following function
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) style_path = resource_path("resources\\ac_style.txt")
Installing the PyInstaller package
To compile the project, we use the PyInstaller library, which creates an executable file containing all the dependencies needed to run the code.
Before installing pyinstaller, check that C:\Users\ADMIN\AppData\Local\Programs\Python\Python38\Scripts is added to the environment variables path.
python -m pip install pyinstaller==5.6.2
N.B.: version 5.7.0 has been released but displays a permission error
Configuring the install_bat file
We create an install_app.bat file to automate the compilation process. In this file, we specify the compilation type (onefile, noconsole), the libraries (PySide2) and the folders to be included in the executable (resources).
python -m PyInstaller --noconfirm --log-level=WARN ^
--onefile --noconsole ^
--hidden-import=PySide2 ^
--hidden-import=shiboken2 ^
--add-data ./resources;resources ^
--icon=./resources/logo_araignee.ico ^
app.py
PyInstaller will create the folders build, dist and a file app.spec
The app.spec file contains the compilation specifications and the folder where the EXE file is stored.
You can now share this executable file with anyone (using the same OS) without any special installation on their part.
Limitations
PyInstaller doesn’t allow you to compile for different OS on the same machine. To create a Windows application, you need to use PyInstaller on a Windows machine. You’ll need to do the same for Linux or MacOS.
PyInstaller does not contain all dependencies. In fact, some dependencies are contained in the OS. This means that some people may not be able to run your software.
It is possible to cross-compile for different OSes using a docker.