pennyBoy
pennyBoy

Reputation: 397

MVC design pattern QT

I am currently trying to implement a MVC design pattern. I prefer an approach where the model and the view are independent from each other as well as from the controller. As shown in the image. I understand that this topic has been talked about before but based on the research I am still not entirely sure how I should connect my view to the controller to access the ui object. I did not provide the model because it seems straight forward to me at the moment. Please critique and give me improvement strategies. Again, I strictly want to use a MVC design pattern and not Model/View. MVC

main.cpp

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    View* view = new View();
    Model* model= new Model();
    Controller controller(view, model);
    controller->show();
    return a.exec();
}

View.h

namespace Ui {
    class View;
}

class View: public QMainWindow
{
    Q_OBJECT

public:
    View(QWidget *parent = Q_NULLPTR);
    ~View();
 
private:
    Ui::ViewClass *ui;
};

View.cpp

View::View(QWidget* parent)
    : QMainWindow(parent),
    ui(new Ui::ViewClass)
{
    ui->setupUi(this);
    setWindowTitle("MVC");
    ui->aDoubleSpinBox->setValue(0.0);
    ui->bDoubleSpinBox->setValue(0.0);
    ui->cDoubleSpinBox->setValue(0.0);
    ui->dDoubleSpinBox->setValue(0.0);

    ui->aDoubleSpinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ui->bDoubleSpinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ui->cDoubleSpinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ui->dDoubleSpinBox->setButtonSymbols(QAbstractSpinBox::NoButtons);

}
View::~View()
{
    delete ui;
}

Controller.cpp

class Controller : public QObject
{
    Q_OBJECT

public:
    Controller(const View& view_, const Model& model, QObject* parent = Q_NULLPTR);

public slots:
    void setA(double a);
    void setB(double b);
    void setC(double c);
    void setD(double d);

signals:
    void eA(double value);
    void eB(double value);
    void eC(double value);
    void eD(double value);

private:
    std::unique_ptr<View> view;
    std::unique_ptr<Model> model;
};

Controller.cpp

Controller::Controller(const View& view_, const Model& model_, QObject* parent)
    : QObject(parent)
{
     view = std::make_unique<View>(view_);
     model = std::make_unique<Model>(model_);

     // Here is where I am not sure how to access the ui object since it is private.
     // Do I create a public function in the View class? For eg...
     // QDoubleSpinBox* getADoubleSpinBox() { return *ui.aDoubleSpinBox; }
     

     //connect(view-> , QOverload<double>::of(&QDoubleSpinBox::valueChanged, this, 
     //&Controller::setA);


     connect(this, &Controller::eA, model.get(), &Model::setA);
     connect(this, &Controller::eB, model.get(), &Model::setB);
     connect(this, &Controller::eC, model.get(), &Model::setC);
     connect(this, &Controller::eD, model.get(), &Model::setD);   
}

void Controller::setA(double a)
{
    emit eA(a);
}

void Controller::setB(double b)
{
    emit eB(b);
}

void Controller::setC(double c)
{
    emit eC(c);
}

void Controller::setD(double d)
{
    emit eD(d);
}

Upvotes: 1

Views: 1815

Answers (1)

asitdhal
asitdhal

Reputation: 641

A typical solution can be, the view has data members exposed using Q_PROPERTY(set, get and notify). View doesn't need to know the controller at all. View is an independent object owned by the controller.

For one spinbox, the view declaration can be

namespace Ui {
    class View;
}

class View: public QMainWindow
{
    Q_OBJECT
    Q_PROPERTY(double a READ a WRITE setA NOTIFY aChanged)
public:
    View(QWidget *parent = Q_NULLPTR)
    ~View();
    double a();
    void setA(double val);
    
 signals:
    void aChanged();

private:

    Ui::ViewClass *ui;
};

And the view definition can be,

double View::a()
{
    return  ui->aDoubleSpinBox->value();
}

void View::setA(double val)
{
     ui->aDoubleSpinBox->setValue(val);
}

View::View(QWidget* parent)
: QMainWindow(parent),
ui(new Ui::ViewClass)
{
    ui->setupUi(this);
    setWindowTitle("MVC");
    connect(ui->aDoubleSpinBox, &QDoubleSpinBox::valueChanged, [&](double) {
        emit aChanged();
    });
}

The controller should connect the view notify signals to its setters. When it wants to change the view data, it should call the setter method.

In this way, the view is only responsible for showing data and user interaction and the controller is responsible for modifying data and getting updates.

Upvotes: 1

Related Questions