3

Following good MVC practices, where should the signal slot connection happen? I feel like it should be outside the view class, possibly in a dedicated controller class that has pointers to the view and model objects.

This raises a complication though, lots of signals may come from objects within the view class (such as a QPushButton). This means I have to allow the controller to break the interface of the view class and access its members in order to set up the connect(). The alternative is to connect() things in the view class, but then it's directly interfacing with the Model class, which defeats the purpose of trying to separate them through MVC.

Blacktempel
  • 3,935
  • 3
  • 29
  • 53
daj
  • 6,962
  • 9
  • 45
  • 79
  • You could have a vector in your view that only contains active signal objects, like `QVector activeSignals;` and let the controller deal with the connections. – Frederik.L Jul 25 '14 at 05:54
  • There are SIGNAL-SIGNAL connections available to pass those signals. But i believe (at least thats what we are doing often) having a slot to handle the push itself and calling a sync-to-model/sync-from-model makes things a lot easier. – Sebastian Lange Jul 25 '14 at 05:57
  • Consider creating an abstract interface class to derive from that provides some general methods to access specific UI elements like `GetButton(int WhatButton)`. Then you can access specific elements i.e. only those that was defined in interface class. – bkausbk Jul 25 '14 at 06:09
  • 1
    in Qt usually parent object connects signal and slots of children objects. – Marek R Jul 25 '14 at 08:10
  • @Marek how would this work? The child may have UI elements the parent class doesn't. Also, this would also mean that the view object (via inheritance) would have a pointer to the model object, breaking the separation between the model and the view. – daj Jul 25 '14 at 12:35
  • View has to know the model so it could know how to represent it, so yes view has a pointer to a model (model doesn't "know" about view existence). – Marek R Jul 25 '14 at 13:21

1 Answers1

3

One of the purposes of object-oriented design is encapsulation. To the user of the view class it is irrelevant how you implemented the class. The design should focus on a convenient interface first. You can then have one implementation that uses widget controls, another can be ncurses-based, yet another can use QML, etc. This means that ideally nothing about the class's insides should be visible at the level of the interface.

The whole point of the signal-slot mechanism was, in fact, decoupling the classes involved in the connections: the classes being connected need to know nothing about each other. It is thus a sensible starting point, and indeed usually correct, to set up the connections from outside of the classes.

To help you in this task, you can leverage the signal-signal connections. In Qt, a signal is just a method whose implementation is machine-generated. It has a different designation in the metadata, but it is an invokable method just as a slot is. So, when considered as a target of a connection, the signals and slots are equivalent. You can connect signals to signals. A signal-signal connection simply invokes the target signal method when the source signal method is invoked.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I understand the motivation for signals and slots, but in terms of design, where should the signal-slot connection be made? I.e. where should the connect() functions get called?. I could preserve the encapsulation of the view class by chaining signals and having the button emit a signal to a slot within the view class which emits another signal, but this two-step signaling is a lot of verbosity to add for every piece of interaction within each view class. – daj Jul 25 '14 at 12:32
  • @daj It's not much verbosity unless your views are trivial. The signal-slot connection between a model and a view should be made at the point close to the instantiation of the latter of the view or model. If both view and model are instantiated in `main()`, the connections belong there (or in a setup helper function). You can easily reduce the verbosity by having helpers that auto-forward internal signals to external signals, and vice-versa, based on object names (`QObject::objectName`). – Kuba hasn't forgotten Monica Jul 25 '14 at 13:17
  • what do you mean by helper? a method? I don't see how would you avoid having to set up the internal signal/slot pair within the view for each forwarded signal? – daj Jul 25 '14 at 23:12
  • @daj Signals, slots and their connections are all under programmatic control. You can enumerate them and create connections on the fly. A *proper* (not a hack!) view **must** hide that there is a "start" button in it that gets "clicked". You should have a `started` signal. Internally, you have to connect `ui->startButton` signal `clicked()` to the view's `started()` signal. This shouldn't be a burden unless your view is trivial. You'll need the connection somewhere, better have it inside the view, not outside of it. Again, if you worry about it, you have trivial code, so don't worry. – Kuba hasn't forgotten Monica Jul 25 '14 at 23:23