I have a python application which runs under python3.6 and is using PyQt5 for loading Ui windows. These windows were created with Qt Designer 5.9.4. The Code below shows a working example with PyQt5.
Now i want to have exactly the same functionality but with PySide2. For now, I couldn't work out how to load an Ui File and use its objects (buttons, tables etc.) in a separate class. For example: by clicking a button in the first window/class, a second window apears which functions are defined in a separate class, see example. All examples that I found, just load an Ui-Window but don't show how to work with it. Can anyone help?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from PyQt5.uic import loadUiType
from PyQt5 import QtGui, QtCore
Ui_FirstWindow, QFirstWindow = loadUiType('first_window.ui')
Ui_SecondWindow, QSecondWindow = loadUiType('second_window.ui')
class First(Ui_FirstWindow, QFirstWindow):
def __init__(self):
super(First, self).__init__()
self.setupUi(self)
self.button.clicked.connect(self.show_second_window)
def show_second_window(self):
self.Second = Second()
self.Second.show()
class Second(Ui_SecondWindow, QSecondWindow):
def __init__(self):
super(Second, self).__init__()
self.setupUi(self)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = First()
main.show()
sys.exit(app.exec_())
PySide does not offer these methods, but one solution is to modify the source code of the PyQt uic module by changing the imports from PyQt5 to PySide2, for legal terms do not modify the license, in addition to the code that will keep the PyQt licenses.
To do this, download the source code from the following link and unzip it.
And execute the following script:
convert_pyqt5_to_pyside2.py
import os
import fileinput
import argparse
import shutil
def modify_source_code(directory, text_to_search, replacement_text):
for path, subdirs, files in os.walk(directory):
for name in files:
filename = os.path.join(path, name)
with fileinput.FileInput(filename, inplace=True) as file:
for line in file:
if line.startswith('#'):
# not change on comments
print(line, end='')
else:
print(line.replace(text_to_search, replacement_text), end='')
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help="Input directory")
parser.add_argument("-o", "--output", help="Output directory")
args = parser.parse_args()
if args.input and args.output:
input_dir = os.path.join(os.path.abspath(args.input), "pyuic", "uic")
output_dir = os.path.abspath(args.output)
shutil.copytree(input_dir, output_dir)
modify_source_code(output_dir, 'PyQt5', 'PySide2')
if __name__ == '__main__':
main()
Using the following command:
python convert_pyqt5_to_pyside2.py -i /path/of/PyQt5-folder -o fakeuic
Then you can use the loadUiType method from fakeuic:
from fakeuic import loadUiType
from PySide2 import QtCore, QtGui, QtWidgets
Ui_FirstWindow, QFirstWindow = loadUiType('first_window.ui')
Ui_SecondWindow, QSecondWindow = loadUiType('second_window.ui')
class First(QFirstWindow, Ui_FirstWindow):
def __init__(self):
super(First, self).__init__()
self.setupUi(self)
self.button.clicked.connect(self.show_second_window)
def show_second_window(self):
self.Second = Second()
self.Second.show()
class Second(QSecondWindow, Ui_SecondWindow):
def __init__(self):
super(Second, self).__init__()
self.setupUi(self)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = First()
main.show()
sys.exit(app.exec_())
You can find the complete example here
PySide2 brought back loadUiType in May 2020. So if you upgrade, you can get a drop-in replacement. The only difference is the import:
from PySide2.QtUiTools import loadUiType
Syntax is the same (you will use loadUiType(<file>)[0] )
Follow these simple steps:
Assuming the ui file from qt designer is mycode.ui, convert this to the py file using the pyside2 ui converter by typing "pyside2-uic mycode.ui -o mycode.py" without the quotes. (Note use the pyside2 converter of pyside2-uic and not pyqt5 converter of pyuic5)
With mycode.py generated by pyside2 format, just replace all the headers for the PyQt5 code to "import sys" and "from mycode import *"
You are done...Hope this helps
Related
I have a fresh QtCreator installation, and I set it up to run using a fresh install of Python3.8 on which I pip-installed both pyside2 and pyside6.
When I create a new Qt for Python - Window (UI file) application, whatever I do to the UI file the window always shows up empty and with the default size when I run the app.
I've tried with a QDialog, QMainApplication, using Pyside2 or Pyside6, I've checked that it was correctly loading the UI (and the right one) - no dice. It just won't update, and appears not to have any reason not to.
Default code for completeness:
# This Python file uses the following encoding: utf-8
import os
from pathlib import Path
import sys
from PySide2.QtWidgets import QApplication, QDialog
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
class Dialog(QDialog):
def __init__(self):
super(Dialog, self).__init__()
self.load_ui()
def load_ui(self):
loader = QUiLoader()
path = os.fspath(Path(__file__).resolve().parent / "form.ui")
ui_file = QFile(path)
ui_file.open(QFile.ReadOnly)
loader.load(ui_file, self)
ui_file.close()
if __name__ == "__main__":
app = QApplication([])
widget = Dialog()
widget.show()
sys.exit(app.exec_())
(In the UI I just drag-dropped a button right in the middle and saved the file)
Am I forgetting something fundamental? I'm only used to programming in C++ using QtCreator.
I was expecting this to work right out of the box, but it's not.
This only dynamically load the UI as a new widget, with this custom class as a parent.
If I want signals and slots to work, the only thing I've found was to add a custom build step:
Command: <path to pyside6 install, use pip show to know where>\uic.exe
Arguments: <filename of the .ui file> -o <the python translation .py
which will serve as baseclass> -g python
Working directory: [your project dir]
Then signals and slots need to be connected manually, so QtCreator really only enables you to draw the user interface but all the logic still needs to be done by hand. Component variables are normally named after their UI name, but you can see for yourself in the baseclass file. This is a big step back from how QtCreator is used in C++ ("go to slot" will not work).
Code to use:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QFile
from PySide6.QtUiTools import QUiLoader
from YourGenPyFileName import Ui_YourUIName
class MainWindow(QMainWindow, Ui_YourUIName):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication([])
widget = MainWindow()
widget.show()
sys.exit(app.exec_())
I wrote below code
import sys,time
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
app = QApplication(sys.argv)
sys.path.append(r"C:\Users\hpaalm\Desktop")
a=QPushButton()
a.setIcon(QIcon('1.png'))
a.show()
app.exec_()
when i run it in IDE, it show my icon, but when run it in CMD it not show icon. what is problem?
python C:\Users\hpaalm\Desktop\a.py
sys.path contains a list of paths where python imports the modules, this does not serve to import files, icons or similar resources. Instead it is best to create a function that binds the directory path with the filename and return the full path of the icon:
import os
import sys
from PyQt5 import QtGui, QtWidgets
ICON_DIR = r"C:\Users\hpaalm\Desktop"
def get_path_icon(filename):
return os.path.join(ICON_DIR, filename)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
a = QtWidgets.QPushButton()
a.setIcon(QtGui.QIcon(get_path_icon('1.png')))
a.show()
sys.exit(app.exec_())
I tried compiling my .ui file via this:
pyuic4 gui.ui > gui.py
then I tried importing it, but I get:
ImportError: cannot import name GUI
I tried plenty of tutorials but none of them were helpful, they were all linux.
Is there a way I could modify my gui.py and use it like a normal program?
EDIT:
Figured it out!
much easier then I thought
import sys
from PyQt4 import QtCore, QtGui, uic
form_class = uic.loadUiType("gui.ui")[0] # Load the UI
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
If your want to run directly your ui form without subclassing it you can use '-x' parameter with uic;
pyuic4 -x gui.ui -o gui.py
I have just designed my application inside pyQt Designer 5, generated my main.ui to main.py and my assets.qrc to assets_rc.py. No errors, when I now run main.py from my terminal nothing happends. Have I missed a step? Am I supposed to edit my main.py file now?
Cheers!
Python 3.3.0
pyQT 5
This is for PyQt4, but should be the same for PyQt5.
Let's say your ui is called "mainwindow.ui". Compile this with pyuic4 into "mainWindowUi.py" (or whatever, just stick to the name).
Now create a file "mainWindow.py" with more or less this content:
from PyQt4 import QtGui
from mainWindowUi import Ui_MainWindow #same name as appears in mainWindowUi.py
class MainWindow (QtGui.QMainWindow): #Or wherever you are inheriting from
def __init__ (self, parent = None):
super (MainWindow, self).__init__ ()
self.ui = Ui_MainWindow () #same name as appears in mainWindowUi.py
self.ui.setupUi (self)
#implement slots and signals and other funny things
Now create a file "program.py" with more or less this content:
#! /usr/bin/python3.3
import sys
from PyQt4 import QtGui
from mainWindow import MainWindow
def main():
app = QtGui.QApplication (sys.argv)
m = MainWindow ()
m.show ()
sys.exit (app.exec_ () )
if __name__ == '__main__':
main ()
Run the program.py file. This is more or less the skeleton of a Qt application.
I am looking for a simple example of how to directly load a QtDesigner generated .ui file into a Python application.
I simply would like to avoid using pyuic4.
For the complete noobs at PySide and .ui files, here is a complete example:
from PySide import QtCore, QtGui, QtUiTools
def loadUiWidget(uifilename, parent=None):
loader = QtUiTools.QUiLoader()
uifile = QtCore.QFile(uifilename)
uifile.open(QtCore.QFile.ReadOnly)
ui = loader.load(uifile, parent)
uifile.close()
return ui
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = loadUiWidget(":/forms/myform.ui")
MainWindow.show()
sys.exit(app.exec_())
PySide, unlike PyQt, has implemented the QUiLoader class to directly read in .ui files.
From the linked documentation,
loader = QUiLoader()
file = QFile(":/forms/myform.ui")
file.open(QFile.ReadOnly)
myWidget = loader.load(file, self)
file.close()
Another variant, based on a shorter load directive, found on https://askubuntu.com/questions/140740/should-i-use-pyqt-or-pyside-for-a-new-qt-project#comment248297_141641. (Basically, you can avoid all that file opening and closing.)
import sys
from PySide import QtUiTools
from PySide.QtGui import *
app = QApplication(sys.argv)
window = QtUiTools.QUiLoader().load("filename.ui")
window.show()
sys.exit(app.exec_())
Notes:
filename.ui should be in the same folder as your .py file.
You may want to use if __name__ == "__main__": as outlined in BarryPye's answer
Here is some example for PySide6 and Windows. (For linux you need use /, not \\)
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile
from PySide6.QtWidgets import QApplication
import sys
if __name__ == "__main__":
app = QApplication(sys.argv)
loader = QUiLoader()
file = QFile("gui.ui")
file.open(QFile.ReadOnly)
ui = loader.load(file)
file.close()
ui.show()
sys.exit(app.exec_())
For people coming from PyQt5/6 who are thoroughly confused by this:
PySide does not have the same functionality that we're used to, which is to load the ui file at the top of the window/widget subclass like so:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
'''Load ui'''
uic.loadUi("mainwindow.ui", self)
There is nothing very similar to this in PySide.
Instead, the best thing to do is embrace the ui-file compilation that you've avoided because loading the ui file is so easy in PyQt. This has a couple of advantages
There is a performance advantage - the ui file does not need to be compiled at run time
You get type hints for all your widgets without needing to manually add them
The disadvantage is that you have to use pyside6-uic to compile the *.ui files each time you edit them, but this can be made less painful by using scripts to automate the process - either setting it up in VSCode, a batch file or a powershell script. After you've done this, the code is nice:
#ui_mainwindow is the ui_mainwindow.py file
#Ui_MainWindow is the class that was produced within that .py file
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
'''Load the ui'''
self.setupUi(self)