Skip to content

Use Python PyInstaller to make an executable file with picture

Last Updated on 2020-11-04 by Clay

python
Display Python code on the screen

Introduction

When we used PyInstaller to create a Python program as an executable file, we usually need to display a picture in our program.

But if we used PyInstaller to make an executable file directly, we found we can't show the picture. How does we solve this problem?

We have three methods to solve it, and I will record them in this article.

Method 1Method 2Method 3
Degree of difficultyEasyEasyDifficult
MethodPut the picture in the directory of the executable fileUse absolute path to load pictureConvert the picture to byte and load it in the code
PropertiesOn your computer, neither executable files nor pictures can be changed into foldersCan only be used on your own computerIt is still available even if you share it with others

And I will introduce these ways.


Prepare in advance

If you want to know how does PyInstall work, may be you can refer my blog article in past: PyInstaller —— How to make a exe file from Python file

If you want to learn about PyQt5 module, may be you can refer my blog article: PyQt5 Tutorial (1) Install PyQt5 and print “Hello World!”

Here we go!

First, I use Qt Designer (a graphic tool) to create a simple window, there is only a component: QLabel.

My sample code:

# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(796, 554)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(170, 70, 431, 341))
        self.label.setText("")
        self.label.setObjectName("label")

        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))


This is just a graphic interface code, I will write another program to inherit this interface class.

# -*- coding: utf-8 -*-
import sys
import cv2
from PyQt5 import QtWidgets
from PyQt5.QtGui import *
from test import Ui_MainWindow


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        image = cv2.imread('test.png')
        h, w, channel = image.shape
        bytesPerLine = 3*w
        image = QImage(image, w, h, bytesPerLine, QImage.Format_RGB888).rgbSwapped()
        image = QPixmap(image)

        self.ui.label.setPixmap(image)
        self.ui.label.setScaledContents(True)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


Output:

I use OpenCV module to load the picture (the picture named test.png), and use QPixmap() to store it, and use the QLabel component to show it.

At this point, the preparatory work has come to an end.


Method-1: Put the picture in the directory of the executable file

First we using the Python package PyInstaller to package our program with picture to the executable file.

pyinstaller -F test_GUI.py --noconsole

(Explain: -F means we package to just only one executable file, --noconsole means we cancel the terminal black window show when we running the executable file)

Then we come to the dist folder.

We have an executable file test_GUI.exe, double click and run it.

We will get an error message: Failed to execute script test_GUI. This is normal, because the executable file cannot find the picture we want to display.

We can use the method 1 to fix it: We copy the explore picture and move it to this folder. Don't forget will must named it be test.png.

And then we try again!

This time we can execute the program normally. But I have to remind: I do not recommend this way. You can not move the picture, and the picture need to stay with the executable file.


Method-2: using absolution path to load picture

This is another easy way to display our picture.

image = cv2.imread('C:/Users/Clay/PycharmProjects/VideoScreenshot/test.png')


We use absolution path to replace the relative path, and we use PyInstaller to package this program again.

pyinstaller -F test_GUI.py --noconsole
這張圖片的 alt 屬性值為空,它的檔案名稱為 image-65.png

This time, we have no picture under our executable file directory. But when we execute the test_GUI.exe:

這張圖片的 alt 屬性值為空,它的檔案名稱為 image-64.png

We can show the picture! But we can not move the picture path (It is stored somewhere in our computer). This way is not good, we is difficult to share this executable program to others.


The best Method:
Convert picture to byte data, and put it in code and load it.

This is the my most recommended method, it is almost guaranteed to be shared with others.

Fist, we need to use base64 module to convert picture to byte data:

# -*- coding: utf-8 -*-
import base64


def pic2str(file, functionName):
    pic = open(file, 'rb')
    content = '{} = {}\n'.format(functionName, base64.b64encode(pic.read()))
    pic.close()

    with open('pic2str.py', 'a') as f:
        f.write(content)


if __name__ == '__main__':
    pic2str('test.png', 'explode')


The function pic2str() only needs to input picture path and variable name and it will be worked. This function will create a new .py file named pic2str.py to assign the picture byte data to the variable in the pic2str.py.

Let's take a look at the results:

The byte data is very long and it is too long make we can not see it all.

And we change some executable file code, import pic2str script (and its variable).

# -*- coding: utf-8 -*-
import sys
import base64
from io import BytesIO
from PyQt5 import QtWidgets
from PyQt5.QtGui import *
from test import Ui_MainWindow
from PIL import Image, ImageQt

from pic2str import explode


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # Load byte data
        byte_data = base64.b64decode(explode)
        image_data = BytesIO(byte_data)
        image = Image.open(image_data)

        # PIL to QPixmap
        qImage = ImageQt.ImageQt(image)
        image = QPixmap.fromImage(qImage)

        # QPixmap to QLabel
        self.ui.label.setPixmap(image)
        self.ui.label.setScaledContents(True)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())


And we package it again:

pyinstaller -F test_GUI.py --noconsole

We can delete all the explode picture in your computer and execute the test_GUI.exe. And you can see our program is executable successful.

If it can be displayed normally, it means that this method is very successful and we can share it with friends at will!

This method is worked because PyInstaller does not package Python files as native executable files, but creates an environment that can execute Python files, so usually this exe file is very big and not link to any picture.

External materials such as pictures are one of the objects that PyInstaller will not link together, so this loophole is exploited and the bytes of the picture are directly stored in the .py file.

If the .py files are not successfully packaged, it means that this module really has a big loophole.

However, I currently use PyInstaller. Although I have encountered a lot of pitfalls, it can be solved by patience.

By the way, if you get an error message:

ValueError: unsupported image mode 'LA'

Maybe you can refer this article: [Solved][Python] ValueError: unsupported image mode ‘LA’


References

Tags:

4 thoughts on “Use Python PyInstaller to make an executable file with picture”

Leave a Reply