Packaging An Sqlite db-included CRUD PyQt5 app using PyInstaller

Packaging An Sqlite db-included CRUD PyQt5 app using PyInstaller

The Python programmer’s journey inevitably leads him to one of the black belts of the industry: packaging and distribution. But, particularly in Python, distribution can also be a black beast. We’ve seen slow and steady progress in the field with the advent of tools like cx_freeze, pyinstaller and protocols like zipapp. In this post we’ll see how to package a realistic PyQt5 app.

As a side note, i was hesitating between titles to choose so i made a poll in our facebook group. This title got far more votes many requests beforehand. I went on with it.

Complexity of the demo app

Since we are focusing on packaging, we deliberately choose a demo that tackles common headaches. We included picture files and sqlite database. We’ll package a CRUD app which uses SqlAlchemy. You can use this as a basis for more ambitious projects. Moreover we used some ‘good’ PyQt5 practices like custom widgets, OOP approach and namespaced functions.

Repo at PyQt5_CRUD

Project Structure

Our project looks like this

db/ is the folder used for storing databases

pics/ is the picture where pics are stored

models.py is where we’ll define our model

main.py is the file we’ll run

utils.py contains some utitlities functions

base.py contains some SqlAlchemy configurations

main.spec is the pyinstaller specifications file

App & Code

The app we’ll be building

It features the minimum working mechanism a CRUD app should have. Let’s see what each file contains

base.py

models.py

Our demo product just has a barcode and a name.

utils.py

The first function allows you to get the correct resource path like an image right. Just putting pics/icon.png is going to break when using the executable version. So putting

ensure smooth fetching.

The second function just spares you having to write layout.addWidget many times.

main.py

Let’s see our imports

We can see that for PyQt5 we imported only QtWidgets. To use a QPushButton we need to write QtWidgets.QPushButton. This takes longer to read but makes you a Qt master, believe me. There are many advantages in doing so, the least of them is easy PySide2 migration

Then we’ll see our globals

session is used by SqlAlchemy to make queries

Then we have our first custom widget: the AddProduct widget. In the above picture, the complete add product section is defined and manage by the following widget

The layout_addWidget functions come from utils.py

The EditProduct widget manages the edit section. The edit section works as follows: you must input a barcode, then press the load button, then edit.

DeleteProduct manages the delete section

ViewProduct is the display area

Then the main window using all these widgets

And finally to finish, we have our main

template.spec

The spec file is normally generated using the command pyinstaller main.py

However you can save it as template.spec

The added_files variable adds everything in the designated folder, it’s one of the tricks to get our app working. Notice the wildcard operator (*).

then we add it in

Run instructions

Make sure you have the required packages. Run

To test if all is well, run python main.py and see if the app appears

Next we generate our main.spec using

-F tells pyinstaller that we need a single file executable

Now add this after block_cipher

Then change this

to this

You might want to change the last part where console=True to

In short we copied template.spec’s content into main.spec keeping pathex= intact.

Build your executable using

You will get two new folders

Under dist/ you should see your executable

Dealing with headaches!

Normally you should have a working executable file, however i’ve discovered that while the exec file can work well on your PC, it might not work so well on another person PC due to some internal hardcoding of paths. The issue apparently varies from PyQt5 version to version. In case you encounter this error, downgrade some 0.0.01 version back or forward.

Words of caution

This app is without safety nets and was intended as a packaging demo.

You can download the repo here: PyQt5_CRUD . Anything unclear, mail me at <arj.python at gmail dot com>

Lives in Mauritius, cruising python waters for now.