Search code examples
c++qtuiloader

QUiLoader: requirements for loading .ui file with custom widgets?


I've created a UI using Qt5-Designer which I load at runtime by calling

QUiLoader().load(qfile_object, this);

Works like charm but now I've promoted some QLabel elements to a widget class MyQLabel with is derived from QLabel.

When I now try to load the UI I get a warning for each promoted widget:

"QFormBuilder was unable to create a custom widget of the class 'MyQLabel'; defaulting to base class 'QLabel'."

The class looks like this:

class MyQLabel : public QLabel {
    Q_OBJECT
  public:
    MyQLabel(QWidget *parent = nullptr) : QLabel(parent) {}
};

It's been auto-moc'ed and linked against my executable.

I have the feeling that somehow I have to tell QUiLoader about my class before trying to use it but I don't know how..

Do I have to create a plugin for this? Is there a way to reproduce, what to QUiLoader does to examine it?


Solution

  • You need to create your own derived version of QUiLoader, and provide an implementation of the factory method QUiLoader::createWidget that can create your widgets.

    You could factor this out into a plugin that gets loaded by the QUiLoader. It would have to implement a QDesignerCustomWidgetInterface instance. See the Custom Widget Plugin Example for a complete example of a plugin.

    // https://github.com/KubaO/stackoverflown/tree/master/questions/uiloader-custom-37775472
    #include <QtUiTools>
    #include <QtWidgets>
    
    const char uiData[] =
        "<ui version=\"4.0\"><class>Widget</class><widget class=\"MyWidget\" name=\"Widget\">\n"
            "<property name=\"windowTitle\" ><string>Widget</string></property>\n"
            "</widget><pixmapfunction></pixmapfunction><resources/><connections/>\n"
        "</ui>";
    
    class MyWidget : public QLabel
    {
        Q_OBJECT
        bool m_closed = false;
    public:
        MyWidget(QWidget* parent = 0) : QLabel("This is MyWidget", parent) {}
        bool isClosed() const { return m_closed; }
        void closeEvent(QCloseEvent *) Q_DECL_OVERRIDE { m_closed = true; }
    };
    
    class MyUiLoader : public QUiLoader {
    public:
        MyUiLoader(QObject * parent = 0) : QUiLoader(parent) {}
        QWidget * createWidget(const QString & className, QWidget * parent = 0,
                               const QString & name = QString()) {
            if (className == "MyWidget") {
                MyWidget * w = new MyWidget(parent);
                w->setObjectName(name);
                return w;
            }
            return QUiLoader::createWidget(className, parent, name);
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        QBuffer buf;
        buf.setData(uiData, sizeof(uiData));
        MyUiLoader uiLoader;
        auto uiMain = qobject_cast<MyWidget*>(uiLoader.load(&buf));
        uiMain->show();
        return app.exec();
    }
    
    #include "main.moc"