Defining deeply nested widget hierarchies
-
Here I am wondering what is the correct way to go while constructing a MainWindow Gui with deeply nested Widget Elements.
I used to subclass QWidget and populated it with layouts, built-in widgets and what have you.
However, I found that propagating signals all the way up the gui hierarchy by means of dispatch function etc. is a nightmare. Also, if I would want to change certain properties of specific Widgets posteriori, e.g. the slider-range according to the size of some loaded data structure, I hit brick-walls due to private pointer members of widget elements and thus unreachable widget property functions.
The alternative, (the way the designer handles things?), is to declare all widget members (pointers, etc.) in the top level Widget, where the entire layout is then defined as well. But I find this rather messy and it violates my modular construction of the Gui application quite considerably.
I am wondering, what is the way to go ?
How do you approach this in general?
-
Hi
Im not sure why you need to dispatch signals as you can connect
to any public signal/slot across a widget tree.
For the ability to alter ui->Widgets from outside, its normally
best to provide access functions to do that - to not let all know
about the private details.
I also use signals to signals to surface ui->widgets signals to world.However, if you need , its possible to do
class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = 0); ~MainWindow(); Ui::MainWindow* ui; private: //Ui::MainWindow *ui; };
But since you care about modular design, i think you might need something else?
-
thank you for your reply, I appreciate it. I think I might not have stated my issue clear enough.
Lets say I have the following code
class GuiWindow : public QMainWindow { Q_OBJECT public: explicit GuiWindow(Widget* parent = nullptr) : QMainWindow{parent}{ dock = new QDockWidget{"Snapshots", this}; mainCtrlWidget = new MainCtrlWidget{dock}; dock->setWidget(mainCtrlWidget); addDockWidget(Qt::BottomDockWidgetArea, dock); label = new QLabel; QVBoxLayout *v = new QVBoxLayout; v->addWidget(b); setLayout(v); } QDockWidget *dock; MainCtrlWidget *mainCtrlWidget; QLabel *label; };
Now, MainCtrlWidget is also a QWidget according to
class MainCtrlWidget : public QWidget { Q_OBJECT public: explicit MainCtrlWidget(QWidget* parent) : QWidget{parent}{ QHBoxLayout *layout = new QHBoxLayout; QPusbButton *button = new QPushButton; layout->addWidget(button); setLayout(layout); } };
Now the Pushbutton "button" is local to the MainCtrlWidget class, so how would I go about connecting this element with the QLabel in the top-level Gui Widget?
-
Hi
There are varies ways, depending on what goal really is.My absolute favorite is new public signals
So in MainCtrlWidget you define
public signals:
void LampON(); // some signal that describes the action/info this class provideThen internally , you do connect ( ui->button, clicked(), this, LampON)
( signal to signal. )Then from GuiWindow , you can connect the label to LampON
This way, you do not bleed implementation details all over the program and the exposed signals are
model after what the class supply of into to the world.If the need is to fiddle with the actual widget inside ui, for test or other purpose
you can use findchildren on the parent and qobject_cast to get actual type.
(this is normally not needed for a design, but can be useful) -
thank you for your reply, I appreciate it. I think I might not have stated my issue clear enough.
Lets say I have the following code
class GuiWindow : public QMainWindow { Q_OBJECT public: explicit GuiWindow(Widget* parent = nullptr) : QMainWindow{parent}{ dock = new QDockWidget{"Snapshots", this}; mainCtrlWidget = new MainCtrlWidget{dock}; dock->setWidget(mainCtrlWidget); addDockWidget(Qt::BottomDockWidgetArea, dock); label = new QLabel; QVBoxLayout *v = new QVBoxLayout; v->addWidget(b); setLayout(v); } QDockWidget *dock; MainCtrlWidget *mainCtrlWidget; QLabel *label; };
Now, MainCtrlWidget is also a QWidget according to
class MainCtrlWidget : public QWidget { Q_OBJECT public: explicit MainCtrlWidget(QWidget* parent) : QWidget{parent}{ QHBoxLayout *layout = new QHBoxLayout; QPusbButton *button = new QPushButton; layout->addWidget(button); setLayout(layout); } };
Now the Pushbutton "button" is local to the MainCtrlWidget class, so how would I go about connecting this element with the QLabel in the top-level Gui Widget?
@Sewing said in Defining deeply nested widget hierarchies:
Now the Pushbutton "button" is local to the MainCtrlWidget class, so how would I go about connecting this element with the QLabel in the top-level Gui Widget?
There are 2 distinct approaches:
- You provide a fully self-contained widgets that have all their relevant slots and signals exposed (i.e.
MainCtrlWidget
exposes all the needed signals and slots from its children). Usually this involves delegating the signals and slots (what you describe in your original post). - You don't subclass when you don't need to (and in your example above you don't need to) and you have a "controller" object/function that does all the connecting for you when creating everything. E.g.:
QMainWindow mainWindow; QWidget * mainCtrlWidget = new QWidget(); QHBoxLayout * layout = new QHBoxLayout(mainCtrlWidget); QPushButton * button = new QPushButton; layout->addWidget(button); dock->setWidget(mainCtrlWidget); mainWindow.addDockWidget(Qt::BottomDockWidgetArea, dock); QLabel * label = new QLabel; QVBoxLayout * mainLayout = new QVBoxLayout(&mainWindow); mainLayout->addWidget(label);
As you see there's not one class derived here and you can connect the signals and slots of all objects at that point. This also works with designer forms, something akin to:
QWidget * widget = new QWidget(); Ui::SomeFormClass ui; ui.setupUi(widget); // Connect signals and slots at this point while `ui` is still available.
- You provide a fully self-contained widgets that have all their relevant slots and signals exposed (i.e.