模型视图框架其主要目的就是将数据与界面分开,起初可能会有不少小伙伴觉得不是很好理解,但在不断的深入和学习中,会慢慢掌握并理解。一旦你掌握了它,你会觉得这很简单,而且还很实用。
下面,就跟大家分享下Qt中的MVC。
在传统的Widgets应用程序中,Qt并没有将数据和窗口分开,显然这样的工作就留给了用户。而Qt的MVC框架则是将界面与数据进行了隔离,VIEW(视图)负责展示数据,MODEL(模型)负责管理数据,DELEGATE(委托)负责VIEW和MODEL的交互。
也许,有的同学看了Qt的帮助文档,洋洋洒洒一大堆,最后也看的是云里雾里,小豆君在这里就以一个简单的例子来帮助大家理解Qt中的MVC如何操作的,好让大家有个初步的认识。
1 数据和窗口在一起的困境
有这样一个需求,需要将某个班级的学生以列表的形式展示出来,并且可以向班级中添加学生。
1.1 首先创建两个类,我用StudentClass表示班级,用Student表示学生。
//学生类 class Student { public: QString getId() const; void setId(const QString &value); QString getName() const; void setName(const QString &value); //判断该学生是否有效,学号和姓名都不能为空 bool isValid() const { return !id.isEmpty() && !name.isEmpty(); } private: QString id; //学生学号 QString name; //姓名 }; //班级类 class StudentClass { public: QString getId() const; void setId(const QString &value); QString getName() const; void setName(const QString &value); //为班级添加一个新同学 bool add(const Student& student) { bool success = false; if (student.isValid()) { if (!studentMap.contains(student.getId())) { studentMap.insert(student.getId(), student); success = true; } } return success; } //获取学生列表 QList<Student> getStudentList() const { return studentMap.values(); } private: QString id; //班级id QString name;//班级名称 QMap<QString, Student> studentMap;//key-学生学号 };
1.2 接下来,创建一个窗口用来展示学生姓名列表,并且可以录入学生学号和姓名,添加学生。该窗口类中保存了班级信息。
namespace Ui { class StudentsWidget; } //学生列表窗口 class StudentsWidget : public QWidget { Q_OBJECT public: explicit StudentsWidget(QWidget *parent = 0); ~StudentsWidget(); private slots: void on_pushButton_clicked(); private: Ui::StudentsWidget *ui; StudentClass studentClass;//保存有班级数据 };
添加学生的槽函数实现:
void StudentsWidget::on_pushButton_clicked() { QString id = ui->line_studentid->text(); QString name = ui->line_name->text(); Student student; student.setId(id); student.setName(name); if (studentClass.add(student)) { ui->listWidget->addItem(student.getName()); } }
1.3 好了,我们运行程序,窗口可以方便的添加学生。
1.4 可恶的是,现在需求改变了,客户提出要再创建一个表格来展示学生的姓名和所属班级。为此,你不得不在studentClass成功添加学生后,为列表和表格控件分别添加一个对应的QListWidgetItem和QTableWidgetItem。
当然,如果只有这么两个控件需要修改,这样的事情也不算多繁琐,但是你防不住客户有更多的需求,例如他需要更多不同的表格对班级或学生数据进行展示或更改,如果一个一个改将会是一件令人抓狂的事情,并且极易出错。
2 使用MVC解决问题
为了解决这个问题,我们来采用MVC的办法,直接看代码。
2.1 首先在StudentsWidget的私有类中添加一个model成员变量,一般情况下,都会以QStandardItemModel类作为视图的model。
private: Ui::StudentsWidget *ui; StudentClass studentClass;//保存有班级数据 QStandardItemModel* model;
2.2 修改构造函数
StudentsWidget::StudentsWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StudentsWidget) { ui->setupUi(this); studentClass.setId("1"); studentClass.setName("1班"); //为model设置标题 QStringList headers; headers << "姓名" << "所属班级"; model = new QStandardItemModel; model->setHorizontalHeaderLabels(headers); //为view设置model ui->listView->setModel(model); ui->tableView->setModel(model); ui->tableView->hide(); }
2.3 修改onpushButtonclicked
model使用QStandardItem存储单个数据项
void StudentsWidget::on_pushButton_clicked() { QString id = ui->line_studentid->text(); QString name = ui->line_name->text(); Student student; student.setId(id); student.setName(name); if (studentClass.add(student)) { //创建model对应的item QStandardItem* itemName = new QStandardItem(name); QStandardItem* itemStudentClass = new QStandardItem(studentClass.getName()); QList<QStandardItem*> itemList; itemList << itemName << itemStudentClass; model->appendRow(itemList); } }
2.4 现在运行程序,添加一个学生看看是什么效果吧
每当添加一个学生时,列表和表格就会同时更新显示结果,这是不是比我们直接为列表和表格添加数据便捷多了。
MVC的使用,使我们只需要关注更新model的部分,而不需要关心view该如何变化,因为当model改变了,所有关联它的view都会得到相应的更新,最令人兴奋的是你只需要维护一个model。
最后也希望大家多多支持小豆君的创作,关注小豆君的公众号“小豆君Qt分享”,最新文章都会在公众号第一时间发布,也可加入C++\Qt交流群,一起学习。