I've this in my app.py
:
from PyQt5 import QtWidgets
from screens import Ui_home, Ui_login_page
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.home = Ui_home()
self.home.setupUi(self)
self.login = Ui_login_page()
self.login.setupUi(self)
self.screens = QtWidgets.QStackedWidget()
self.screens.addWidget(self.home)
self.screens.addWidget(self.login)
self.home.pushButton.clicked.connect(
lambda: self.screens.setCurrentWidget(self.login)
)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec_()
In above code, Ui_home
& Ui_login_page
screens are generated from a *.ui
file which is generated from PyQt5 Designer. When I run this code, I get this error:
self.screens.addWidget(self.home)
TypeError: addWidget(self, w: Optional[QWidget]): argument 1 has unexpected type 'Ui_home'
I know that self.setCurrentWidget()
requires a QWidget
, and the code generated from pyuic5
inherits the class from Python object
. How can I fix this so that my home and login screens can be added into the stack widget?
The following things are wrong or don't make sense:
Ui_home
and Ui_login_page
are form classes (fundamentally, basic python objects) and not widgets;setupUi(self)
more than once is pointless, as it will rewrite the UI of the current widget;setCentralWidget()
more than once will automatically destroy the previously set widget, which is obviously wrong for the purpose of a stacked widget, since it should allow switching back to a previously shown "page";Make the stacked widget as central widget, then create appropriate QWidget instances and call setupUi()
on each one of them, finally add those widgets to the stacked widget:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.screens = QtWidgets.QStackedWidget()
self.setCentralWidget(self.screens)
self.home = QtWidgets.QWidget()
self.home_ui = Ui_home()
self.home_ui.setupUi(self.home)
self.login = QtWidgets.QWidget()
self.login_ui = Ui_login_page()
self.login_ui.setupUi(self.login)
self.screens.addWidget(self.home)
self.screens.addWidget(self.login)
self.home_ui.pushButton.clicked.connect(
lambda: self.screens.setCurrentWidget(self.login)
)
Note that a more appropriate approach (also explained in the official guidelines about using Designer) is to use multiple inheritance against composition (which is what is shown above); that approach provides better modularization since each "page" will have its own UI elements as instance attributes, avoiding the need for a further ui
object.
class HomePage(QtWidgets.QWidget, Ui_home):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
class LoginPage(QtWidgets.QWidget, Ui_login_page):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.screens = QtWidgets.QStackedWidget()
self.setCentralWidget(self.screens)
self.home = HomePage()
self.login = LoginPage()
self.screens.addWidget(self.home)
self.screens.addWidget(self.login)
# note that now it's "self.home", not "self.home_ui"
self.home.pushButton.clicked.connect(
lambda: self.screens.setCurrentWidget(self.login)
)
In the code above the difference seems minimal (controversially both pointless and unnecessarily verbose), but the point is that creating a subclass for each page or container widget allows proper and simpler implementation and debugging (see encapsulation), such as the possibility of adding custom signals, creating internal functions, and simplify access to object attributes.