Note: see the edits below for the latest investigation.
I have a PySide2 application (running on miniforge python 3.10.6 with PySide2 v5.15.5 on Windows 10) that uses a QLabel with a QPixmap. The QPixmap loads a jpeg image from a file and then a QPainter draws some additional stuff on top.
On the PC I'm using for development, it all works fine. I've written a cx_freeze setup script that compiles the python code to an executable (plus a folder full of DLLs etc).
Both the python script (run with python MyScript.pyw) and the compiled executable work fine on my development PC.
When I copy the cx_freeze executable folder to another PC (without python / Qt installed), the application runs, but the image isn't displayed and there are lots of errors in the console, starting with:
QPainter::begin: Paint device returned engine == 0, type: 2
QPainter::setPen: Painter not active
Given that it works flawlessly on the development PC (either via the script or the compiled executable), I presume there's nothing fundamentally wrong with the code so I'm guessing that there is some component that isn't being included in the cx_freeze folder. Is there any easy way to work out what's been omitted?
This is the setup.py script:
import sys
import os
import shutil
from cx_Freeze import setup, Executable
build_exe_options = {
"packages": ["atexit"],
"include_files": [
"image.jpg",
"uiFile.ui",
],
"excludes": [
"tkinter",
],
"build_exe": "MyScript",
}
base = None
#if sys.platform == "win32":
# base = "Win32GUI"
setup( name="MyScript",
version="0.1",
description="My Script",
options={
"build_exe": build_exe_options,
},
executables = [Executable("MyScript.pyw", base=base)])
tkinter is excluded as without its exclusion, the build fails due to a non-found file (I don't think I have tcl/tkinter installed on the development PC).
The folder generated by cx_freeze includes about 270 MB of files in the lib/PySide2 subfolder (including multiple copies of the 28 MB unicode DLL in several folders!), so presumably cx_freeze thinks it's getting everything needed.
The relevant bit of source code looks like this:
# [snip]
done_pen = QtGui.QPen(QtCore.Qt.green, pen_size)
not_done_pen = QtGui.QPen(QtCore.Qt.red, pen_size)
pixmap = QtGui.QPixmap.fromImage(image_filename)
qp = QtGui.QPainter(pixmap) # I think this line produces the first error
for i, (x, y) in enumerate(coordinates):
if i in existing:
qp.setPen(done_pen)
else:
qp.setPen(not_done_pen)
# [snip]
self.ui.myLabel.setPixmap(pixmap)
# [snip]
Edit:
I've just been investigating a bit further and it looks like the image isn't being loaded properly, which results in the QPixmap having size 0, 0. Again, I'm not sure how to resolve this: the imageformats folder has been included by cx_freeze, in ./lib/PySide2/Qt/plugins/imageformats. I've tried copying that folder into a few different locations ./plugins/imageformats and ./imageformats but that hasn't helped. I also tried creating a qt.conf file in the same folder as the executable with the following contents, but that didn't help either:
[Paths]
Plugins = lib/PySide2/Qt/plugins
Edit 2:
I've done a bit more investigation and it definitely seems to be specific to the jpeg image plugin. I added this code in:
print(QtGui.QImageReader.supportedImageFormats())
pixmap = QtGui.QPixmap.fromImage(image_filename)
print("Loaded image: ", pixmap.size())
imageReader = QtGui.QImageReader()
imageReader.setFileName(image_filename)
print("Exists: ", os.path.exists(imageReader.fileName()))
print("Can read: ", imageReader.canRead())
print("Reading...")
im = imageReader.read()
if im.isNull():
print("Null image")
print(imageReader.errorString())
else:
print("Okay")
The supportedImageFormats thing produced a long list, with things like bmp, webp and png included but jpg was notable by its absence. The error string that was printed was "Unsupported image format".
I don't know why the jpeg loader isn't working: the imageformats folder contains libjpeg.dll and qjpeg.dll alongside libpng16.dll and qwebp.dll (amongst lots of other files).
Anyway, for now I've just converted the jpeg images to png ones and everything's working as expected. I'll leave this question open in case anyone can answer the specific question of why jpeg might fail after transferring a cx_freeze application to another PC.
I've changed the title accordingly...