Search code examples
c++qtraii

Design reason behind setupUi()


I'd like to know why, in your opinion, Qt engineers decided to put the setupUi() method in every generated form.

(for those who do not know how Qt works: setupUi() is a method which allocates in dynamic memory every button, texbox, widget which is inside the form. Every element's reference is stored inside a pointer member variable which is not even initialized to nullptr and it is placed under a public: access modifier, so that also looks like a somewhat dangerous situation until you actually call setupUi())

In my newby opinion, that totally violates RAII principle, because that means any Ui_Whatever class is actually not constructed by its constructor call: constructor just allocates the class itself, but it is unusable till we call Ui_Whatever::setupUi().

Which could be the design reason behind the choose of having:

  • an empty compiler-generated default-constructor
  • a method which do the real construction

(I'm asking 'cause I can't figure out myself any valid reason at all)

Thanks in advance!!


Solution

  • I agree, that it totally violates RAII principles. But I found a fairly easy workaround for that:

    template<typename UiClass> class QtRaiiWrapper : public UiClass
    {
    public:
        QtRaiiWrapper(QMainWindow *MainWindow)
        {
            setupUi(MainWindow);
        }
    };
    
    ...
    
    #include "ui_Main.h"
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
        ...
    private:
        // Ui_MainWindow m_Ui; // no RAII
        QtRaiiWrapper<Ui_MainWindow> m_Ui; // RAII
    };
    

    (It might need some improvements, but this is the basic idea and works for me pretty well.)

    Some more discussion on the topic:

    Qt's choice of implementing the initialization via setupUi does not only lack RAII for this component, it can also make RAII impossible for other parts of your program, which is double bad. In my case, another object used the value of an GUI-Element for initialization (this way I could define the initial value in QtDesigner, rather than using a fake value there and overwriting it upon startup), but could not use RAII for that, because the GUI would only be initialized inside the body of the constructor of my MainWindow.

    Therefore I disagree with Kuba Ober, you have not full control of when exactly you want the GUI to be initialized, as you can only do it inside the constructor body and not the initialization list (and following RAII, the initialization list is the place where perhaps most of all the initialization should happen). But luckily, C++ is awesome and we can write a workaround for that (as posted above). I also think this is far better than changing the way uic because as soon as other people try to compile your Code they will see a big surprise.