Implementing an MVC Model with the Qt C++ Framework

Model-View-Controller (MVC) is a design pattern frequently used in the development of all types of software. Sometimes, it is termed as architectural pattern (Wikipedia), a wider connotation of the design pattern that takes into account various software design principles not addressed by a simple design pattern. MVC began as a framework in SmallTalk, which became so popular and widely used that the character exhibits semblance to many software engineering principles as it evolved. This is a particular interest of study for the software engineering purist. Here, we begin with intuitive understanding of MVC to its actual implementation in the Qt C++ Framework.

MVC Overview

The structure of MVC, as the name suggests, consists of three decoupled term, called model, view, and controller. And decouple it is—as you apply the idea, you segment your code into three sections based on the principle that one segment must not be dependent on another to function. That means the model section should be independent of view and controller and so on... But, there is a catch there; in practice, it is found that the main stress is in decoupling the model from the view. Such strict rules cannot be applied to the controller. It often dwindles between the model and the view, or sometimes it is inseparable from the view. It is not always possible to separate the controller from either two although our intention should be to make them as distinct as much as possible. But, one thing for sure is that the view and the model should be so decoupled that if any changes are made to the view that must not trigger a ripple effect on the model code.

MVC1
Figure 1: The structure of MVC

When we talk of MVC, we talk of three-tier architecture, where the model generally represents the data layer, the view represents the presentation layer, and the controller represents the business layer. The data layer consists of entity classes such as an Employee class to an employee table that holds the information detail. The presentation layer consists of the interface that the client uses to interact with the data layer, such as a UI form or HTML page. And, the business layer represents the rules of the game; that means, the defining terms of interaction between the presentation layer and the data layer.

MVC2
Figure 2: MVC's tiers

The MVC pattern can be extended to work in more than three layers. That means it can also be used for n-tier models. Here, what we do is keep on decoupling the middle tier business logic into n-segments. You may argue that it is still a three-tier architecture, head-torso-tail. Yes, it is; the torso simply got elongated, or you can imagine new compartment coaches are added to accommodate more passengers. And, in the case of MVC, multiple business tiers get added to accommodate more complex logic.

MVC3
Figure 3: Adding more tiers

Intuitively, this is as far as the MVC principle goes.

MVC in the Qt Framework

Thus, according to the classic definition, the model keeps the data and the view renders it to display units. The controller helps in mapping the two layers. When the client wants to change a certain record, the controller holds the key mechanism to initiate the change. So far, so good.

However, the Qt Framework has a slightly different perspective. Instead of having controller classes, the view handles data updating via delegates. It has two primary objectives: one, to help the view render each value; two, help the view when the user wants to make some changes. So, in a way, the controller has merged with the view and the view does some of the controller's job through delegates. So, the idea becomes this.

MVC4
Figure 4: The Qt framework

Qt offers three views by default: a list a table and a tree represented by the QListView, QTableView, and QtreeView classes, respectively.

Models and Views

Models are used to store data items and act as a backing entity for views. Qt provides view widgets such as QListView, QTreeView, and QTableView. Each of them has its own special way of representing data items. For example, QtreeView shows a tree hierarchy as a horizontal series of lists. All these views must be backed by a model. There are several build in models in the framework. However, if these seem insufficient, we can always build a customized model.

Apart from these widgets, there are many convenience widgets that have their own built-in models, such as QTableWidget, QListWidget, and QTreeWidget. Unlike the view widgets (QTreeView, QTableView,...), the convenience widgets do not need to be backed by a model and can be used directly. The main advantages of using convenience widgets is that it requires the least amount of effort to work with them.

MVC5
Figure 5: The Qt widgets

The APIs to represent data through models are of two types; one is a tabular representation of data in the form of rows and columns and another is a hierarchical representation of data. Also, the model API can be used either to create a custom model or to use the predefined set of models.

The hierarchy of model classes in the Qt API framework is as follows:

MVC6
Figure 6: The hierarchy of model classes in the Qt API framework

The hierarchy of view classes in the Qt API framework is as follows:

MVC7
Figure 7: The hierarchy of view classes in the Qt API framework

Delegates

Delegates provide a control over the presentation of items displayed in the view. It enables specific widgets to be deputed as editors for editable items in the model. Qt's predefined delegates use built-in widgets to edit a particular data type. This in a way provides immense flexibility and trouble at the same time. Flexibility because, delegates get their relevant widgets as its editor. Trouble because, what if the data item does not fit to a particular widget that a predefined depute as its editor? In such a case, the only way out is to resort to building custom delegates. And, like any customization, creating a custom delegate is a complex process. However, such cases are not rare but infrequent, where we can safely use built in delegates.

The hierarchy of delegate classes in the Qt API framework is as follows:

MVC8
Figure 8: The hierarchy of delegate classes in the Qt API framework

A Quick Example

The following example illustrates how predefined QSqlTableModel is used in association with the built-in QTableView. Delegation is automatically handled when the table is double clicked. Relevant widgets act as an editor to the data type. The CRUD operations are performed on the model and the view automatically reflects the changes.

Qt Designer is used to design the UI layout; as a result, most of the design properties are not shown here. However, it can be understood very easily from the widget.h, widget.cpp code and also the output window can also be taken as a hint on the design details.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtSql>
#include <QtGui>

namespace Ui {
   class Widget;
}

class Widget : public QWidget
{
   Q_OBJECT

   public:
      explicit Widget(QWidget *parent = 0);
      ~Widget();
      void populateDataItem();

   public slots:
      void addnew();
      void remove();
      void save();

   private:
      Ui::Widget *ui;
      QSqlTableModel *model;
      QSqlDatabase db;

      bool initDatabase();
      void closeDatabase();
};
#endif   // WIDGET_H

Listing 1: widget.h

#include "widget.h"
#include "ui_widget.h"

#include <QMessageBox>

Widget::Widget(QWidget *parent) : QWidget(parent),
   ui(new Ui::Widget)
{
   ui->setupUi(this);
   initDatabase();
   populateDataItem();
   connect(ui->deleteButton, SIGNAL(clicked(bool)),this,
      SLOT(remove()));
   connect(ui->addButton, SIGNAL(clicked(bool)),this,
      SLOT(addnew()));
   connect(ui->submitButton, SIGNAL(clicked(bool)),this,
      SLOT(save()));
}

void Widget::populateDataItem(){
   model=new QSqlTableModel(0, db);
   model->setTable("books");
   model->setEditStrategy(QSqlTableModel::OnManualSubmit);
   model->select();
   model->setHeaderData(0, Qt::Horizontal, tr("ISBN"));
   model->setHeaderData(1, Qt::Horizontal, tr("Title"));
   model->setHeaderData(2, Qt::Horizontal, tr("Edition"));
   model->setHeaderData(3, Qt::Horizontal, tr("Publisher"));
   model->setHeaderData(4, Qt::Horizontal, tr("Copyright"));
   model->setHeaderData(5, Qt::Horizontal, tr("Price"));
   model->setHeaderData(6, Qt::Horizontal, tr("Authors"));

   ui->tableView->setModel(model);
   ui->tableView->setAlternatingRowColors(true);
}

bool Widget::initDatabase(){
   db=QSqlDatabase::addDatabase("QMYSQL","MyLibrary");
   db.setHostName("localhost");
   db.setDatabaseName("test");
   db.setUserName("user1");
   db.setPassword("secret");
   return db.open();
}

void Widget::closeDatabase(){
   db.close();
}

Widget::~Widget()
{
   closeDatabase();
   delete model;
   delete ui;
}

void Widget::addnew(){
   int row=0;
   model->insertRows(row,1);
   model->setData(model->index(row,0),ui->edIsbn->text());
   model->setData(model->index(row,1),ui->edTitle->text());
   model->setData(model->index(row,2),ui->edEdition->text());
   model->setData(model->index(row,3),ui->edPublisher->text());
   model->setData(model->index(row,4),ui->edCopyright->text());
   model->setData(model->index(row,5),ui->dspinPrice->value());
   model->setData(model->index(row,6),ui->edAuthors->text());
}

void Widget::remove(){
   int row=ui->tableView->currentIndex().row();
   if(QMessageBox::question(0,"Delete", "Record no. "
                            +QString::number(row+1)
                            +" will be deleted. Are you sure?",
                            QMessageBox::No,QMessageBox::Yes)==
                            QMessageBox::Yes){
      model->removeRow(row);
   }
}

void Widget::save(){
   bool flag=model->submitAll();
   if(flag==false)
      QMessageBox::critical(0,"Failed", "cannot save changes.");
   else
      QMessageBox::information(0,"success",
      "changes saved successfully.");
}

Listing 2: widget.cpp

#include "widget.h"
#include <QApplication>
#include <QStyleFactory>

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QApplication::setStyle(QStyleFactory::create("fusion"));
   Widget w;
   w.show();
   return a.exec();
}

Listing 3: main.cpp

Output

MVC9
Figure 9: The programs' output

Conclusion

The Qt Framework handles the MVC pattern implicitly, especially when we work with pre-built APIs of the model, view, and delegation classes. However, it is equally possible to design an application in Qt according to the classical definition of MVC. The APIs are flexible enough to design an application as one likes, whether you use the convenient classes or not; it is not at all decisive for a good design.



About the Author

Manoj Debnath

manojdebnath@fastmail.fm

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date