Swapping QWidget child object for another object
-
While the question applies generally to QObjects, it becomes more important with QWidgets generated in Qt Designer, so I'll word it in terms of QWidgets.
Suppose in Designer I am making a FooWidget that has a child widget that is a BarWidget. In designer, I connect up signals and slots, and in the source I may make direct function calls to
ui->barWidget->doStuff()
.Suppose further that BarWidget is a base class for multiple implementations. Maybe BallWidget derives from BarWidget. I'd like to be able to do an in-place swap of barWidget to some derived widget type, so that FooWidget's connections now connect to the same signals and slots of ballWidget, FooWidget's calls to
ui->barWidget->doStuff()
call the ballWidget's override of doStuff().In other areas, I would simply pass a BarWidget pointer in the constructor for Foo and just allow that to do dependency injection. But the ui_FooWidget.h file will create its own BarWidget and connect that regardless of what subtype I may pass into Foo's constructor. So that seems a dead end.
Is there any way to do what I'm trying? Does it help if I restrict the problem to just a problem of unit testing where the dependent class I'd like to inject is a MockBarWidget? (so I don't have to be very respectful of modularity or anything in that case).
-
Hi,
What about adding a setter method that will delete the original widget and replace it with the one passed in parameter ? The setup of the signals and slots should be done once the replacement has been made.
-
A setter method will work, but it rather negates the benefit of Qt Designer, don't you think? I have to go find where the widget is located in layout, remove the existing one, add the new one to the same location. I can't use the Designer signal/slot connection mechanism because I have to manually make those connections when a widget is swapped.
For example, one thing I tried, but didn't work, is to do something like:
BarWidget* bar = foo.findChild<BarWidget*>("barWidget"); bar = new BallWidget(&foo);
I understand why it doesn't work, and I can't say I really expected it would, but it feels like some technique to allow this kind of an in-place swap could be a useful technique.
-
@shavera
Another thing that would be more complicated for end users, but could possibly be handled relatively easily from Qt's perspective would be for the uic to generate a virtual setupUi function. This way, instead of trying to replace whichever single element in FooWidget, I could write a class derived from Ui::FooWidget that overrides the setupUi function, and inject this derived class into FooWidget at its construction if needed.Edit: Actually, this seems to be the closest to an answer, at least as far as needing to inject mocks for unit testing is concerned. In addition to mocking whatever child class (MockBar from Bar), you can modify Foo's constructor to be
FooWidget(QWidget* parent = nullptr, Ui::FooWidget* _ui = nullptr) : QWidget{parent} , ui{nullptr == _ui ? new Ui::FooWidget{} : _ui} {}
And then inject a copy of Ui::FooWidget you maintain in the test where you can directly swap ui's variables
-
Why would it negate it ? Whether you are using designer or not, the implications are the same: you have to set the widget in the layout and connect it in any case.
On a side note, you should put parent as last parameter. That's the usual position and makes it coherent with the other Qt classes.