Очень простое приложение "модель-вид" с сигналами и слотами в классе модели
Вот этот пример - неплохой, он даже с отдельным классом-контроллером, и теория вполне правильная, только вот контроллеров в "чистом виде" в QT всё равно нет и реально во многих случаях мы пользуемся парадигмой "модель-вид", а не "модель-вид-контроллер".
Напишем небольшое приложение, придерживающееся этих простых, в общем-то, правил отделения логики проекта (модели) от внешнего представления данных (вида).
Нашей моделью будет простейший конвертер, умеющий умножать некоторую величину value
на коэффициент пересчёта rate
, значения value
и rate
могут быть вещественными числами и работать с разными типами входных данных (как минимум, числом и строкой QString
) с помощью механизма сигналов и слотов.
При этом, модель никак не управляет отображением своих данных, а только предоставляет методы для доступа к ним и отправляет "во внешний мир" сигнал, если результат расчёта изменился.
Ниже показан файл model.h
, в котором описан класс модели.
#ifndef MODEL_H #define MODEL_H #include <QObject> class Model : public QObject { Q_OBJECT private: double value, //Пересчитываемая величина rate; //Коэффициент пересчёта public: explicit Model (QObject *parent = 0, double value = 0, double rate = 0); double getInputValue(void); double getExchangeRate(void); signals: //Один сигнал - "результат изменился" void resultChanged (double); public slots: void setInputValue(double); //Установить величину для пересчёта void setInputValue(QString); void setExchangeRate(double); //Поменять коэффициент пересчёта void setExchangeRate(QString); }; #endif // MODEL_H
В файле model.cpp
реализуем логику приложения.
#include "model.h" Model::Model (QObject *parent, double v, double r) : QObject(parent), value(v), rate(r) {} double Model::getInputValue(void) { return value; } double Model::getExchangeRate(void) { return rate; } void Model::setInputValue(double v) { if (value == v) return; //Величина не менялась - выйти value = v; emit resultChanged(value*rate); //Иначе послать сигнал, что результат стал другим } void Model::setInputValue(QString v) { bool ok = false; double vs = v.toDouble(&ok); if (ok) this->setInputValue(vs); } void Model::setExchangeRate(double r) { if (rate == r) return; rate = r; emit resultChanged(value*rate); } void Model::setExchangeRate(QString r) { bool ok = false; double rs = r.toDouble(&ok); if (ok) this->setExchangeRate(rs); }
Вид должен иметь, как минимум, конструктор, метод для подключения модели и слот, которым он подцепится к сигналу
resultChanged
. В этом слоте он проверит также, нет ли нужды обновить представление других данных модели (для простоты будем делать это всегда).
Напишем два альтернативных вида, первый из них (View
) будет использовать компоненты QDoubleSpinBox
для ввода и метку QLabel
для вывода, а второй (SimpleView
) и для ввода, и для вывода предназначит обычные текстовые поля QLineEdit
.
Файл view.h
#ifndef VIEW_H #define VIEW_H #include <QWidget> #include <QtWidgets> #include "model.h" class View : public QWidget { Q_OBJECT private: QDoubleSpinBox *converted; QDoubleSpinBox *rate; QLabel *convertedValue; Model * model; public: explicit View(QWidget *parent = 0); void setModel(Model *); signals: public slots: void convertedValueSlot(double); }; #endif // VIEW_H
Файл view.cpp
#include "view.h" void View::setModel(Model * model_) { model = model_; connect(converted, SIGNAL(valueChanged(double)), model, SLOT(setInputValue(double))); connect(rate, SIGNAL(valueChanged(double)), model, SLOT(setExchangeRate(double))); connect(model, SIGNAL(resultChanged(double)), this, SLOT(convertedValueSlot(double))); /* //В "новом стиле" и с опцией проекта //QMAKE_CXXFLAGS += -std=gnu++11 //эти строки в версиях QT младше 5.10 выглядели бы так: QObject::connect (converted,static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), model, &Model::setInputValue); QObject::connect (rate, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), model, &Model::setExchangeRate); QObject::connect (model, static_cast<void (Model::*)(double)>(&Model::resultChanged), this, &View::convertedValueSlot); */ } View::View(QWidget *parent) : QWidget(parent) { converted = new QDoubleSpinBox(this); converted->setDecimals(2); converted->setMaximum(1e6); converted->setSingleStep(0.5); rate = new QDoubleSpinBox(this); rate->setDecimals(2); rate->setMaximum(1000); rate->setSingleStep(0.5); convertedValue = new QLabel(this); QGridLayout *layout = new QGridLayout(this); layout->setSpacing(4); layout->addWidget(new QLabel(QString(tr("Сумма"))),0,0,1,1); layout->addWidget(converted,0,1,1,1); layout->addWidget(new QLabel(QString(tr("Коэффициент"))),1,0,1,1); layout->addWidget(rate,1,1,1,1); layout->addWidget(convertedValue,2,0,1,2); setLayout(layout); } void View::convertedValueSlot(double d) { converted->setValue(model->getInputValue()); rate->setValue(model->getExchangeRate()); convertedValue->setText("="+QString::number(d,'f',2)); }
Файл simpleview.h
#ifndef SIMPLEVIEW_H #define SIMPLEVIEW_H #include <QWidget> #include <QtWidgets> #include "model.h" class SimpleView : public QWidget { Q_OBJECT public: explicit SimpleView(QWidget *parent = 0); void setModel(Model *); private: QLineEdit *converted, *rate, *convertedValue; Model * model; public slots: void convertedValueSlot(double); }; #endif // SIMPLEVIEW_H
Файл simpleview.cpp
#include "simpleview.h" void SimpleView::setModel(Model * model_) { model = model_; connect(converted, SIGNAL(textChanged(QString)), model, SLOT(setInputValue(QString))); connect(rate, SIGNAL(textChanged(QString)), model, SLOT(setExchangeRate(QString))); connect(model, SIGNAL(resultChanged(double)), this, SLOT(convertedValueSlot(double))); } SimpleView::SimpleView(QWidget *parent) : QWidget(parent) { converted = new QLineEdit(QString("0"),this); rate = new QLineEdit(QString("0"),this); convertedValue = new QLineEdit(QString("0"),this); convertedValue->setReadOnly(true); QHBoxLayout *layout = new QHBoxLayout(this); layout->setSpacing(4); layout->addWidget(new QLabel(QString(tr("Сумма")))); layout->addWidget(converted); layout->addWidget(new QLabel(QString(tr("Коэффициент")))); layout->addWidget(rate); layout->addWidget(new QLabel(QString(tr("Результат")))); layout->addWidget(convertedValue); setLayout(layout); } void SimpleView::convertedValueSlot(double d) { QString str; converted->setText(str.setNum(model->getInputValue())); rate->setText(str.setNum(model->getExchangeRate())); convertedValue->setText(QString::number(d)); }
Главной программе останется создать экземпляр модели, экземпляры видов и запустить основное приложение, вот листинг файла main.cpp
:
#include <QApplication> #include "model.h" #include "view.h" #include "simpleview.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Model *m = new Model(); View *v = new View(); v->setModel(m); v->show(); SimpleView *w = new SimpleView(); w->setModel(m); w->show(); return a.exec(); }
Вот что получилось в работе:
скриншот работы приложения
Так как оба вида используют один и тот же экземпляр модели, они будут косвенно влиять на отображение данных модели друг другом, если мы изменим что-то в одном представлении, обновится и другое. Иногда это может приводить к мелким коллизиям, скажем, в текстовое поле ввода можно ввести большой коэффициент пересчёта, а в соответствующем QDoubleSpinBox
стоит ограничение "1000" и результаты преобразования в двух видах будут выглядеть разными. Но это, в любом случае, проблемы представлений, а не модели :)
Скачать этот проект QT5 в архиве .zip, папка уже создана внутри архива (4 Кб)
21.04.2019, 14:45 [1576 просмотров]