Search code examples
pythonpyqt5pyside2qwizardqwizardpage

How to load a Qt Designer file (.ui) in a QWizardPage using 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:

enter image description here

Can someone tell me what's going on?

(I get the expected result using PyQt5 with the following code: https://pastebin.com/6W2sx9M1)


Solution

  • 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_())