Is it possible to iterate a qrc file? [duplicate] - pyqt

I have this estructure on my application:
|-App
|
|-functions
|
|-ui
|--ui.py
|
|images
|
|main.py
i have a functions folder with some scripts and a ui folder with the PyQt generated code on the ui.py file.
and a main.py file that loads the ui.py to show the interface and ui.py loads some images from the "images" folder on the root.
if I execute my script directly on python (double clic on main.py file), the images wont show..
But if I use the terminal with "python main.py" the images show correctly.
The references on ui.py are like:
icon.addPixmap(QtGui.QPixmap(_fromUtf8("images/flags/MXN.png"))

Use the Qt Designer Resource System to create a resource file for the images.
Then use PyQt's pyrcc tool to convert the Qt resource file into a python module.
NB: The python resource module should go in the same directory as your ui files. So if you created the resource file App/resources.qrc in Qt Designer, you should then convert it like this:
pyrcc5 -o App/ui/resources_rc.py App/resources.qrc
The equivalent tool for PySide (Qt4) was pyside-rcc. For PySide2/PySide6, the Qt rcc tool itself has an option to produce python output:
rcc -g python -o App/ui/resources_rc.py App/resources.qrc
If you're not using Qt Designer, see the standalone example below. The following files all go together in the same root directory. If the image files are in a sub-directory, the qrc file must use relative paths (e.g. subdir/image.jpg). The resource_rc module is generated by executing the following command within the root directory:
pyrcc5 -o resource_rc.py resource.qrc
To make the resources accessible, you must import the generated module:
import resource_rc
The files within the resource are then accessed by prefixing ":/" to the path (e.g. ":/image.jpg" in the example below).
resource.qrc
<RCC>
<qresource>
<file>image.jpg</file>
</qresource>
</RCC>
main.py:
from PyQt5 import QtGui, QtWidgets
import resource_rc
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.label = QtWidgets.QLabel()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.label)
self.label.setPixmap(QtGui.QPixmap(':/image.jpg'))
app = QtWidgets.QApplication(['Test'])
window = Window()
window.show()
app.exec_()
UPDATE:
PyQt6 has now removed the pyrcc tool (see: How can resources be provided in PyQt6 (which has no pyrcc)?).

Related

How can I resolve a python3 ModuleNotFoundError in thonny when importing from a subfolder of my project folder

I began developing my python3 application in a single source file, called main.py, but then began to implement sub-windows as classes and moved each class into a separate source file in the Classes subdirectory. The application ran fine as a single source file, but now with all the imports, it is not able to run, due to missing module errors. I want to resolve these errors so that the application can run with the new project directory hierarchy, making it possible to create parallel projects that make use of my Classes library.
I expect my application to run without generating errors.
I'm getting the following message in thonny Assistant when I run main.py:
ModuleNotFoundError: No module named 'ToplevelWindow'
python_backend.py, line 314
However, when I run CalibrationWindow.py in thonny, it shows:
The code in CalibrationWindow.py looks good.
Also, when I run ToplevelWindow.py in thonny, it shows:
The code in ToplevelWindow.py looks good.
I'm using thonny 3.3.10 via ssh hosted by a Raspberry Pi running raspbian buster and python 3.7.3.
My project directory structure is as follows:
gui
\
__init.py__
main.py
Classes
\
__init__.py
CalibrationWindow.py
ToplevelWindow.py
I added the empty __init__.py files, created in nano, in order to try to get the imports working, but have not noticed any improvement.
The source file essential contents are as follows:
# File: main.py
from tkinter import *
import tkinter.ttk as ttk
from Classes.CalibrationWindow import CalibrationWindow
def OpenCalibrationWindow():
CalibrationWindow()
# File: ToplevelWindow.py
import tkinter as tk
class ToplevelWindow():
def __init__(self, title, geometry):
self.window = tk.Tk()
#...
if __name__ == 'main':
obj = ToplevelWindow('Test ToplevelWindow', '600x400')
# File: CalibrationWindow.py
from ToplevelWindow import ToplevelWindow
from tkinter import *
class CalibrationWindow(ToplevelWindow):
def __init__(self, windowTitle, windowGeometry):
super().__init__(windowTitle, windowGeometry)
#...
if __name__ == 'main':
obj = CalibrationWindow('Test CalibrationWindow', '600x400')
The error only seems to appear when a source file within the Classes folder imports from another source file within the Classes folder.
I don't know where to find sys.path or how to set that up, so I don't think I have set up the project correctly for all imports to work right, but it seems the first level of imports is working and it's only the last level where it is going wrong.

Pyinstaller bundled exe not working, but folder exe does [duplicate]

I'm trying to export my .py script to .exe using PyInstaller, which has dependencies on .ui files which were created using Qt Designer.
I can confirm that my .py script works just fine when running it through PyCharm - I'm able to see the GUI I've created with the .ui files.
However, when I export my .py script to .exe and launch it, I recieve the following errors in the command line:
C:\Users\giranm>"C:\Users\giranm\PycharmProjects\PyQt Tutorial\dist\secSearch_demo.exe"
Traceback (most recent call last):
File "secSearch_demo.py", line 13, in <module>
File "site-packages\PyQt4\uic\__init__.py", line 208, in loadUiType
File "site-packages\PyQt4\uic\Compiler\compiler.py", line 140, in compileUi
File "site-packages\PyQt4\uic\uiparser.py", line 974, in parse
File "xml\etree\ElementTree.py", line 1186, in parse
File "xml\etree\ElementTree.py", line 587, in parse
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\giranm\\securitySearchForm.ui'
Failed to execute script secSearch_demo
For some reason, the .exe file is looking for the .ui file within the path - C:\Users\giranm\
However, having done some research already, I was told that I needed to use os.getcwd() and ensure that I have the full path in my script. Even with the code below, I still get errors trying to locate the .ui files.
PyInstaller: IOError: [Errno 2] No such file or directory:
# import relevant modules etc...
cwd = os.getcwd()
securitySearchForm = os.path.join(cwd, "securitySearchForm.ui")
popboxForm = os.path.join(cwd, "popbox.ui")
Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)
# remainder of code below.
I'm aware that one can convert .ui files to .py and import them into the main routine using pyuic4. However, I will be making multiple edits to the .ui files
and thus it is not feasible for me to keep converting them.
Is there anyway to fix this so that I can create a standalone .exe?
I'm fairly new to using PyQT4 and PyInstaller - any help would be much appreciated!
After scratching my head all weekend and looking further on SO, I managed to compile the standalone .exe as expected using the UI files.
Firstly, I defined the following function using this answer
Bundling data files with PyInstaller (--onefile)
# Define function to import external files when using PyInstaller.
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
Next I imported the .UI files using this function and variables for the required classes.
# Import .ui forms for the GUI using function resource_path()
securitySearchForm = resource_path("securitySearchForm.ui")
popboxForm = resource_path("popbox.ui")
Ui_MainWindow, QtBaseClass = uic.loadUiType(securitySearchForm)
Ui_PopBox, QtSubClass = uic.loadUiType(popboxForm)
I then had to create a resource file (.qrc) using Qt Designer and embed images/icons using this resource file. Once done, I used pyrcc4 to convert the .qrc file to .py file, which would be imported in the main script.
Terminal
C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyrcc4 -py3 resources.qrc -o resources_rc.py
Python
import resources_rc
Once I have confirmed the main .py script works, I then created a .spec file using PyInstaller.
Terminal
C:\Users\giranm\PycharmProjects\PyQt Tutorial>pyi-makespec --noconsole --onefile secSearch_demo.py
As per PyInstaller's guide, I've added data files by modifying the above .spec file.
https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files
Finally, I then compiled the .exe using the .spec file from above.
You can simply use:
uic.loadUi(r'E:\Development\Python\your_ui.ui', self)
Use the full path, and use pyinstaller with standard arguments, and it works fine. The r prefix makes sure the backslashes are interpreted literally.
Another method, tested on Ubuntu 20.04 is to add the .ui file to the data section in the spec file. First generate a spec file with pyinstaller --onefile hello.py. Then update the spec file and run pyinstaller hello.spec.
a = Analysis(['hello.py'],
...
datas=[('mainwindow.ui', '.')],
...
The next step is to update the current directory in your Python file. To do this, the os.chdir(sys._MEIPASS) command has to be used. Wrap it in a try-catch for development use when _MEIPASS is not set.
import os
import sys
# Needed for Wayland applications
os.environ["QT_QPA_PLATFORM"] = "xcb"
# Change the current dir to the temporary one created by PyInstaller
try:
os.chdir(sys._MEIPASS)
print(sys._MEIPASS)
except:
pass
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile, QIODevice
if __name__ == "__main__":
app = QApplication(sys.argv)
ui_file_name = "mainwindow.ui"
ui_file = QFile(ui_file_name)
if not ui_file.open(QIODevice.ReadOnly):
print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
sys.exit(-1)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
if not window:
print(loader.errorString())
sys.exit(-1)
window.show()
sys.exit(app.exec_())

Python to EXE - PySide2 and Custom Modules

I'm trying to create an EXE from a Python project I built and I'm running into some issues. I've done this before with PyInstaller for simpler tools and cx-freeze for tools where I use custom modules but the way I used to set it up doesn't seem to be working anymore.
In this particular case I've tried:
cx-freeze==6.10
pyinstaller==4.10
py2exe==0.11.1.0
auto-py-to-exe==2.18.0
and a few others with no luck
Everything works perfectly fine through the Python 3.8 interpreter. I'm assuming it's either because of the way I'm importing PySide2 here (which I don't normally do but did for this project to see if it would speed up my programming time) or that the EXE modules can't find my custom modules. Here is a mock version of my program (names/paths changed for simplicity):
Example folder of my project ("C:\a\MyProjects\Project1"):
Example folder of the custom module I'm using. Each using various other built-in and/or 3rd party python modules. ("C:\a\path\to\external\modules"):
Example of my main file (C:\a\MyProjects\Project1\ui.py) I want to turn into an EXE:
import os
import sys
import colorsys
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
import utils # module in project ("C:\a\MyProjects\Project1\utils.py")
sys.path.append(r"C:\a\path\to\external\modules") # custom module location for MyModule
from MyModule.foo import module1 as foo_mod1
from MyModule.foo import module2 as foo_mod2
from MyModule.bar import module1 as bar_mod1
from MyModule.bar import module2 as bar_mod2
from MyModule.baz import module1 as baz_mod1
from MyModule.baz import module2 as baz_mod2
class MainDialog(QDialog):
[...code...]
[...use of "dark.stylesheet"...]
[...use of "images\image 1.png"...]
[...use of "images\image 2.png"...]
def main():
global win
try: # try to close existing instances
win.close()
except NameError:
pass
win = MainDialog()
win.show()
if __name__ == "__main__":
app = QApplication.instance()
if not app:
app = QApplication(sys.argv)
main()
sys.exit(app.exec_())
Can someone tell me the best module and method to make this into a windowed (console-less), if possible, single file application. This is one of the more complicated tools I've had to make into a desktop app so if I can figure this out I should be good for most of the other things I have to do.
Thanks in advance.
EDIT:
Example of cx-freeze setup.py file in project I tried (C:\a\MyProjects\Project1\setup.py):
from cx_Freeze import setup, Executable
import sys
setup(
name="MyProject",
version="1.0",
options={'build_exe': {
'excludes': ["Tkinter"],
'includes': ["utils", "MyModule.foo", "MyModule.bar", "MyModule.baz"],
'path': sys.path + [r"C:\a\path\to\external\modules"],
'include_files': ["images\image 1.png", "images\image 2.png"],
'packages': ["PySide2.QtWidgets", "PySide2.QtCore", "PySide2.QtGui"]}},
executables=[Executable("ui.py", base="Win32GUI")]
Example of pyinstaller cmd command I tried:
cd C:\\a\MyProjects\Project1
pyinstaller -p "C:\a\path\to\external\modules" --onefile -w ui.py
# even tried this with a custom spec file like:
# pyinstaller --onefile -w ui.spec
There is a command in PyInstaller to include hidden modules call --hidden-module (You can use multile times). So, you can just do
pyinstaller -p "C:\a\path\to\external\modules" --onefile -w --hidden-module="example module 1" -hidden-module="example module 2" ui.py
This seems to have fixed itself after I got a new workstation and installed the latest Python (3.10) and Pyinstaller (5.1) versions.
Also think having your custom Python Libraries in the PYTHONPATH and/or PATH Environment variables may be important for allowing Pyinstaller to find them.

Pygame importing from different location

Game Folder
data
assets
fonts
sprites
player.png
sounds
movement.wav
modules
sprites.py
sounds.py
main.py
This is my pygame application directory. I'm importing sprites.py and sounds.py in main.py with:
from data.modules.sprites import *
from data.modules.sounds import *
In sprites.py file, I want to get file with this directory: Game Folder/data/assets/sprites/player.png but it can't remove directories. When I try to print directory before the error, it prints:
pygame.error: Couldn't open 'data/modules/data/assets/sprites/player.png'
I tried pathlib, os, sys modules. How do I move upper directories with any module?
I'm using Windows.
You need the whole directory. for example C:/Users/user/Documents/Game Folder/data/assets/sprites/player.png

Images not showing when running a frozen pyqt app on another computer

I have a PyQt4 program that I froze using cx_freeze. The problem I am having is when I make a QGraphicsPixmapItem, which it is getting its' pixmap made from a SVG file, the Item gets made no problem, but the Pixmap doesn't load so there is no image just the item in the scene. The thing that confuses me is that this only happens when I am running it on a different computer than the one that built the exe. When I run the exe on the computer that built it the program works perfectly. Even when I try to run it on a computer with all the required python components and pyqt components installed on the computer, if it isn't the computer that built it, the pixmap is not loaded from the svg file. I am not sure if this is a problem with my cx_freeze setup.py file or if I need to change something in the main code so any help or just pointing me in the right direction will be great. My feeling is that something is getting messed up when cx_freeze is building it so I will paste the contents of my setup.py file below. Also I am running on Windows using Python v3.1.
from cx_Freeze import setup, Executable
files = ['drawings\\FULL', 'drawings\\PANEL', 'data.csv', 'panelData.csv']
binIncludes = ['C:\\Python31\\Lib\\site-packages\\PyQt4\\bin\\QtSvg4.dll']
includes = ['main', 'PunchDialog', 'ArrayDialog', 'PricingDialog', 'FontAndInputDialog', 'PanelSelector', 'PyQt4', 'os', 'sys', 'ctypes', 'csv']
packages = ['drawings']
path = ['C:\\Users\\Brock\\Documents\\Programming\\PanelDesigner\\DrawingFirst', 'C:\\Python31\\Lib', 'C:\\Python31\\Lib\\site-packages', 'C:\\Python31\\DLLs']
setup(
name = 'PanelBuilder',
version = '1.0',
description = 'Allows user to draw custom panel layouts.',
author = 'Brock Seabaugh',
options = {'build_exe': {'packages':packages, 'path':path, 'include_files':files, 'bin_includes':binIncludes, 'includes':includes}},
executables = [Executable('PanelBuilder.py')])
PS. Here is my file hierarchy(if that helps at all):
\DrawingFirst
Main .py file
All .py files for all custom dialogs used
\drawings
some modules used
\FULL
A bunch of SVG files used
\PANEL
More SVG files used
This is a nasty problem I have run into myself in the past.
Let me quote http://www.py2exe.org/index.cgi/Py2exeAndPyQt:
(I know you are using cx_freeze but I am sure you can adapt your script)
PyQt4 and image loading (JPG, GIF,
etc)
PyQt4 uses plugins to read those image
formats, so you'll need to copy the
folder PyQt4\plugins\imageformats to
appdir\imageformats. Like in the
above cases, you can use data_files
for this. This won't work with
bundle_files on.
If the plugins are not reachable, then
QPixmap.load/loadFromData will return
False when loading an image in those
formats.
testapp.py:
from PyQt4 import QtGui, QtSvg
import sys
app = QtGui.QApplication([])
wnd = QtSvg.QSvgWidget()
wnd.load("flower.svg")
wnd.show()
sys.exit(app.exec_())
setup.py:
from cx_Freeze import setup, Executable
files = ['flower.svg']
includes = ['sip', 'PyQt4.QtCore']
setup(
name = 'Example',
version = '1.337',
description = 'Allows user to see what I did there.',
author = 'something',
options = {'build_exe': {'include_files':files, 'includes':includes}},
executables = [Executable('testapp.py')])
I created this test app on a Windows 7 machine and copied it over to a Windows XP machine. I did not have to copy any dlls around - it worked just like that.
I've added a hook to cx_freeze that includes imageformats whenever PyQt4.QtGui is included in the original code. With imageformats in the right place, even the externally stored icons work.
https://bitbucket.org/anthony_tuininga/cx_freeze/pull-request/11/added-pyqt4qtgui-load-hook-that-adds/diff
For people coming here from Google: if you only use QtWebKit, you do need to copy the imageformats dir (which you find in PYTHONDIR\lib\site-packages\PyQt4\plugins) into your app dir. Specifying PyQt4.QtWebKit among the includes is not enough.

Resources