fbpixel
Tags:

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()
python-dummy-app Creating an EXE executable from a Python script

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
python-pyinstaller Creating an EXE executable from a Python script

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.

python-pyinstaller-result Creating an EXE executable from a Python script

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.

Sources