correct way to integrate GUI - python

I have a question that has been around my mind for a while.
Lets say that I have designed my GUI with Qt designer, so I would have the .iu file that I would convert into .py file like this:
pyuic4 mainGUI.ui -o mainGUI.py
So now, If I do this I have all the necessary to access widgets and etc etc
main.py file:
from PyQt4 import QtGui
from mainGUI import Ui_mainGUI
class MyMainWindow(Ui_mainGUI):
def __init__(self, *args, **kwargs)
super(MyMainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
My question now is that, if I have all my so called worker class in another .py file, how do I integrate all together to make my GUI show results, or show progress etc all these kind of thigs.
A simple-fast example of worker class:
myworker.py file:
import numpy as np
import pandas as pd
class worker():
def sum2numbers(self, a,b):
return a+b
....
....
Should class MyMainWindow inherit?? im a bit confused:

Just import your worker class module to your main file and use it there. Your MyMainWindow class should also inherit QMainWindow so you can access all the underlying Qt widget methods. Using your example:
from PyQt4 import QtGui
from mainGUI import Ui_mainGUI
from myworker import worker
class MyMainWindow(QMainWindow, Ui_mainGUI):
def __init__(self, *args, **kwargs)
super(MyMainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)

This Link here explains quite well the ways you can connect the Ui file with your main worker file where YourFormName is the name of the file you created from your UI. I personally prefer the single inheritence stated in that link, then you can change your Ui file whenever you want without it affecting your main worker file. Here is what I do.
from Your_ui import Ui_MainWindow
import sys
from PyQt4 import QtGui,QtCore
class Worker(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow() #Creating your Ui object
self.ui.setupUi(self) #calling your Ui.
#use a function if you want to modify your ui that designer doesn't provide....
self.my_worker_stuff()
def my_worker_stuff(self):
#Now you can access your Ui widgets using the 'self.ui'
#for eg.
myText = self.ui.lineEdit.text()
Here is how your __main__ would look
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
worker = Worker()
worker.show()
sys.exit(app.exec_())

Related

update the progressBar value from emitted signal

getting the CPU percentage from psutil and display it on a progress bar.
i was able to display the value of the CPU percentage but it's not updating.
i have a file from QT designer called Progress i convert it using pyuic5 to py file and import it in the main file called cpuprogress.py, also i created a python file called sysinfo to get the value of CPU percentage and import it to the main file.
import sys
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import (QWidget, QApplication)
from progress import Ui_Form
import sysinfo
class MyTest(QWidget, Ui_Form):
def __init__(self, parent = None):
super(MyTest, self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadCLass()
self.threadclass.start()
self.threadclass.change_value.connect(self.updateProgressBar)
def updateProgressBar(self, val):
self.progressBar.setValue(val)
class ThreadCLass(QThread):
change_value = pyqtSignal(int)
def __init__(self, parent = None):
super(ThreadCLass, self).__init__(parent)
def run(self):
while 1:
val = int(sysinfo.getCPU())
self.change_value.emit(val)
a =QApplication(sys.argv)
window = QWidget()
app = MyTest()
app.setupUi(window)
app.show()
sys.exit(a.exec_())
enter image description here
tl;dr
The problem is that you called setupUi twice, and the second time it was using another widget as argument.
Why doesn't it work?
When you generate a file with pyuic, it actually creates a class, based on Python's object type. That class doesn't do anything on itself, is just an "interface" to load the elements of the ui in a "pythonic way".
If you try to open one of those files (which should never be edited!), you'll see something like this:
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
self.progressBar = QtWidgets.QProgressBar(Form)
self.progressBar.setProperty("value", 24)
# ...
def retranslateUi(self, Form):
# things related to the localization of the ui
The official guide on using Designer reports as a first example that actually does nothing, besides showing the window.
from PyQt5.QtWidgets import QApplication, QWidget
from ui_form import Ui_Form
# create an instance of the base class
window = QWidget()
# create an instance of the Ui "builder"
ui = Ui_Form()
# apply the ui to the base class created before
ui.setupUi(window)
The last step is the most important to understand: look carefully to what object the setupUi method belongs, and its argument, then go back to the contents of the file created with pyuic:
def setupUi(self, Form):
Form.setObjectName("Form")
self.progressBar = QtWidgets.QProgressBar(Form)
Form is the QWidget instance created before ("window"), while obviously self refers to the instance of Ui_Form; let's translate the variables with the name of the instancies they represent:
def setupUi(ui, window):
window.setObjectName("Form")
ui.progressBar = QtWidgets.QProgressBar(window)
In the example at the beginning it means that, while window is displayed with its children widgets, they are not attributes of the instance: there's no window.progressBar.
The second example in the documentation shows the "single inheritance" method, that allows the implementation of interactions between widgets (the "logic"). I'll use the class names as I did above:
from PyQt5.QtWidgets import QApplication, QWidget
from ui_form import Ui_Form
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
Now ui is an attribute of the window instance; let's "translate" setupUi once again, assuming that now "self" is the instance of MyWidget that is being created:
class Ui_Form(object):
def setupUi(window.ui, window):
window.setObjectName("Form")
window.ui.progressBar = QtWidgets.QProgressBar(window)
This means that now you can have access to the widgets from the window instance, but only through self.ui (as in, window.ui).
Now, let's see the multiple inheritance approach, which is very similar:
class MyWidget(QWidget, Ui_Form):
def __init__(self):
super(MyWidget, self).__init__()
self.setupUi(self)
In this case, MyWidget inherits from the methods and attributes of both Qwidget and Ui_Form. Let's translate it again (note the class):
class MyWidget(object):
def setupUi(window, window):
window.setObjectName("Form")
window.progressBar = QtWidgets.QProgressBar(window)
This approach makes all widgets as direct attributes of the instance (self.progressBar, etc), and I usually suggest it as it's more direct and simple (it often happens that you try to access a widget and forget that ui prefix, with this method it doesn't happen).
Now, finally, your problem.
window = QWidget()
app = MyTest()
setupUi is called in the __init__ of MyTest, which means that app has an attribute called progressBar, which at this point should be "visible" as soon as we call app.show().
You also created another widget, though (window), and used setupUi using that as parameter:
app.setupUi(window)
Let's translate it one last time (to avoid confusion, let's change the name of the "window" QWidget you created by mistake):
def setupUi(app, otherWindow):
otherWindow.setObjectName("Form")
app.progressBar = QtWidgets.QProgressBar(otherWindow)
As you can see, now you've "overwritten" the app.progressBar attribute, but that progress bar is actually a new one, and it is a child of window (which you never show).
The updateProgressBar function then will successfully modify the value of the progress bar, but not the one you see.
Finally, there's also another way of using ui files, and it is through the uic module, which can directly load ui files.
from PyQt5 import QtWidgets, uic
class MyTest(QtWidgets.QWidget):
def __init__(self, parent=None)
super().__init__(parent)
uic.loadUi('mywindow.ui', self)
This approach as a big advantage on the other: you don't have to create files with pyuic anymore, and it behaves exactly as the multiple inheritance method, so you can use self.progressBar as you did before.

Best way to handle multiple windows Qt4 python

I have got two main-windows which will run all the time and open other windows. Every window has to communicate with each other. All windows are created with qt designer and as I read in another question I should not modify these files, so I tried to make a new class (communicator) to handle all the jobs.
What is the best way to structure a project like this. So how can i communicate best between these windows now, e.g. if I press a button in 'self.mainWindows_images' I want to do something in 'self.mainWindows_classifier'
from classifier import Form as Form_classifier
from cnn_avg import Form as Form_cnn_avg
from images import Form as Form_images
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Communicator():
def __init__(self, parent=None):
self.mainWindow_images = Form_images()
self.mainWindow_images.show()
self.mainWindow_classifier = Form_classifier()
self.mainWindow_classifier.show()
def main():
app = QApplication(sys.argv)
app.setStyle('cleanlooks')
comm = Communicator()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
One of the window files, generated by Qt Designer, looks like with the additional -w flag:
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8("Form"))
Form.resize(1145, 654)
...
class Form(QtGui.QWidget, Ui_Form):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QWidget.__init__(self, parent, f)
self.setupUi(self)
I want to do something like this and add the setImage-method to the mainWindow_classifier, becaus as I mentioned I should not modify the generated qt python file:
self.mainWindow_classifier.pushButton.clicked.connect(self.setImage)
def setImage(self):
# change QLabel in self.mainWindow_images

Is it possible to have PySide's QUIloader act like PyQt's uic.loadUi? [duplicate]

This question already has an answer here:
How do I load children from .ui file in PySide?
(1 answer)
Closed 12 months ago.
So I have an application where I'm considering moving from PyQt4 to PySide. In this application, I use .ui files pretty frequently, with the following usage pattern:
class BaseGUIWidget(QWidget):
def __init__(self, parent = None, ui_file = None):
'''
:param parent: parent widget of this widget
:param ui_file: path to UI file to load (optional)
'''
super(BaseGUIWidget, self).__init__(parent)
if ui_file is not None:
uic.loadUi(ui_file, self)
Let's assume I have similar classes for QFrame, QMainWindow, QGroupBox, etc.
This allows me to create python classes that use the data from the UI file, as well as any additional functionality I add manually in the code. Essentially, my BaseGUIWidget class acts as if it was extending the class generated by the UI file. A lot of the functionality in the application is pretty reliant on this behavior.
However, from what I can tell, PySide's QUIloader doesn't have a similar functionality. Instead of 'shimming' the UI file's contents into your class, it simply builds a widget from the UI file internally, then returns it, and you then embed it into your widget in a layout like you would any other widget., I.E:
class BaseGUIWidget(QWidget):
def __init__(self, parent = None, ui_file = None):
'''
:param parent: parent widget of this widget
:param ui_file: path to UI file to load (optional)
'''
super(BaseGUIWidget, self).__init__(parent)
self.setLayout(QVBoxLayout())
if ui_file is not None:
loader = QUILoader()
uifile = QFile(ui_file)
uifile.open(QFile.ReadOnly)
self.ui_widget = loader.load(ui_file, self)
self.layout().addWidget(self.ui_widget)
uifile.close()
This is a pretty massive difference. If, for example, you wanted your UI file to contain a QMainWindow and your python class to still be an extension of QMainWindow so it acts like one to other classes, you'd wind up with a QMainWindow inside a QMainWindow, which isn't what you want. It also means you have to do widget.ui_widget.XXX to access the widgets produced by the UI file.
Is there any way to make PySide's uic implementation act like PyQt's?
use QtPy package
pip3 install QtPy
it autodetect used binding(pyqt5 or pyside2)
from QtPy import uic # and all other QT modules
w = uic.loadUi("mywidget.ui")
w.show()
Yes it is possible.
I know it is old question, but suddenly someone will come in handy.
I found this solution on:
https://robonobodojo.wordpress.com/2017/10/03/loading-a-pyside-ui-via-a-class/
ui_loader.py:
#!/usr/bin/env python
from PySide.QtUiTools import QUiLoader
from PySide.QtCore import QMetaObject
class UiLoader(QUiLoader):
def __init__(self, base_instance):
QUiLoader.__init__(self, base_instance)
self.base_instance = base_instance
def createWidget(self, class_name, parent=None, name=''):
if parent is None and self.base_instance:
return self.base_instance
else:
# create a new widget for child widgets
widget = QUiLoader.createWidget(self, class_name, parent, name)
if self.base_instance:
setattr(self.base_instance, name, widget)
return widget
def load_ui(ui_file, base_instance=None):
loader = UiLoader(base_instance)
widget = loader.load(ui_file)
QMetaObject.connectSlotsByName(widget)
return widget
example:
#!/usr/bin/env python
from PySide import QtGui
from ui_loader import load_ui
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
load_ui('my_interface.ui', self)
def main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
Actually I was wondering about the same thing and just tried this:
Copy the uic package from a PyQt4 installation to your PySide package(its all python modules, no compiled libraries, so there should not be any incompatibilities)
Do a search & replace on the uic package, replacing "PyQt4" with "PySide"
Use it:
from PySide import uic
w = uic.loadUi("mywidget.ui")
w.show()
It also worked for me with the use case you describe (uic.loadUi(ui_file, self)) :)
Of course there is no guarantee that everything will work, but I was surprised that this hack made it work.

Running python files

I am working on pyqt4 and python26 application.I created forms using qt designer (.ui files).
I converted them to .py and .pyc files.But when i try to run .py file ,python command line comes and goes within a second,the form (corresponding .ui file) cannot be seen...what can be the problem??
this is my code:(.py file)
from DlgAbout_ui import Ui_DlgAbout
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import resources
class DlgAbout(QDialog, Ui_DlgAbout):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)
self.logo.setPixmap( QPixmap( ":/icons/faunalia_logo.png" ) )
text = self.txt.toHtml()
text = text.replace( "$PLUGIN_NAME$", "RT Sql Layer" )
self.txt.setHtml(text)
First, don't use:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
Instead:
from PyQt4 import QtCore, QtGui
And reference the modules explicitly.
class DlgAbout(QtGui.QDialog, Ui_DlgAbout):
etc.
In your code, all you've done is defined a dialog box. You haven't defined any main application to run, or any way to show the dialog box.
For an example, here's a basic main application to run:
from PyQt4 import QtGui
import sys
class MyMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.form_widget = FormWidget(self)
self.setCentralWidget(self.form_widget)
class FormWidget(QtGui.QWidget):
def __init__(self, parent):
super(FormWidget, self).__init__(parent)
self.layout = QtGui.QVBoxLayout(self)
self.button = QtGui.QPushButton("Button!")
self.layout.addWidget(self.button)
if __name__ == "__main__":
app = QtGui.QApplication([])
foo = MyMainWindow()
foo.show()
sys.exit(app.exec_())
This defines a main window, and a form (Which MyMainWindow sets up, as you can see).
I then check if this is the main file being run (if __name__ == "__main__":), and I start the application (The app = QtGui.QApplication([]), create the main window, and show the main window.
In your case, you could define a main application like I did, and make it alert your QDialog.
Your python code just imports some modules and then defines a new class. It doesn't do anything with the class it has defined, though. In other words, once Python is done creating the new class, it is finished, and it exits.
I don't know PyQT at all, but most likely you need to start the GUI's main loop, and also instantiate an instance of your new class and pass it to PyQT.

Loading QtDesigner's .ui files in PySide

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)

Categories