What are consequences of forcing QObject as a parent of QWidget?
Asked Answered
D

1

12

The following code compiles perfectly:

QObject* o = new QObject(0);
QWidget* w = new QWidget(0);
qobject_cast<QObject*>(w)->setParent(o);

I cannot legally set QObject as a parent of QWidget. But using qobject_cast it is possible. Are there negative consequences?

Dearborn answered 11/3, 2015 at 16:34 Comment(5)
Just curious: what is the reason for such hierarchy?Syllogistic
What?!? QWidget inherits publicly from QObjectNewlin
QWidget::setParent(QWidget*) hides QObject::setParent(QObject*).Hedgcock
static_cast would be more appropriate. Or w->QObject::setParent(o);.Hedgcock
For those voting to close this as "unclear" - please, don't. It's a perfectly clear question! A related one is https://mcmap.net/q/1009097/-is-it-possible-to-have-a-qwidget-as-a-child-to-a-qobject/1329652 - but they are not duplicates, I don't think.Nikianikita
S
17

What are consequences of forcing QObject as a parent of QWidget?

Irrevocable undefined behavior.

Qt is not designed to support a non-widget parent to a QWidget. I consider it an API bug in Qt, since a QWidget isn't fully a QObject in the Liskov Substitution Principle sense because of that limitation.

Qt 4.x will crash when attempting to activate the widget. So it'll work until you focus your application and then will crash.

Qt 5.x asserts in QObject::setParent().

The assertion can be bypassed, though:

// https://github.com/KubaO/stackoverflown/tree/master/questions/widget-parent-28992276
#include <QApplication>
#include <QLabel>

class ParentHacker : private QWidget {
public:
   static void setParent(QWidget * child_, QObject * parent) {
      // The following line invokes undefined behavior
      auto child = static_cast<ParentHacker*>(child_);
      Q_ASSERT(child->d_ptr->isWidget);
      child->d_ptr->isWidget = 0;
      child->QObject::setParent(parent);
      child->d_ptr->isWidget = 1;
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QLabel w{"Hello!"};
   w.setMinimumSize(200, 100);
   w.show();
   ParentHacker::setParent(&w, &app);
   return app.exec();
}

It will crash somewhere else then.

You'd be fighting an uphill battle trying to patch Qt to get it to work. It's not a worthwhile fight, I think - not unless a decision is made to make a QWidget truly-a QObject and change its constructor signature. That can be done at the earliest in the next major version of Qt since it's a binary-incompatible change AFAIK.

Moreover, what you're trying to do is mostly unnecessary. You can certainly have a hidden QWidget parent to multiple stand-alone top-level widgets.

#include <QApplication>
#include <QLabel>

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QWidget parent;
   QLabel l1{"Close me to quit!"}, l2{"Hello!"};
   for (auto label : {&l1, &l2}) {
      label->setMinimumSize(200, 100);
      label->setParent(&parent);
      label->setWindowFlags(Qt::Window);
      label->setText(QString("%1 Parent: %2.").
                     arg(label->text()).arg((quintptr)label->parent(), 0, 16));
      label->show();
   }
   l2.setAttribute(Qt::WA_QuitOnClose, false);
   return app.exec();
}

The overhead of having the widget hidden is minimal, you're not wasting any resources by using a QWidget instead of a QObject for the parent.

Secular answered 11/3, 2015 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.