slalomchip
slalomchip

Reputation: 909

Main Window Icon Not Displayed when Frozen with cx_Freeze

I want to display a custom icon in a PyQt window after freezing the baseline with cx_Freeze. The icon displays fine when the unfrozen script is executed from within the IDE (Spyder, for me). I'm using PyQt5, Python 3.6, and Windows 10. Here is my Python script (IconTest.py) that creates a main window and shows the path to the icon and whether the path exists. The icon file needs to be in the same directory as IconTest.py:

import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import QIcon

class Example(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):        
        self.setGeometry(200, 300, 600, 100)
        if getattr(sys, 'frozen', False): #If frozen with cx_Freeze
            self.homePath = os.path.dirname(sys.executable)
        else: # Otherwise, if running as a script (e.g., within Spyder)
            self.homePath = os.path.dirname(__file__)
        self.iconFileName = os.path.join(self.homePath, 'myIcon.ico')
        self.setWindowIcon(QIcon(self.iconFileName))        
        self.setWindowTitle('Icon')
        self.label1 = QLabel(self)
        self.label2 = QLabel(self)
        self.label1.move(10, 20)
        self.label2.move(10, 40)
        self.label1.setText("Path to icon file: " + str(self.iconFileName))
        self.label2.setText("Does file exit?  " + str(os.path.exists(self.iconFileName)))
        self.show()

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

Here is my result when running the script from within Spyder (unfrozen). As you can see, there is an icon displayed that resembles a stopwatch: enter image description here

Here is my setup.py for creating the frozen baseline:

from cx_Freeze import setup, Executable
import os, sys

exeDir = os.path.dirname(sys.executable)
platformsPath = os.path.join(exeDir, "Library\\Plugins\\Platforms\\")
iconPath = os.path.join(os.path.dirname(__file__), "myIcon.ico")
exe=Executable(script="IconTest.py", base = "Win32GUI", icon = iconPath)
includes=[iconPath, platformsPath]
excludes=[]
packages=[]
setup(
     version = "0.1",
     description = "My Icon Demo",
     options = {'build_exe': {'excludes':excludes,'packages':packages,'include_files':includes}},
     executables = [exe]
     )

Here is my result when running the frozen script (the executable in the build directory). As you can see, the stopwatch icon is replaced with a generic windows icon:

enter image description here

Suggestions?

Upvotes: 2

Views: 985

Answers (3)

Saren Tasciyan
Saren Tasciyan

Reputation: 483

I had a similar problem and posted an answer here.

Basically, I have overcome this issue by storing the file in a python byte array and loading it via QPixmap into a QIcon.

Upvotes: 0

slalomchip
slalomchip

Reputation: 909

ANSWER: I have been using the Anaconda platform and read in other posts that there are issues between PyInstaller and Anaconda because of the way Anaconda structures its content. Thinking the same issue might exist with cx_Freeze, I installed Python (no Anaconda) on a different machine and froze the script from this new Python installation. The icon appeared as expected in the frozen script. To make the icon display properly, I made the following changes to the setup.py script:

  1. Removed import sys
  2. Removed the line exeDir = ...
  3. Removed the line platformsPath = ...
  4. Removed platformsPath from the includes = list

Upvotes: 1

jpeg
jpeg

Reputation: 2461

Interesting question and nice minimal example. After some searching I guess it could have to do with PyQt5 missing a plugin/DLL to display .ico image files in the frozen application. See e.g. How to load .ico files in PyQt4 from network.

If this is true, you have 2 options:

  1. Try the same example with a .png file as window icon

  2. If the plugins directory is included in the frozen application but it cannot find it, try to add the following statements

    pyqt_dir = os.path.dirname(PyQt5.__file__)
    QApplication.addLibraryPath(os.path.join(pyqt_dir, "plugins"))`
    

    before

    app = QApplication(sys.argv)
    

    in your main script. See this answer.

    If the plugins directory is not included in the frozen application, you need to tell cx_Freeze to include it using the include_files entry of the build_exe option. Either you manage to dynamically let your setup script include it at the place where PyQt5 is looking for it, using a tuple (source_path_to_plugins, destination_path_to_plugins) in include_files, or you tell PyQt5 where to look for it, using QApplication.addLibraryPath.

    In your previous question to this issue you actually had an entry to include a Plugins\\Platforms directory in your setup script, maybe you simply need to repair this include. Please note that cx_Freeze version 5.1.1 (current) and 5.1.0 move all packages into a lib subdirectory of the build directory, in contrary to the other versions.

Upvotes: 1

Related Questions