Main Window Icon Not Displayed when Frozen with cx_Freeze - python

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:
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:
Suggestions?

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:
Try the same example with a .png file as window icon
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.

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:
Removed import sys
Removed the line exeDir = ...
Removed the line platformsPath = ...
Removed platformsPath from the includes = list

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.

Related

Python to exe but keeping the icon

I'm new to python and I don't know much yet. So, I wanted to convert my python project to an exe file, and so I followed the steps like in every youtube tutorial, terminal, pyinstaller and stuff (I use Pychram), but soon I realized that the icon I set for my exe isn't loading unless it's in the same folder as dist and .spec file. I decided to search some, I found a way on this site: https://clay-atlas.com/us/blog/2020/11/04/python-en-package-pyinstaller-picture/ (3rd method), but i was unable to decode it, I just need to replace something with my filename but I don't understand where exactly, like is pic2str a variable or a project name? Thanks.
(Code might be slightly shifted in here copy from the website if you need)
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')
And this:
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_())
I found a solution,
you just have to use
--onefile
so the .exe file doesn't require any connection to the folder, and use
--icon insertpicturename.ico
to make the file have the icon you need, you can convert an image to .ico using online converters. And, I would also recommend using
--noconsole
if it fits your needs, it just removes the console showing up before opening the file.
you can try auto-py-to-exe, its a convenient little script that will allow you to use pyinstaller from a GUI and it will create the required terminal command for you. you can get it using pip install auto-py-to-exe and launch it from your terminal with the command auto-py-to-exe, you will see a section called Icon where you can open a .ico file through a file broswer window

Launch Python Script with Pythonw from Batch and give it Focus

I'm on Windows 10 and I have a PyQt5 Application I launch using a .bat file to use the venv interpreter.
When I call the script using python my_script.py it opens the main window in focus, but also shows the Python console in background. To get rid of the console, I tried launching it with pythonw my_script.py, but then it silently opens in background.
I tried things like window.setWindowState(Qt.WindowState.WindowActive) or window.setFocus(), but this only makes the icon blink in the task bar. Other Google results said that Windows does not allow programs to easily grab focus anymore, but then again, python can do it on start-up, so I would like to replicate that behavior with pythonw.
Below you can find testing code and the batch file, context was launching it from a custom URI protocol.
# https://stackoverflow.com/a/38205984 to register any protocol for testing
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtCore import Qt
class MainWindow(QMainWindow):
def __init__(self, title):
super().__init__()
self.setWindowTitle("Test App")
label = QLabel(title)
self.setCentralWidget(label)
if __name__ == '__main__':
if len(sys.argv) == 1:
the_title = "I got no arguments"
else:
the_title = f"I was run with argument {sys.argv[1]}"
app = QApplication(sys.argv)
window = MainWindow(the_title)
window.show()
window.setFocus()
app.exec()
and
cd %~dp0
call ..\venv\Scripts\activate
start "" "pythonw" "test_url_scheme_one.py" "%1"
deactivate
Replacing the batch file with the following fixed it:
start "" "C:\path\to\venv\Scripts\pythonw.exe" "C:\path\to\my_script.py" "%1"
Now the window gets started in foreground and in focus.

tkinter exe added to startup shows console window

I made a python application in python.
Swaminarayan.py
import tkinter as Tkinter
def main():
top = Tkinter.Tk()
top.geometry("500x500")
B = Tkinter.Button(top, text ="Ghanshyam Maharaj")
B.pack()
top.mainloop()
if __name__ == '__main__':
main()
I converted this code to .exe using cx_freeze in virtual environment
setup.py
import sys
from cx_Freeze import setup, Executable
build_exe_options = {"packages": ["os"], "excludes": []}
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup(
name = "Swaminarayan",
url='https://github.com/ajinzrathod/Swaminarayan',
version = "0.1",
description = "My GUI application!",
options = {
"build_exe": build_exe_options,
"bdist_msi": {
'install_icon': r"F:\executable-python-windows\Swaminarayan\icon.ico",
}
},
executables = [
Executable("Swaminarayan.py",
base=base,
icon=r"path\to\icon.ico",
shortcutName='Swaminarayan',
shortcutDir='DesktopFolder',
)
]
)
and then created MS Installer
py setup.py bdist_msi
This creates an installer and when I installed Swaminarayan.exe, it opens normally without a console window.
I wanted my Swaminarayan Application to start at boot.
So I created a batch file in C:\Users\Ajinkya Rathod\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup named open.bat
And pasted the following code
"C:\Users\Ajinkya Rathod\AppData\Local\Programs\Swaminarayan\Swaminarayan.exe"
Which is the path to my installed Swaminarayan.exe
It works perfectly till here.
The main problem is here. When I double-click on the batch file, it also loads a console window. I just want the GUI created with Tkinter. But it also loads the console window.
I can't find the source but some StackOverflow answers asked to used start in a batch script like this
start "C:\Users\Ajinkya Rathod\AppData\Local\Programs\Swaminarayan\Swaminarayan.exe"
But the problem is that including start in batch scripts removes the console window along with the GUI application. I don't want my GUI to vanish. I just want to get rid of Console Window.
Trying this since long time. Any help would be appreciable.
According to Microsoft's documentaiton, start's "title" is optional, but depending on the options used, it can become an issue, like you are experiencing here. Currently your script is starting cmd with a title of the script path.
So include the empty title ""
start "" "%localappdata%\Programs\Swaminarayan\Swaminarayan.exe"

QTableView items was draw differently in PyQt 5.8 and 5.9

It happened when I use the following code on win7 32bit,
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
if __name__ == '__main__':
app = QApplication(sys.argv)
# ui = QMainWindow()
path = r'D:\BaiduYunDownload\untitled'
model = QFileSystemModel()
model.setRootPath(path)
table = QTableView()
table.setModel(model)
table.setRootIndex(model.index(path))
# ui.setCentralWidget(table)
table.resize(800, 600)
table.show()
viewOptions = table.viewOptions()
print(table.wordWrap(),
int(viewOptions.textElideMode),
int(viewOptions.decorationAlignment),
int(viewOptions.displayAlignment),
int(viewOptions.features))
app.exec_()
there is only one file with the long name(for test):
A directory model that displays the contents of a default directory is usually constructed with a parent object.txt
in dir D:\BaiduYunDownload\untitled
In PyQt 5.8 (installed via pip3 install pyqt5==5.8)
In PyQt 5.9.1 (installed via pip3 install pyqt5==5.9)
I wonder why the QTableView item in the Name column was draw differently ? I checked with the following property in the code, all returned the same value in both version of PyQt .
print(table.wordWrap(),
int(viewOptions.textElideMode),
int(viewOptions.decorationAlignment),
int(viewOptions.displayAlignment),
int(viewOptions.features))
That appears to be a Qt bug. Check out Qt's bug report here.
One workaround consists in setting font properties via CSS.

Cx_freeze exe troubleshooting

I wrote an application using wxpython and boa constructor. This application stores class instances in ordered dictionaries (I import odict) and ultimately stores the data in a shelve on the local machine.
The application runs as I expect, so I want to distribute it.
Previously, I have used pyinstaller but have learned that python 2.6 is not fully supported at this time (verified by me because my *exe didn't work) so I switched to cx_freeze. My newly compiled exe seems to run fine, but does not create the shelve file. Looking through the library file in the build folder, I do not see the odict module. I do see shelve. Seems like this is the problem but I don't know why odict is not automatically included. I get no errors when running the application so I'm not really sure how to go about finding the problem. Any tips or suggestions would be sincerely appreciated.
Using python 2.6.6, wx python 2.8.11, cx_freeze 4.2.2 on windows XP.
I wrote this example to try and determine if it would write the shelve file and it doesn't work after running cx_freeze....
import wx
import sys
import os
import shelve
def create(parent):
return Frame1(parent)
[wxID_FRAME1, wxID_FRAME1BUTTON1,
] = [wx.NewId() for _init_ctrls in range(2)]
class Frame1(wx.Frame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
pos=wx.Point(557, 369), size=wx.Size(400, 250),
style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
self.SetClientSize(wx.Size(392, 223))
self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label='button1',
name='button1', parent=self, pos=wx.Point(0, 0), size=wx.Size(392,
223), style=0)
self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
id=wxID_FRAME1BUTTON1)
def __init__(self, parent):
self._init_ctrls(parent)
def OnButton1Button(self, event):
filename='c:\\MakeAShelve.db'
data=[1,2,3,4]
database=shelve.open(filename)
database['data']=data
database.close()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = create(None)
frame.Show()
app.MainLoop()
The setup I ran is below and executed as python setup.py build
import sys
from cx_Freeze import setup,Executable
includefiles=[]
exe=Executable(
script="ShelveTesting.py",
base="Win32Gui",
)
setup(
name="TimingDiagram",
version="0.2",
description="An Excel Based Timing Diagram Application",
options={'build_exe':{'include_files':includefiles}},
executables=[exe]
)
You can always manually include modules like this
build_exe_options = {'packages': ['os','sys','shelve'],'include_files':includefiles}
options = {"build_exe": build_exe_options}
NOTE!!
When using wxpython some special care need to be made.
http://wiki.wxpython.org/cx_freeze

Categories

Resources