This app will be a FTP desktop application meant to transfer files between a server and a client, pretty much like Filezilla, there is no server code to show as I will not implement Network features until I have reached a comfortable design architecture.
It is a qt Gui program, and I wish to construct it in a way that has a good Mvc design principles, meaning the View class is only responsible for the UI elements, Model class will only be responsible to perform action around Data, and neither class will be aware of one another.
I did not want to construct the Model Class from inside or alongside the UI class as I feel it makes my code too co-dependent, meaning if I would want to update the UI from inside the Data class I would have to pass a pointer to the main View class,but to me that seems like a badly written design, each class should not be aware of other parts of the program and should only be responsible to their own actions, which is why I've decided to go use a single controller class that would combine SIGNALS and SLOTS from the View and Data classes.
If the View class detects a button press, it simply raises a flag (signal), the Model class has its own function responsible for processing that request (slot), and the controller class is the one that connects that signal and slot, without co-dependency of neither the View or the Model, a different example would be the Model class needing to update a UI element by writing to the screen, it would emit a signal, and the View class would catch it with the slot.
I believe this way of working makes this type of a project easier to be re-useable, as these classes are not intrinsically tied to each other, by theory if ever needed, it should be easier to re-write the Model or the View class should the need arise, but I'm curious to get some second opinions going forward.
Is this good Mvc design? it could be that this way of writing the code is simply not worth the hassle of managing the extra controller class.
Bonus question: In clientView.h i'm setting clientController to all the UI elements by setting it as a friend class, I believe this is necessary for ease of use, instead of setting up multiple redundant get pointers functions for the UI class I simply set up the controller as a friend class. do you believe this is a bad practice in this situation?
main.cpp
#include "stdafx.h"
#include "clientController.h"
int main(int argc, char *argv[])
{
clientController controller(argc, argv);
return controller.init();
}
clientController.h
#pragma once
#include "stdafx.h"
#include "clientView.h"
#include "clientModel.h"
class clientController : public QObject
{
public:
clientController(int argc, char* argv[], QWidget* parent = Q_NULLPTR);
int init();
private:
Q_OBJECT
QApplication app;
clientView window;
clientModel data;
};
clientController.cpp
#include "stdafx.h"
#include "clientController.h"
clientController::clientController(int argc, char* argv[], QWidget* parent) : QObject(parent), app(argc, argv) { }
int clientController::init()
{
//**** This is the part responsible for connecting the signals and slots for the View and the Model.
connect(window.ui.connectButton, &QPushButton::clicked, &data, &clientModel::connect);
connect(&data, &clientModel::writeTextSignal, &window, &clientView::writeTextToScreen);
window.show();
return app.exec();
}
clientView.h
#pragma once
#include "stdafx.h"
#include "ui_clientView.h"
class clientView : public QMainWindow
{
public:
explicit clientView(QWidget* parent = Q_NULLPTR);
public slots:
void writeTextToScreen(QString text);
private:
Q_OBJECT
Ui::clientView ui;
friend class clientController;
//*** Take note that i'm exposing clientController to all the UI elements.
};
clientView.cpp
#include "stdafx.h"
#include "clientView.h"
clientView::clientView(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
}
void clientView::writeTextToScreen(QString text)
{
ui.mainTextWindow->append(text);
}
clientModel.h
#pragma once
#include "stdafx.h"
class clientModel : public QObject
{
Q_OBJECT
public:
signals:
void writeTextSignal(QString text);
public slots:
void connect();
void updateData();
};
clientModel.cpp
#include "stdafx.h"
#include "clientModel.h"
void clientModel::connect()
{
qDebug() << "void clientModel::connect()";
emit writeTextSignal("Attempting Connection To FTP Server");
}
void clientModel::updateData()
{
emit writeTextSignal("Updating data");
}
auto generated qt code: ui_clientView.h
#ifndef UI_CLIENTVIEW_H
#define UI_CLIENTVIEW_H
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_clientView
{
public:
QAction *actionExit;
QAction *actionOptions;
QWidget *centralWidget;
QTextEdit *mainTextWindow;
QPushButton *connectButton;
QMenuBar *menuBar;
QMenu *menuFile;
QMenu *menuOptions;
void setupUi(QMainWindow *clientView)
{
if (clientView->objectName().isEmpty())
clientView->setObjectName(QString::fromUtf8("clientView"));
clientView->resize(640, 486);
actionExit = new QAction(clientView);
actionExit->setObjectName(QString::fromUtf8("actionExit"));
actionOptions = new QAction(clientView);
actionOptions->setObjectName(QString::fromUtf8("actionOptions"));
centralWidget = new QWidget(clientView);
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
mainTextWindow = new QTextEdit(centralWidget);
mainTextWindow->setObjectName(QString::fromUtf8("mainTextWindow"));
mainTextWindow->setGeometry(QRect(60, 30, 511, 241));
mainTextWindow->setReadOnly(true);
mainTextWindow->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
connectButton = new QPushButton(centralWidget);
connectButton->setObjectName(QString::fromUtf8("connectButton"));
connectButton->setGeometry(QRect(60, 320, 91, 31));
clientView->setCentralWidget(centralWidget);
menuBar = new QMenuBar(clientView);
menuBar->setObjectName(QString::fromUtf8("menuBar"));
menuBar->setGeometry(QRect(0, 0, 640, 21));
menuFile = new QMenu(menuBar);
menuFile->setObjectName(QString::fromUtf8("menuFile"));
menuOptions = new QMenu(menuBar);
menuOptions->setObjectName(QString::fromUtf8("menuOptions"));
clientView->setMenuBar(menuBar);
menuBar->addAction(menuFile->menuAction());
menuBar->addAction(menuOptions->menuAction());
menuFile->addAction(actionExit);
menuOptions->addAction(actionOptions);
retranslateUi(clientView);
QMetaObject::connectSlotsByName(clientView);
} // setupUi
void retranslateUi(QMainWindow *clientView)
{
clientView->setWindowTitle(QCoreApplication::translate("clientView", "client", nullptr));
actionExit->setText(QCoreApplication::translate("clientView", "Exit", nullptr));
actionOptions->setText(QCoreApplication::translate("clientView", "Options", nullptr));
connectButton->setText(QCoreApplication::translate("clientView", "Connect", nullptr));
menuFile->setTitle(QCoreApplication::translate("clientView", "File", nullptr));
menuOptions->setTitle(QCoreApplication::translate("clientView", "Tools", nullptr));
} // retranslateUi
};
namespace Ui {
class clientView: public Ui_clientView {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_CLIENTVIEW_H
stdafx.h
#include <QtWidgets>