Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    Unsolved Swapping QWidget child object for another object

    General and Desktop
    2
    5
    897
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • shavera
      shavera last edited by

      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).

      1 Reply Last reply Reply Quote 0
      • SGaist
        SGaist Lifetime Qt Champion last edited by

        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.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply Reply Quote 0
        • shavera
          shavera last edited by

          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 1 Reply Last reply Reply Quote 0
          • shavera
            shavera @shavera last edited by shavera

            @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

            1 Reply Last reply Reply Quote 0
            • SGaist
              SGaist Lifetime Qt Champion last edited by

              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.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply Reply Quote 0
              • First post
                Last post