MVC and Qt?
-
Hello,
All my desktop software projects with an actual complex UI were made with Java.
I used the Cocoa MVC design pattern and I would like to apply a MVC with Qt now.When I search for MVC I end up on tutorial about how to use the Q...Model and Q...View but I would like advises on how to to apply MVC in general not on complex component.
Let's say I want to make a small application that let you add 2 integers.
I will have my .ui file with the xml UI and I will have my MainWindow.h/.cpp that will have the rest.Now, how to split it ? With MVC I should have a MainWindowController, a MainWindowModel and a MainWindowView.
The controller will have a lot of slots and signal (I guess)
The model will have slots to receive the controllers call, some signals to call it back, the data and the computations
The view will have all the operations to update the view and to send the signal to the controller.Is that how I should do it ? Should I put my model in a separate thread or create of thread for each complex/long computation ?
Thanks for the help,
I really just want to do it as Qt recommend it :)
-
i @rxpswiss
I think is not a good idea to try to implement the MVC pattern with Qt simply because it doesn’t use it, hence it is not design for. And trying to doing so, may not very relevant and a source of complications too.Qt uses the Model-View-Delegate for a few classes like QTableView or QTreeView but this is very different from the Cocoa counterparts and the Delegate or DataSource patterns associated with it.
The first benefit of the MVC pattern is that it prevents you from subclassing any GUI object if you don’t need it.Usually in the Qt main source, the first thing you do is to create a QWidget/QWindow subclass object:
MyWindow window(); window.show();
And as soon as you have subclassing your window, the need for a Controller object is very scarce i think.
Your Window become a sort of View-Controller and hence there is no raison not to manage your GUI objects in it.Even more, for a window coming from the Designer, the source looks like this:
MyDialog::MyDialog(QWidget *parent) : Dialog(parent), ui(new Ui::MyDialog) { ui->setupUi(this);
In this case, you will access the GUI objects through the ui pointer. If you want to access the GUI objects from a controller, you need to pass the ui pointer to it and to include the "UI_xx ".h file too.
That not impossible to do that, if you want a separate Controller object, but a think it’s irrelevant in most cases.On the other hand, if you create your own classes, you can use all the patterns you like, and it will work well because this classes will be designed to use them.
Hope it helps …
-
-
To manage your interface, there are several possibilities.
First - You can manage signals/slots in Qt Designer, see: https://doc.qt.io/qt-5.8/designer-connection-mode.html
It’s similar to the connect Action to objects mechanism in InterfaceBuilder on Mac.Second - using the ui pointer to access your widgets
#include "MyWindow.h" #include "ui_MyWindow.h" MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MWindow) { ui->setupUi(this); // do additional setup here }
Say, there are two widgets in this window, a label and a button, you can do the following:
ui->myLabel->setText("Hello"); connect(ui->myButton, SIGNAL(clicked(bool)), this, SLOT(myButtonClicked(bool)));
myLabel and myButton are names set for this widgets in Qt Designer.
Third - you can search widgets by names:
QLabel* myLabel=findChild<QLabel*>("myLabel"); QPushButton* myButton=findChild<QPushButton*>("myButton");
If the widget is not found a nullptr is return, so it’s advisable to check it with Q_ASSERT(myLabel) , etc
There’s a special type of Controller that i’m calling Manager, that’s very common, for example in a web browser, you can have an HistoryManager, a DownloadManager, a BookMarksManager , etc.
In this cases, this is the Manager who creates the Dialog/Window:MyManager::MyManager(QObject *parent) : QObject(parent) { myDialog=new MyDialog(); // myDialog is a instance variable
At this point you have no access to the widgets of the Dialog.
You can retrieve one by one the widgets by name -> tedious and nullptr only detected at compile time.
Or add access methods in MyDialog:QLabel* MyDialog::myLabel(){ return ui->myLabel; }
Only possible if your Dialog has a very few widgets …
A better solution is to pass the ui pointer directly:Ui::MyDialog* MyDialog::myDialogUi() { return ui; }
In the manager class you need to include "Ui_MyDialog.h" to gain access to the ui object tree.
As you can see, it’s possible to implement some View-Controller split with some adaptation in the classes.
If you look at the DemoBrowser example, there're several manager classes, so how they access to the widgets? simple, the manager is the Dialog : problem solved :)
-
Hi,
Just a quick note, when designing widgets, you should still design classes with clean interfaces. For example, why should any other widget know that MyDialog shows a text in a QLabel ? In the long run it could be changed to a QLineEdit. Then how many objects will you have to update in order to accommodate that change ? That's an implementation detail that mishandled will create useless tight-coupling and lead to maintenance nightmare.
-
@mpergand said in MVC and Qt?:
In this cases, this is the Manager who creates the Dialog/Window:
[snip ...]
At this point you have no access to the widgets of the Dialog.
You can retrieve one by one the widgets by name -> tedious and nullptr only detected at compile time.
Or add access methods in MyDialog:
[snip ...]
Only possible if your Dialog has a very few widgets …
A better solution is to pass the ui pointer directly:
[snip ...]
In the manager class you need to include "Ui_MyDialog.h" to gain access to the ui object tree.I don't like this one bit. For one why the hell derive from
QObject
if you're going to derive the widget too? It's a bad design. Either derive from the widget and have the widget as the one responsible for stuff, or don't and have theQObject
responsible, but not both. @SGaist's right, you're exposing way too much from the widget by passing back theui
object (i.e. exposing the widget's internals) to a third-party (your manager/controller object).There are 2 main approaches to do this properly:
- Have the widget responsible - i.e. derive from the widget, initialize the ui in the constructor and do not expose it to the outside world, which is the most common approach taken in the Qt docs.
class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget * parent = Q_NULLPTR) : QWidget(parent) { ui.setupUi(this); // Make connections, etc. } private: Ui::MyWidgetForm ui; };
- Have a controller
QObject
, that initializes the widget with the ui, makes the connections and so on, but do not derive from the widget.
class MyWidgetController : public QObject { Q_OBJECT public: MyWidgetController(QWidget * parent = Q_NULLPTR) : QObject(parent), widget(parent) { ui.setupUi(&widget); //< This can even be made with a stack local Ui::MyWidgetForm if handled properly // Make connections, etc. } private: Ui::MyWidgetForm ui; QWidget widget; };
-
ui.setupUi(&widget); //< This can even be made with a stack local Ui::MyWidgetForm if handled properly
Ha ! seems a very good idea.
I'm missing or forgot this way to setup the ui of a widget.
If i understand well, when creating the form, we have to choose "Qt Designer Form" alone, thus there will be no widget class created with it, right ? -
@mpergand said in MVC and Qt?:
If i understand well, when creating the form, we have to choose "Qt Designer Form" alone, thus there will be no widget class created with it, right ?
Yes, that is correct.
-
Good !
Thanks @kshegunov