embed custom widget in pyqt fails with ImportError - python

I have created a layout for my custom widget an created a class in file src\WidgetStartEndCenterWidth.py
from PyQt4 import QtGui, uic
from PyQt4 import QtCore
class WidgetStartEndCenterWidth(QtGui.QWidget):
def __init__(self):
super(WidgetStartEndCenterWidth, self).__init__()
self.initUI()
def initUI(self):
uic.loadUi('widgetStartEndCenterWidth.ui', self)
self.show()
self.setupSignalSlots()
def setupSignalSlots(self):
self.spinBoxStart.valueChanged.connect(self.onSpinBoxStartValueChanged)
#QtCore.pyqtSlot(int)
def onSpinBoxStartValueChanged(self, value):
self.spinBoxEnd.setValue(value)
I promoted the widget in my MainWindow as WidgetStartEndCenterWidth with header file (does this make sence at all?) WidgetStartEndCenterWidth.h.
However the execution fails with
ImportError: No module named 'WidgetStartEndCenterWidth'
I assume that this has to do with my code in directory src, but anyway I would like to know how this is supposed to be done in the Qt Designer.
EDIT:
The widget is embedded (or promoted) in the ui of MainWindow. Here is the code of that file, but naturally there is not code of the widget, because all that happens in the Qt Designer...
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
uic.loadUi('MainWindow.ui', self)
self.show()

Related

Loading A PyQt5 UI Via A Class

I'm currently trying to update my PySide code using PyQt5. And I have a class called "loader.py" that used to use "QUiLoader" from "PySide.QtUiTools", but as far as I know in PyQt5 this module has been changed by "uic".
So the problem here is that I changed my "QUiLoader" import from "uic" but I always get this error:
ui_loader.py", line 4, in <module> class UiLoader(uic): TypeError: module() takes at most 2 arguments (3 given)
Original Code in Pyside
Here is where I got the code for my PySide app
Code in PyQt5
ui_loader.py
from PyQt5 import uic
from PyQt5.QtCore import QMetaObject
class UiLoader(uic):
def __init__(self, base_instance):
uic.__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 = uic.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
main.py
from PyQt5.QtWidgets import QMainWindow, QApplication
from ui_loader import load_ui
import sys
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
load_ui('my_interface.ui', self)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
if __name__ == '__main__':
main()
I have also tried to used submethod of the class and refactoring all the code but it was useless.
You can actually load the ui using uic and then just inherit that ui into a class then self.setupUI()
ui_MainWindow, QtBaseClass = uic.loadUiType("main_window.ui")
class MainWindow(QMainWindow, ui_MainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
Another way as musicamante is suggesting would be like this:
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
uic.loadUi("main_window.ui", self)

Transfer QMessageBox setting to a separate class in Python

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

How to load second .ui into self with PyQt4.uic.loadUi?

I'm creating an application which loads a couple of .ui files. The first one is of type QMainWindow and the others are of type QWidget.
I can't figure out how to load the second UI (module.ui) into self, making widgets accessible through self.<widget_name>.
How can this be achieved?
from PyQt4 import QtGui
from PyQt4 import uic
class TestApp(QtGui.QMainWindow):
def __init__(self):
super(TestApp, self).__init__()
# Load main window and the module
uic.loadUi('main_window.ui', self) # QMainWindow, contains testLayout, loads into self
ui_module = uic.loadUi('module.ui') # QWidget
# Attach module to main window
self.testLayout.addWidget(ui_module) # this works fine
# Edit widget in UI module
self.label.setText('Hello') # does not work (since self.label doesn't exist)
I could do this:
self.label = ui_module.label
self.label.setText('Hello')
...but I'd like to instead load the UI into self from the start.
If I try to load the UI into self, I get an error:
uic.loadUi('module.ui', self)
>>> QLayout: Attempting to add QLayout "" to TestApp "Form", which already has a layout
You need to create a widget to load the ui file onto
self.widget = QWidget(self)
uic.loadUi('module.ui', self.widget)
self.widget.label.setText('Hello')
That being said, it would probably be better if you created a separate class for the other widget.
class MyWidget(QWidget):
def __init__(self, **args, **kwargs):
super(MyWidget, self).__init__(*args, **kwargs)
uic.loadUi('module.ui', self)
self.label.setText('Hello')
class TestApp(QtGui.QMainWindow):
def __init__(self):
...
self.widget = MyWidget(self)

Python PyQt4 open from QDialog new QWidget window

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.

Qt mdi application with custiom UI from QtDesigner

Assume I have two UI files from Qt Designer:mainform.ui stores mdiArea and figureslist.ui stores listView.
Now I'd like to create a mdi application, that can open numbers of figureList windows.
import sys
from PyQt4 import QtGui
#from PyQt4.QtGui import *
from PyQt4 import QtCore, QtGui, uic
class HelloWorldApplication(QtGui.QApplication):
def __init__(self, args):
QtGui.QApplication.__init__(self, args)
self.maindialog = MainUI(None)
class MainUI(QtGui.QMainWindow):
def __init__(self, parent):
QtGui.QMainWindow.__init__(self, parent)
self.ui = uic.loadUi("mainform.ui")
self.ui.show()
# create child and show it
child = self.createFiguresListView()
# problem here (*)
child.show()
def createFiguresListView(self):
child = FiguresListView()
self.ui.mdi.addSubWindow(child)
return child
class FiguresListView(QtGui.QWidget):
def __init__(self):
super(FiguresListView, self).__init__()
self.ui = uic.loadUi("figureslist.ui")
app = HelloWorldApplication(sys.argv)
sys.exit(app.exec_())
But unfortunately my child window shows up collapsed without layout described in figureslist.ui, but acts like mdi child, but if I replace code marked with (*) to child.ui.show() it shows actual layout, but doesn't act like mdi child.
What's wrong?
You forgot to set the parent for the ui (also, if you didn't specified minimum size in Designer, you need to do it here):
class FiguresListView(QtGui.QWidget):
def __init__(self):
super(FiguresListView, self).__init__()
self.ui = uic.loadUi("figureslist.ui", self)
#self.setMinimumSize(400, 200)

Categories