diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.pro b/research/MVC/MVC_sample.pro new file mode 100644 index 0000000..4cb2e6c --- /dev/null +++ b/research/MVC/MVC_sample.pro @@ -0,0 +1,9 @@ + +QT += widgets uitools + +SOURCES = ModelViewController.cpp \ + MVC_sample.cpp + +HEADERS = ModelViewController.hpp + +UI = mainwindow.ui diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.pro b/research/MVC/MVC_sample.pro new file mode 100644 index 0000000..4cb2e6c --- /dev/null +++ b/research/MVC/MVC_sample.pro @@ -0,0 +1,9 @@ + +QT += widgets uitools + +SOURCES = ModelViewController.cpp \ + MVC_sample.cpp + +HEADERS = ModelViewController.hpp + +UI = mainwindow.ui diff --git a/research/MVC/MVC_sample.ui b/research/MVC/MVC_sample.ui new file mode 100644 index 0000000..49d3943 --- /dev/null +++ b/research/MVC/MVC_sample.ui @@ -0,0 +1,73 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 40 + 30 + 681 + 391 + + + + + 1 + + + + + + + 420 + 470 + 158 + 25 + + + + + + + Blah + + + + + + + Foo + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + + + + diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.pro b/research/MVC/MVC_sample.pro new file mode 100644 index 0000000..4cb2e6c --- /dev/null +++ b/research/MVC/MVC_sample.pro @@ -0,0 +1,9 @@ + +QT += widgets uitools + +SOURCES = ModelViewController.cpp \ + MVC_sample.cpp + +HEADERS = ModelViewController.hpp + +UI = mainwindow.ui diff --git a/research/MVC/MVC_sample.ui b/research/MVC/MVC_sample.ui new file mode 100644 index 0000000..49d3943 --- /dev/null +++ b/research/MVC/MVC_sample.ui @@ -0,0 +1,73 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 40 + 30 + 681 + 391 + + + + + 1 + + + + + + + 420 + 470 + 158 + 25 + + + + + + + Blah + + + + + + + Foo + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + + + + diff --git a/research/MVC/ModelViewController.cpp b/research/MVC/ModelViewController.cpp new file mode 100644 index 0000000..e42c78d --- /dev/null +++ b/research/MVC/ModelViewController.cpp @@ -0,0 +1,91 @@ +/* + + MVC base classes + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include "ModelViewController.hpp" +#include + + +namespace +{ + // Based on: QMetaObject::connectSlotsByName + // Searches rootObject and all it's descendants for signals, and connects them to any slots in receiverObject where the signatures match + // the pattern on__ + void connectSlotsByName(QObject* rootObject, const QObject* receiverObject) + { + const QMetaObject* slotInfo = receiverObject->metaObject(); + if (slotInfo && rootObject) + { + const QObjectList list = rootObject->findChildren(QString()) << rootObject; // look for signals in rootObject and all it's descendants + // for each method/slot of mo ... + for (int i = 0; i < slotInfo->methodCount(); ++i) + { + const QByteArray slotSignature = slotInfo->method(i).methodSignature(); + const char* slot = slotSignature.constData(); + // ...that starts with "on_", ... + if (slot[0] == 'o' && slot[1] == 'n' && slot[2] == '_') + { + // ...we check each object in our list, ... + for (int j = 0; j < list.count(); ++j) + { + const QObject* signalObject = list.at(j); + const QByteArray coName = signalObject->objectName().toLatin1(); + // ...discarding those whose objectName is not fitting the pattern "on__...", ... + if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size() + 3] != '_') + continue; + std::string signal = slot + coName.size() + 4; + // we attempt to connect it... + rootObject->connect(signalObject, ("2" + signal).c_str(), receiverObject, (std::string("1") + slot).c_str()); + } + } + } + } + } + + class LayoutWrapper : public QVBoxLayout + { + public: + LayoutWrapper(QWidget* parent, QWidget* view) + : QVBoxLayout(parent) + { + addWidget(view); // view is now a child of parent + parent->resize(view->size()); // resize parent to the size of view + } + }; +} + + +ModelBase::ModelBase() +{ + setObjectName("model"); +} + + +ViewBase::ViewBase(const char* uiFileName) +{ + setObjectName("view"); + new LayoutWrapper(this, QUiLoader{}.load(&QFile(uiFileName))); +} + + +ControllerBase::ControllerBase(ModelBase* model, ViewBase* view) + : _model(model) + , _view(view) + , _controller(this) +{ + setObjectName("controller"); + new LayoutWrapper(this, view); // view is now a child of controller + view->layout()->addWidget(model); // model is now a child of view +} + + +void ControllerBase::start() +{ + ::connectSlotsByName(this, _model); + ::connectSlotsByName(this, _view); + ::connectSlotsByName(this, _controller); + show(); +} diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.pro b/research/MVC/MVC_sample.pro new file mode 100644 index 0000000..4cb2e6c --- /dev/null +++ b/research/MVC/MVC_sample.pro @@ -0,0 +1,9 @@ + +QT += widgets uitools + +SOURCES = ModelViewController.cpp \ + MVC_sample.cpp + +HEADERS = ModelViewController.hpp + +UI = mainwindow.ui diff --git a/research/MVC/MVC_sample.ui b/research/MVC/MVC_sample.ui new file mode 100644 index 0000000..49d3943 --- /dev/null +++ b/research/MVC/MVC_sample.ui @@ -0,0 +1,73 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 40 + 30 + 681 + 391 + + + + + 1 + + + + + + + 420 + 470 + 158 + 25 + + + + + + + Blah + + + + + + + Foo + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + + + + diff --git a/research/MVC/ModelViewController.cpp b/research/MVC/ModelViewController.cpp new file mode 100644 index 0000000..e42c78d --- /dev/null +++ b/research/MVC/ModelViewController.cpp @@ -0,0 +1,91 @@ +/* + + MVC base classes + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include "ModelViewController.hpp" +#include + + +namespace +{ + // Based on: QMetaObject::connectSlotsByName + // Searches rootObject and all it's descendants for signals, and connects them to any slots in receiverObject where the signatures match + // the pattern on__ + void connectSlotsByName(QObject* rootObject, const QObject* receiverObject) + { + const QMetaObject* slotInfo = receiverObject->metaObject(); + if (slotInfo && rootObject) + { + const QObjectList list = rootObject->findChildren(QString()) << rootObject; // look for signals in rootObject and all it's descendants + // for each method/slot of mo ... + for (int i = 0; i < slotInfo->methodCount(); ++i) + { + const QByteArray slotSignature = slotInfo->method(i).methodSignature(); + const char* slot = slotSignature.constData(); + // ...that starts with "on_", ... + if (slot[0] == 'o' && slot[1] == 'n' && slot[2] == '_') + { + // ...we check each object in our list, ... + for (int j = 0; j < list.count(); ++j) + { + const QObject* signalObject = list.at(j); + const QByteArray coName = signalObject->objectName().toLatin1(); + // ...discarding those whose objectName is not fitting the pattern "on__...", ... + if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size() + 3] != '_') + continue; + std::string signal = slot + coName.size() + 4; + // we attempt to connect it... + rootObject->connect(signalObject, ("2" + signal).c_str(), receiverObject, (std::string("1") + slot).c_str()); + } + } + } + } + } + + class LayoutWrapper : public QVBoxLayout + { + public: + LayoutWrapper(QWidget* parent, QWidget* view) + : QVBoxLayout(parent) + { + addWidget(view); // view is now a child of parent + parent->resize(view->size()); // resize parent to the size of view + } + }; +} + + +ModelBase::ModelBase() +{ + setObjectName("model"); +} + + +ViewBase::ViewBase(const char* uiFileName) +{ + setObjectName("view"); + new LayoutWrapper(this, QUiLoader{}.load(&QFile(uiFileName))); +} + + +ControllerBase::ControllerBase(ModelBase* model, ViewBase* view) + : _model(model) + , _view(view) + , _controller(this) +{ + setObjectName("controller"); + new LayoutWrapper(this, view); // view is now a child of controller + view->layout()->addWidget(model); // model is now a child of view +} + + +void ControllerBase::start() +{ + ::connectSlotsByName(this, _model); + ::connectSlotsByName(this, _view); + ::connectSlotsByName(this, _controller); + show(); +} diff --git a/research/MVC/ModelViewController.hpp b/research/MVC/ModelViewController.hpp new file mode 100644 index 0000000..b72fefb --- /dev/null +++ b/research/MVC/ModelViewController.hpp @@ -0,0 +1,38 @@ +/* + + MVC base classes + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#pragma once + + +#include + + +class ModelBase : public QWidget +{ +public: + ModelBase(); +}; + + +class ViewBase : public QWidget +{ +public: + ViewBase(const char* uiFileName); +}; + + +class ControllerBase : public QWidget +{ +public: + ControllerBase(ModelBase* model, ViewBase* view); + void start(); + + ModelBase* _model; + ViewBase* _view; + ControllerBase* _controller; +}; + diff --git a/research/MVC/MVC_sample.cpp b/research/MVC/MVC_sample.cpp new file mode 100644 index 0000000..8fb300f --- /dev/null +++ b/research/MVC/MVC_sample.cpp @@ -0,0 +1,119 @@ +/* + + MVC sample/demo app + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include +#include +#include +#include "ModelViewController.hpp" + +/* + +This example shows how using the MVC classes, there is automatic +creation of the UI from a .ui file, and the signals and slots to +the created view and between the model, view and controller are +connected together. + +In fact all combinations of pairs of model, view, controller are +hooked up if provided, however the example provided below only +shows the traditional MVC style where the model is only manipulated +by the controller, and the view is only updated by the model. + +*/ + +class Model : public ModelBase +{ + Q_OBJECT +public: + Model() + { + timerId1 = startTimer(5000); + timerId2 = startTimer(7000); + } + + void timerEvent(QTimerEvent* te) + { + if (te->timerId() == timerId1) + { + emit dataChanged(); + } + else if (te->timerId() == timerId2) + { + emit dataChanged(); + } + } + +private slots: + void on_controller_updateSomething(std::string s) // manipulates + { + QMessageBox::information(nullptr, "Info", ("model needs update " + s).c_str(), "Okay"); + } + +signals: + void dataChanged(); + +private: + int timerId1; + int timerId2; +}; + + +class View : public ViewBase +{ + Q_OBJECT +public: + View() + : ViewBase("MVC_sample.ui") + { + } + +private slots: + void on_model_dataChanged() // updates + { + // refresh view + QMessageBox::information(nullptr, "Info", "view needs update", "Okay"); + } +}; + + +class Controller : public ControllerBase +{ + Q_OBJECT +public: + Controller(ModelBase* model, ViewBase* view) + : ControllerBase(model, view) + { + } + +private slots: + void on_blahButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 1", "Okay"); + updateSomething("1"); + } + + void on_fooButton_clicked() // uses + { + QMessageBox::information(nullptr, "Info", "user clicked button 2", "Okay"); + updateSomething("2"); + } + +signals: + void updateSomething(std::string); +}; + + + + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + Controller modelViewController(new Model(), new View()); + modelViewController.start(); + return app.exec(); +} + +#include "MVC_sample.moc" diff --git a/research/MVC/MVC_sample.pro b/research/MVC/MVC_sample.pro new file mode 100644 index 0000000..4cb2e6c --- /dev/null +++ b/research/MVC/MVC_sample.pro @@ -0,0 +1,9 @@ + +QT += widgets uitools + +SOURCES = ModelViewController.cpp \ + MVC_sample.cpp + +HEADERS = ModelViewController.hpp + +UI = mainwindow.ui diff --git a/research/MVC/MVC_sample.ui b/research/MVC/MVC_sample.ui new file mode 100644 index 0000000..49d3943 --- /dev/null +++ b/research/MVC/MVC_sample.ui @@ -0,0 +1,73 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 40 + 30 + 681 + 391 + + + + + 1 + + + + + + + 420 + 470 + 158 + 25 + + + + + + + Blah + + + + + + + Foo + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + + + + diff --git a/research/MVC/ModelViewController.cpp b/research/MVC/ModelViewController.cpp new file mode 100644 index 0000000..e42c78d --- /dev/null +++ b/research/MVC/ModelViewController.cpp @@ -0,0 +1,91 @@ +/* + + MVC base classes + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#include "ModelViewController.hpp" +#include + + +namespace +{ + // Based on: QMetaObject::connectSlotsByName + // Searches rootObject and all it's descendants for signals, and connects them to any slots in receiverObject where the signatures match + // the pattern on__ + void connectSlotsByName(QObject* rootObject, const QObject* receiverObject) + { + const QMetaObject* slotInfo = receiverObject->metaObject(); + if (slotInfo && rootObject) + { + const QObjectList list = rootObject->findChildren(QString()) << rootObject; // look for signals in rootObject and all it's descendants + // for each method/slot of mo ... + for (int i = 0; i < slotInfo->methodCount(); ++i) + { + const QByteArray slotSignature = slotInfo->method(i).methodSignature(); + const char* slot = slotSignature.constData(); + // ...that starts with "on_", ... + if (slot[0] == 'o' && slot[1] == 'n' && slot[2] == '_') + { + // ...we check each object in our list, ... + for (int j = 0; j < list.count(); ++j) + { + const QObject* signalObject = list.at(j); + const QByteArray coName = signalObject->objectName().toLatin1(); + // ...discarding those whose objectName is not fitting the pattern "on__...", ... + if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size()) || slot[coName.size() + 3] != '_') + continue; + std::string signal = slot + coName.size() + 4; + // we attempt to connect it... + rootObject->connect(signalObject, ("2" + signal).c_str(), receiverObject, (std::string("1") + slot).c_str()); + } + } + } + } + } + + class LayoutWrapper : public QVBoxLayout + { + public: + LayoutWrapper(QWidget* parent, QWidget* view) + : QVBoxLayout(parent) + { + addWidget(view); // view is now a child of parent + parent->resize(view->size()); // resize parent to the size of view + } + }; +} + + +ModelBase::ModelBase() +{ + setObjectName("model"); +} + + +ViewBase::ViewBase(const char* uiFileName) +{ + setObjectName("view"); + new LayoutWrapper(this, QUiLoader{}.load(&QFile(uiFileName))); +} + + +ControllerBase::ControllerBase(ModelBase* model, ViewBase* view) + : _model(model) + , _view(view) + , _controller(this) +{ + setObjectName("controller"); + new LayoutWrapper(this, view); // view is now a child of controller + view->layout()->addWidget(model); // model is now a child of view +} + + +void ControllerBase::start() +{ + ::connectSlotsByName(this, _model); + ::connectSlotsByName(this, _view); + ::connectSlotsByName(this, _controller); + show(); +} diff --git a/research/MVC/ModelViewController.hpp b/research/MVC/ModelViewController.hpp new file mode 100644 index 0000000..b72fefb --- /dev/null +++ b/research/MVC/ModelViewController.hpp @@ -0,0 +1,38 @@ +/* + + MVC base classes + Qt Based Implementation + Copyright (C) 2020, John Ryland + +*/ +#pragma once + + +#include + + +class ModelBase : public QWidget +{ +public: + ModelBase(); +}; + + +class ViewBase : public QWidget +{ +public: + ViewBase(const char* uiFileName); +}; + + +class ControllerBase : public QWidget +{ +public: + ControllerBase(ModelBase* model, ViewBase* view); + void start(); + + ModelBase* _model; + ViewBase* _view; + ControllerBase* _controller; +}; + diff --git a/research/MVC/setup.bat b/research/MVC/setup.bat new file mode 100644 index 0000000..24ee6aa --- /dev/null +++ b/research/MVC/setup.bat @@ -0,0 +1,9 @@ +@echo off + +rem "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat" +set QTDIR=C:\Users\John\Documents\Code\Qt-5.13.2\5.13.2\msvc2017_64 +set PATH=%QTDIR%\bin;%PATH% + +qmake.exe -tp vc + +pause