QUiLoader widget not showing if loaded in derived

2019-06-13 16:50发布

问题:

Widget::Widget(QWidget *parent) : QWidget(parent) {
    file.open(QFile::ReadOnly);
    QWidget *w = loader.load(&file);
//    MyWidget *w = new MyWidget(loader.load(&file));
    vLayout.addWidget(w);
    file.close();        
    vLayout.addWidget(&bt);
}

The code above produces the image above. But I would like to do something with the widget just loaded, that's why I derived MyWidget from QWidget. And there seems to be the problem.

If I replace QWidget by MyWidget then it doesn't show there anymore. The code below produces the image below.

Widget::Widget(QWidget *parent) : QWidget(parent) {
    file.open(QFile::ReadOnly);
//    QWidget *w = loader.load(&file);
    MyWidget *w = new MyWidget(loader.load(&file));
    vLayout.addWidget(w);
    file.close();        
    vLayout.addWidget(&bt);
}

Where MyWidget:

// mywidget.h
#include <QWidget>

class MyWidget : public QWidget {
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);
};

// mywidget.cpp
#include "mywidget.h"    
#include <QDebug>

MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {
    qDebug() << "do something here";
}

Question 1: Why the widget (group box) isn't showing there? How to make it appear?

Ok, question 1 completed thanks to @KubaOber. Now I would like to access the widgets inside untitled.ui, I tried:

MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {
    foreach (QLabel *label, parent->findChildren<QLabel*>())
        if(label->objectName().contains("Value"))
            qDebug() << "label" << label;

}

But it returns nothing. Instead, passing a parent in loader.load(&file, vLayoutWidget)) makes the untitled.ui widgets available but also craps the contents of vLayoutWidget. I guess the question is what should be passed in as parent in the loader.load())?

回答1:

The way you use the loader doesn't mean "load a MyWidget using QUiLoader:

MyWidget *w = new MyWidget(loader.load(&file));

What you're doing above is initializing a MyWidget that has whatever the loader has loaded as a parent. And then you're promptly reparenting w by adding it to a layout, effectively discarding whatever the loader has loaded.

You need to tell the loader what parent to add the widgets it has loaded to, of course noting that MyWidget doesn't need to be dynamically allocated:

auto w = new MyWidget;
loader.load(&file, w);

Here's how it might look when put together:

class Widget : public QWidget {
  Q_OBJECT
  QVBoxLayout m_layout{this};
  MyWidget m_myWidget;
  QVBoxLayout m_myLayout{&m_myWidget};
public:
  explicit Widget(QIODevice * source, QWidget * parent = nullptr) : 
    QWidget{parent}
  {
    auto loaded = QUiLoader{}.load(source, &m_myWidget);
    m_layout.addWidget(&m_myWidget);
    m_myLayout.addWidget(loaded);
  }
};

If your ui file's top-level widget is a MyWidget, then you'll need to let the loader know how to instantiate it - see this answer for details. And then you would write it as follows:

class Widget : public QWidget {
  Q_OBJECT
  QVBoxLayout m_layout{this};
  MyWidget * m_myWidget;
public:
  explicit Widget(QWidget * loaded, QWidget * parent = nullptr) : 
    QWidget{parent},
    m_myWidget{qobject_cast<MyWidget*>(loaded)}
  {
    m_layout.addWidget(m_myWidget);
  }
};

...
MyUiLoader loader{}
auto loaded = loader.load(...);
Widget widget{loaded};
...