I'm trying to program a quick dialog with QT4 and Python.
I've generated the Python class, using pyuic4, and tried to make a small python script to start it up:
import sys
from PyQt4 import QtCore, QtGui
from ConfigGUI import Ui_ConfigGUI
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_ConfigGUI()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
When I try to run it, it says AttributeError: 'StartQT4' object has no attribute 'accept'.
What did I do wrong?
I managed to reproduce your problem. You selected a form based on dialogs in the QtDesigner, but are trying to construct it inside a QMainWindow.
The UI code tries to bind its buttons to default dialog slots acceptand reject which are not available in a QMainWindow.
From ConfigGUI.py:
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), Dialog.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), Dialog.reject)
The class contains a method called setupUi(). This takes a single argument which is the widget in which the user interface is created. The type of this argument (typically QDialog, QWidget or QMainWindow) is set in Designer. We refer to this type as the Qt base class.
-- http://pyqt.sourceforge.net/Docs/PyQt4/designer.html
So, either select Main Window in the Designer as a base class, or change the inheritance of StartQT4 to QtGui.QDialog.
Related
I'm actually developping a small soft using PyQt5 and QtDesigner since few weeks now. I've watched a lot of tutos and looked for many answers on SO but I can't find the one concerning the way to override a QWidget's method using uic.loadUI().
Note that I've simplify as much as I could my testing code to point out precisely where my problem is:
1/ This one does not work, it loads my file.ui correctly but clicking doesn't do anything:
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QWidget
class Window(QWidget):
def __init__(self):
super().__init__() # Or QWidget.__init__(self)
self.dlg = uic.loadUi("file.ui")
self.dlg.show()
def mousePressEvent(self, event):
print("click !")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
app.exec_()
2/ But I've figured out that that one is actually working:
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setWindowTitle("My window")
self.show()
def mousePressEvent(self, event):
print("click !")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
app.exec_()
So as I can see it seems to be because I can't override the event's method. Due to the fact that mousePressEvent is not a method attached to my QWidget subclass self.dlg.
Now that I know why, I'd like to know how can I override the mousePressEvent method and use it. I was wondering about 'loading' the method after overrided it, or calling the QWidget method to redefining it, or simply create my own method to catch any event, but everything I tried completely failed.
Anyhow, thanks in advance for your answers / tips / prayers.
Assuming that when creating file.ui you are using the Widget template:
Then the solution is the following:
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QWidget
class Window(QWidget):
def __init__(self):
super().__init__()
uic.loadUi("file.ui", self)
self.show()
def mousePressEvent(self, event):
print("click !")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
app.exec_()
Why did not it work when using self.dlg = uic.loadUi("file.ui")? Because as you point out the widget created is self.dlg which is still an attribute of the win window that was never shown, in the case of uic.loadUi("file.ui", self) you are pointing out that it does not create a window but that fills the existing window
Update:
According to the error you get:
TypeError: ('Wrong base class of toplevel widget', (<class '__main__.Window'>, 'QMainWindow'))
It allows me to deduce that you have not used a QWidget as a basis, but a QMainWindow, so Window must inherit from QMainWindow:
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow
class Window(QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi("file.ui", self)
self.show()
def mousePressEvent(self, event):
print("click !")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
app.exec_()
My approach is to use the Promote to... context menu entry in the Qt Designer object inspector.
That allows you to specify in the designer that specific widgets should use subclasses of what you're pushing around in the graphical builder. uic.loadUi will obey this.
Suppose you have a MyGraphicsView in my_graphics_view.py. You can place a QGraphicsView, then select Promote to... and enter the following:
Base class name: (pre-filled to QGraphicsView)
Promoted class name: MyGraphicsView
Header file: my_graphics_view.h (loadUi will adjust the extension for you)
...then click "Add" and then "Promote" and you're done.
There are only two caveats I've noticed:
As far as I can tell, there's no setupUi method when using uic.loadUi and __init__ returns before the tree of child widgets has been built, so if you want to run any custom code after __init__, you have to put it in a method with some name like widget.configure_children() and call it yourself after calling widget = uic.loadUi(...).
For reasons unknown to me, the copy of Qt Designer 5.5.1 I'm using from Kubuntu Linux 16.04 LTS doesn't offer QMainWindow in the Promote dialog's dropdown of known widgets.
However, if you edit the .ui file, neither Qt Designer nor loadUi will have a problem with it. It appears to be exactly what it seems... a mysterious omission from the dropdown widget and nothing more.
Here's what it looks like in the raw .ui file:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="MyMainWindow" name="MainWindow">
<!-- ... --->
</widget>
<customwidgets>
<customwidget>
<class>MyMainWindow</class>
<extends>QMainWindow</extends>
<header>my_main_window.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
It's also important to note that, according to this page, the PySide2 equivalent to uic.loadUi takes a parent as a second argument instead of a custom subclass, so you're basically forced into using this approach under PySide2.
I want to design my QWizardPages in Qt Designer and I want to load them into my Python program with PySide2. Previously I have been using PyQt5 without any problems but making the switch to PySide2 seems harder then expected.
The problem I am facing is that when I am adding a QWizardPage to my QWizard , the page is indeed added to the Wizard, but also an other (empty) page is added. I'm not able to find what I'm doing wrong so I was wondering if someone can have a look.
I have tried to add the pages with both the functions addPage() and setPage(), but they give the same results. What I also noticed is that when I explicitely set the Title of the page with setTitle(), the empty (unwanted) page gets this title, but not the page I designed in Qt Designer.
import os
import sys
from PySide2.QtWidgets import QWizard, QWizardPage, QApplication
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
from enum import Enum
class MyWizard(QWizard):
def __init__(self):
super().__init__()
self.setPage(PageNumbers.page_one.value, PageOne(self))
class PageOne(QWizardPage):
def __init__(self, parent):
super().__init__(parent)
ui_file = os.path.join(__file__, '..', 'pageOne.ui')
file = QFile(ui_file)
file.open(QFile.ReadOnly)
loader = QUiLoader()
loader.load(file, parent)
file.close()
self.setTitle("This is another test Title")
class PageNumbers(Enum):
page_one = 1
if __name__ == '__main__':
app = QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
app.exec_()
What I would expect is to have just one QWizardPage showing up with directly the Finish button. Instead I get two QWizardPages as shown in this image:
Can someone tell me what's going on?
(I get the expected result using PyQt5 with the following code: https://pastebin.com/6W2sx9M1)
The developers of PyQt implement functions to be able to create classes based on the .ui that is not implemented in Qt by default (Qt/C++ uses the MOC to do this work), but in the case of PySide2-Qt for python it does not implement it, only has the QUiLoader class that allows to create a widget based on the .ui unlike PyQt that allows filling a class.
In conclusion there is no equivalent in PySide2 of the loadUi function so you can not implement the same logic. PySide2 is not PyQt5, there are own equivalences since they use the same base but they have implementations, limitations and advantages.
Going to the practical problem, considering that the .ui is next to the .py the solution is the following:
import os
import sys
from PySide2 import QtCore, QtWidgets, QtUiTools
from enum import Enum
class PageNumbers(Enum):
page_one = 0
class MyWizard(QtWidgets.QWizard):
def __init__(self):
super().__init__()
ui_file = os.path.join(os.path.dirname(os.path.abspath(__file__)) ,'PageOne.ui')
page_one = create_widget(ui_file, self)
self.setPage(PageNumbers.page_one.value, page_one)
def create_widget(filename, parent=None):
file = QtCore.QFile(filename)
if not file.open(QtCore.QFile.ReadOnly):
return
loader = QtUiTools.QUiLoader()
widget = loader.load(file, parent)
return widget
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
sys.exit(app.exec_())
I'm learning pyqt5, and specifically how to use it with the QT Designer. I'm sort of following the turorial HERE. However in this tutorial they are converting the XML interface to Python code with pyuic5, while I'm trying to import it dynamically with uic.loadUi("myui.ui"). In the tutorial we define a slot with the signals and slot editor named " browseSlot".
When I try to run/compile, at the line
dlg = uic.loadUi("myui.ui")
I get the error:
AttributeError: 'QMainWindow' object has no attribute 'browseSlot'
I think what's going on is that QT Designer connects a signal to the slot 'browseSlot' but because a 'browseSlot' method isn't defined in the myui.ui, the error is thrown, because there is no way for the interpreter to know I'm referring to a method that is outside the UI interface file. (In this case, in the module that loads the interface). As far as I can tell QT Designer only lets me connect signals to slots, not define a whole new one. I think that way this is handled in other frameworks is that there will be an abstract method that needs over riding. So what can I do in this situation to make it work?
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtCore import QObject, pyqtSlot
import sys
app = QtWidgets.QApplication([])
dlg = uic.loadUi("myui.ui")
#pyqtSlot
def returnPressedSlot():
pass
#pyqtSlot
def writeDocSlot():
pass
#pyQt
def browseSlot():
pass
dlg.show()
sys.exit(app.exec())
The slots belong to the class that is used returns loadUi(), they are not any functions since they do not magically not connect them, if you want to use loadUi() and implement these methods you must inherit from the class corresponding to the template that you used, in the example of the link Main Window was used so it must be inherited from QMainWindow:
from PyQt5 import QtCore, QtGui, QtWidgets, uic
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
uic.loadUi("mainwindow.ui", self)
#QtCore.pyqtSlot()
def returnPressedSlot():
pass
#QtCore.pyqtSlot()
def writeDocSlot():
pass
#QtCore.pyqtSlot()
def browseSlot():
pass
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
try this out
from PyQt5 import QtWidgets, uic
app = QtWidgets.QApplication([])
form = uic.loadUi("login.ui")
form2.show()
app.exec()
the above python code should display your gui app properly as long as you have install PyQt5 and PyQt5-tools,if you haven't then open CMD and typeenter code here "pip install PyQt5" and click enter.once installation is done type "pip install PyQt5-tools" then you are good to go
Disclaimer: New to both python and qt designer
QT Designer 4.8.7
Python 3.4
PyCharm 5.0.3
Question - How do I add controls to the main form or a scroll area widget on the main form (created in QT Designer) programmatically?
I have created a MainWindow in qt designer and added my widgets. The following is the entire test program in PyCharm:
import sys
from PyQt4 import QtGui, QtCore, uic
from PyQt4.QtGui import *
from PyQt4.QtCore import *
qtCreatorFile = "programLauncher.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
# self.scrollArea_programs.addWidget()
grid = QtGui.QGridLayout()
# self.scrollArea_programs.addWidget(self.pushButton)
grid.addWidget(self.dateLabel,0,0)
grid.addWidget(self.pushButton,0,1)
self.setLayout(grid)
self.pushButton_exit.clicked.connect(self.closeEvent)
def closeEvent(self):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
As you can see I tried to add controls to a grid but nothing shows up when the program runs - I have also tried to add a control to the scroll area. Can someone help me to just add 1 control to the scroll area at run time - so then I can know the proper way to do it or "a" proper way to do this.
Thanks in advance
Without having access to your programLauncher.ui and making minimal changes to your posted code, you can add your UI elements to the window like so:
from PyQt4 import QtGui
import sys
class MyApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# Cannot resize or maximize
self.setFixedSize(1045, 770)
widget = QtGui.QWidget(self)
self.setCentralWidget(widget)
# Add button test
self.dateLabel = QtGui.QLabel("Test")
self.pushButton = QtGui.QPushButton('Test button')
grid = QtGui.QGridLayout()
grid.addWidget(self.dateLabel, 0, 0)
grid.addWidget(self.pushButton, 0, 1)
widget.setLayout(grid)
self.pushButton.clicked.connect(self.closeEvent)
def closeEvent(self, event):
QtGui.QApplication.quit()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
This will get the controls on the screen, although the layout leaves a lot to be desired. You may have to make modifications to this based on what's in your .ui file. One thing that you'll want to note in this example is that the QMainWindow needs a central widget (widget in the example above). You then set the layout on that widget.
You can use the designer to create your .ui file
The you can load it in your .py using something like:
from PyQt4 import QtCore, QtGui, uic
class my_win(QtGui.QMainWindow):
def __init__(self):
self.ui = uic.loadUi('my_ui.ui',self)
then you can access all your widgets with something like
self.ui.actionQuit.triggered.connect(QtGui.qApp.quit)
or
self.ui.my_button.triggered.connect(self.do_someting)
Thanks to JCVanHamme (the programLauncher.ui hint) and also outside help I now learned most of what I need to know to access MainWindow at run time. So for anyone interested in this beginner tip:
Take a blank form in QT Designer
Add a control
Run pyuic4 batch file
Take a look at the generated .py file to learn EVERYTHING about how to add controls.
Don't let the power go to your head - cheers
Here is the error I am getting, which I am really confused about. My UI file which I am loading has this button name and it matches. But for some reason it doesn't seem to recognize and load it. I just tried converting this code over to PySide (it was originally PyQt). Am I doing something wrong in translating it?
Error: AttributeError: file line 25: 'swapRefGUI' object has no attribute 'swapRefBtn' #
from PySide import QtCore, QtGui, QtUiTools
import maya.cmds as cmds
class swapRefGUI(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
loader = QtUiTools.QUiLoader()
uifile = QtCore.QFile('C:\Scripts\swapRef.ui')
uifile.open(QtCore.QFile.ReadOnly)
ui = loader.load(uifile, parent)
uifile.close()
self.setFixedSize(400, 300)
self.swapRefBtn.clicked.connect(self.swapRefBtn_clicked)
self.closeBtn.clicked.connect(self.close)
def swapRefBtn_clicked(self):
pass
if __name__ == "__main__":
#app = QApplication(sys.argv)
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
myGUI = swapRefGUI(None)
myGUI.show()
sys.exit(app.exec_())
Right now you are trying to access swapRefBtn through the class instance swapRefGUI, but you actually need to access it through the ui variable where you load. The 2nd argument of loader.load should also be self to display the qt gui in your window. There's also a few instances where you are trying to access objects from PySide like QDialog, when it should be QtGui.QDialog (because of the way you imported PySide module).
Here's some code that worked with a ui file.
from PySide import QtCore, QtGui, QtUiTools
import maya.cmds as cmds
class swapRefGUI(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
loader = QtUiTools.QUiLoader()
uifile = QtCore.QFile('C:\Scripts\swapRef.ui')
uifile.open(QtCore.QFile.ReadOnly)
self.ui = loader.load(uifile, self) # Qt objects are inside ui, so good idea to save the variable to the class, 2nd arg should refer to self
uifile.close()
self.setFixedSize(400, 300)
self.ui.swapRefBtn.clicked.connect(self.swapRefBtn_clicked) # Need to access button through self.ui
#self.ui.closeBtn.clicked.connect(self.close) # This needs to have an existing function in the class or it will crash when executing
def swapRefBtn_clicked(self):
print 'WORKS'
myGUI = swapRefGUI()
myGUI.show()