I've used QtDesigner to make ui files that I then use to make classes, like
class MyPopup1(MyBaseClass, MyClass):
def __init__(self, parent=None):
super(MyPopup1, self).__init__(parent)
self.setupUi(self)
...
Granted I used some tutorial for this so I'm not actually sure what all of that does. But now I have written code that generates a popup that uses the QMessageBox class and I would like to move this code to a separate class so I can call it from multiple places.
How do I move this code to make a MyPopup2 class?
MyPopup2 = QtWidgets.QMessageBox(parent = self.central_widget)
MyPopup2.setWindowTitle("My Popup 2")
MyPopup2.setText("Some text")
MyPopup2.setIcon(QtWidgets.QMessageBox.Question)
MyPopup2.addButton("Btn1", QtWidgets.QMessageBox.RejectRole)
MyPopup2.addButton("Btn2", QtWidgets.QMessageBox.ActionRole)
choice = MyPopup2.exec_()
I know I probably need to connect the button signals to functions and use self.done() to send the result back to a call.
I am mostly confused on what to put as MyBaseClass and MyClass for the second popup.
Qt Designer provides a class that serves to fill a widget, so a recommended way is to inherit a widget and inherit from the generated class of Qt Designer, for example the structure that Qt Designer provides has the following structure:
class MyClass(object):
def setupUi(self, AAA):
...
self.retranslateUi(AAA)
QtCore.QMetaObject.connectSlotsByName(AAA)
def retranslateUi(self, AAA):
...
Then depending on the template you should choose as MyBaseClass to QMainWindow, QDialog or QWidget and call setupUi() which is the method that you add the other widget to the window as you point out:
class MyPopup1(MyBaseClass, MyClass):
def __init__(self, parent=None):
super(MyPopup1, self).__init__(parent)
self.setupUi(self)
...
But in the case that you are going to create the widget, MyClass is not necessary, so in your case the solution is the following:
from PyQt5 import QtWidgets
class MyPopup2(QtWidgets.QMessageBox):
def __init__(self, parent=None):
super(MyPopup2, self).__init__(parent)
self.setWindowTitle("My Popup 2")
self.setText("Some text")
self.setIcon(QtWidgets.QMessageBox.Question)
self.addButton("Btn1", QtWidgets.QMessageBox.RejectRole)
self.addButton("Btn2", QtWidgets.QMessageBox.ActionRole)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
popup = MyPopup2()
if popup.exec_() == QtWidgets.QMessageBox.Accepted:
print("Btn2")
else:
print("Btn1")
The full solution for the example is
class MyPopup2(QtWidgets.QMessageBox):
def __init__(self, parent=None):
super(NoMatch, self).__init__(parent)
self.setWindowTitle("My Popup 2")
self.setText("Some text")
self.setIcon(QtWidgets.QMessageBox.Question)
self.Btn1 = self.addButton("Btn1", QtWidgets.QMessageBox.RejectRole)
self.Btn2 = self.addButton("Btn2", QtWidgets.QMessageBox.ActionRole)
self.Btn1.clicked.connect(lambda: self.done(QtWidgets.QMessageBox.RejectRole))
self.Btn2.clicked.connect(lambda: self.done(QtWidgets.QMessageBox.ActionRole))
Which can be called with choice = MyPopup2.exec_() from anywhere
Related
I'm using Python3 and PyQt5, make my widgets and windows in Qt Designer. What is more, I do not generate .py files from a .ui. I simply load it using next code:
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
uic.loadUi('UI/Qt/source/MainWindow.ui', self)
So, I wanted to know, how do I bind menu bar actions to functions.
Is there any way I can do something like this?
self.getActionByName("actionTest_Action").connect(self.do_something)
It is not necessary to use findChild when using loadUi since this method adds the object to the attributes of the class using the objectName as a name, for example in this particular case a cleaner code than the other answer is:
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
uic.loadUi('UI/Qt/source/MainWindow.ui', self)
self.actionTest_Action.triggered.connect(self.test)
def test(self):
print("Test")
So, answering my own question..
One way to do this, is to find an action by using FindChild(QAction, "ActionName") function, and then bind a function using connect() function
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
uic.loadUi('UI/Qt/source/MainWindow.ui', self)
action = self.findChild(QAction, "actionTest_Action")
action.triggered.connect(self.test)
def test(self):
print("Test")
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.
By pressing a QPushButton in my QDialog window I want to open a new QWidget window.
My code:
from PyQt4 import QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("Main Window")
class FirstWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(FirstWindow, self).__init__(parent)
self.createWindow()
def createWindow(self):
btn = QtGui.QPushButton('Open New Window', self)
btn.move(10, 10)
self.openNewWindow = MainWindow(self)
btn.clicked.connect(self.openMainWin)
self.setGeometry(250,250, 150,50)
self.setWindowTitle("First Window")
self.show()
def openMainWin(self):
self.openNewWindow.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
firstwin = FirstWindow()
sys.exit(app.exec_())
When I run the code nothing happens by pressing the button.
But when I change the class from
class MainWindow(QtGui.QWidget) to
class MainWindow(QtGui.QDialog) or class MainWindow(QtGui.QMainWindow)
it works!
What am I doing wrong?! Please assist me.
When you instantiate MainWindow you pass in a parent. Qwidget only makes a new window if you don't specify a parent.
This is of course deliberate. If QWidgets with parents were shown in new windows, then you could never build a GUI. Imagine having every widget in it's own window!
QMainWindow and QDialog are specifically designed to both have a parent, and create a new window. You should use them.
In PyQt4 I have a main window which when the settings button is clicked opens the settings dialog
from PyQt4 import QtCore, QtGui
import ui_Design, ui_Settings_Design
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.settingsBtn.clicked.connect(lambda: self.showSettings())
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
The above works and my SettingsDialog is displayed but I cant get the setPageIndex to work
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(self.setPageIndex)
#QtCore.pyqtSlot()
def setPageIndex(self):
print 'selected'
self.settingsStackedWidget.setCurrentIndex(0)
The bookSettingsBtn is a QToolButton
self.bookSettingsBtn = QtGui.QToolButton(self.navigationFrame)
And the settingsStackedWidget is a QStackedWidget
self.settingsStackedWidget = QtGui.QStackedWidget(SettingsDialog)
At this point I am pretty confused on signals and slots and nothing I have read clears this up - if anyone can point out what I am doing wrong above and also potentially direct me to a good (beginners) guide / tutorial on signals and slots it would be greatly appreciated
I would also like to know how to make setPageIndex work as follows:
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)
I'm not sure why you're doing the following, but that's the issue:
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
SettingsDialog itself is a proper QDialog. You don't need to instantiate another QDialog.
Right now, you're creating an empty QDialog and then populate it with the same ui as SettingsDialog (i.e. setupUi(dialog)), then you show this dialog. But... The signal connection is for SettingsDialog, and the dialog you're showing doesn't have that.
Basically, you don't need that extra QDialog at all. The following should be enough:
def showSettings(self):
dialog = SettingsDialog()
dialog.exec_()
Ok. So here is an example how you pass an argument to a slot
from functools import partial
# here you have a button bookSettingsBtn:
self.bookSettingsBtn = QtGui.QPushButton("settings")
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, self.bookSettingsBtn.text()))
#pyqtSlot(str) # this means the function expects 1 string parameter (str, str) 2 string parameters etc.
def setPageIndex(self, selection):
print "you pressed button " + selection
In your case the argument would be an int. Of course you have to get the value from somewhere
and then put it in the partial part as the argument (here I just used the text of the button),
but you can use int, bool etc. Just watch the slot signature.
Here is a tutorial that helped me:
http://zetcode.com/gui/pyqt4/
I hope this helps.
Hey here I have a fully running example (just copy paste it in a python file and run it):
Maybe this helps you. It's a small example but here you see how it works.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from functools import partial
class MyForm(QMainWindow):
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
button1 = QPushButton('Button 1')
button2 = QPushButton('Button 2')
button1.clicked.connect(partial(self.on_button, button1.text()))
button2.clicked.connect(partial(self.on_button, button1.text()))
layout = QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
#pyqtSlot(str)
def on_button(self, n):
print "Text of button is: " + str(n)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
form = MyForm()
form.show()
app.exec_()
So I dont really understand why but changing the way the settingsDialog is called from the MainWindow has fixed my problem. I guess the parent window needed to be specified??:
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
....
def showSettings(self):
self.settingsDialog = QtGui.QDialog(self)
self.settingsDialog.ui = SettingsDialog(self)
self.settingsDialog.ui.show()
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, 1))
#QtCore.pyqtSlot(int)
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)
I'm trying to make a class that extends qwidget, that pops up a new window, I must be missing something fundamental,
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
QtGui.QMainWindow.__init__(self,parent)
self.setWindowTitle('Add New Query')
grid = QtGui.QGridLayout()
label = QtGui.QLabel('blah')
grid.addWidget(label,0,0)
self.setLayout(grid)
self.resize(300,200)
when a new instance of this is made in main window's class, and show() called, the content is overlaid on the main window, how can I make it display in a new window?
follow the advice that #ChristopheD gave you and try this instead
from PyQt4 import QtGui
class NewQuery(QtGui.QWidget):
def __init__(self, parent=None):
super(NewQuery, self).__init__(parent)
self.setWindowTitle('Add New Query')
grid = QtGui.QGridLayout()
label = QtGui.QLabel('blah')
grid.addWidget(label,0,0)
self.setLayout(grid)
self.resize(300,200)
app = QtGui.QApplication([])
mainform = NewQuery()
mainform.show()
newchildform = NewQuery()
newchildform.show()
app.exec_()
Your superclass initialiser is wrong, you probably meant:
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
(a reason to use super):
class NewQuery(QtGui.QWidget):
def __init__(self, parent):
super(NewQuery, self).__init__(parent)
But maybe you want inherit from QtGui.QDialog instead (that could be appropriate - hard to tell with the current context).
Also note that the indentation in your code example is wrong (a single space will work but 4 spaces or a single tab are considered nicer).