Pyinstaller Trouble with PyQt5 and Matplotlib on Python 3.7

Pyinstaller is a fantastic python to exe packager. When I tried making a stand alone exe with a python script (PyQt5Test.py) using a quick PyQt5 example, it creates a working exe file right off the bat using this simple command:

pyinstaller PyQt5Test.py --onefile

Here’s PyQt5Test.py:

import sys
from PyQt5.QtWidgets import QWidget, QMessageBox, QApplication

class Example(QWidget):    
    def __init__(self):
        super().__init__()        
        self.initUI()        
        
    def initUI(self):               
        
        self.setGeometry(300, 300, 250, 150)        
        self.setWindowTitle('Message box')    
        self.show()       
        
    def closeEvent(self, event):
        
        reply = QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QMessageBox.Yes | 
            QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()        
                
if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

Running the executable file from the dist folder in works perfectly just as the same way as you just run the script in the interpreter

I left the console option in the background so that if there’s an error I could see it when the program runs. However, you could get rid of the console by adding “–noconsole” option to pyinstaller.

pyinstaller PyQt5Test.py --onefile --noconsole

Now that works so well, I want to see whether I could make a standalone file with a script that has PyQt5 and Matplotlib. Here’s the code I use to test this:

import sys
import PyQt5
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QVBoxLayout, \
   QSizePolicy, QMessageBox, QWidget, QPushButton, QListWidget, QDesktopWidget
from PyQt5.QtGui import QIcon
from PyQt5 import QtCore
import PIL
import tkinter
from tkinter import filedialog


from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt

import random

class App(QMainWindow):

    def __init__(self):
        super().__init__()
        self.left = 10
        self.top = 10
        self.title = 'PyQt5 matplotlib example'
        self.setFixedSize(640, 700)
        self.initUI()

    def location_on_the_screen(self):
        ag = QDesktopWidget().availableGeometry()
        sg = QDesktopWidget().screenGeometry()

        widget = self.geometry()
        #x = ag.width() - widget.width()
        #y = 2 * ag.height() - sg.height() - widget.height()
        x = sg.width() - widget.width()
        y = 2 * ag.height() - sg.height() - widget.height()
        self.move(x, y)

    def initUI(self):
        self.setWindowTitle(self.title)
        #self.setGeometry(self.left, self.top, self.width, self.height)
        self.location_on_the_screen()

        m = PlotCanvas(self, width=5, height=4)
        m.move(0,0)

        button = QPushButton('PyQt5 button', self)
        button.setToolTip('This s an example button')
        button.move(500,0)
        button.resize(140,100)

        button2 = QPushButton('Plot', self)
        button2.setToolTip('Plot Data')
        button2.move(500,102)
        button2.resize(140,100)

        listBox = QListWidget(self)
        listBox.setGeometry(QtCore.QRect(120,10,281,192))
        listBox.move(500,400)

        self.show()


class PlotCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                QSizePolicy.Expanding,
                QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        self.plot()


    def plot(self):
        data = [random.random() for i in range(25)]
        ax = self.figure.add_subplot(111)
        ax.plot(data, 'r-')
        ax.set_title('PyQt Matplotlib Example')
        self.draw()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())

When creating an exe for this script using Pyinstaller I see a lot of warnings but no errors. I even got an exe. However, when I ran the exe I got an error:
ModuleNotFoundError: No module named ‘numpy.core._dtype_ctypes’

I did have Numpy correctly installed using pip and I have version 1.16.0 and it’s supposedly the most up-to-date version.

After Googling the error, I found that the problem was indeed Numpy itself and the issue is fixed if I have version 1.16.1. However, doing pip install numpy doesn’t work since it insists that 1.16.0 was the up-to-date version.

Therefore, I have to force the install of 1.16.1 Numpy

pip install numpy==1.16.1

This forces the new version of Numpy to be installed. I tried to create the exe file again using Pyinstaller. Again, the exe creation went just fine as before. I wasn’t sure whether this would work or not, but to my surprise when running the exe file I have a working program!

I was quite pleased with the result. Now I could move forward with using Pyinstaller to make a standalone program.