Table Models

It would be useful to have an editable QTableView for viewing and editing a collection of DataObjects. For this, we extend and customize the QAbstractTable Model. See Example 17.17.

Example 17.17. src/libs/dataobjects/dataobjecttablemodel.h

[ . . . . ]

class DataObjectTableModel : public QAbstractTableModel {
 Q_OBJECT
 public:
 DataObjectTableModel(DataObject* headerModel = 0);
 virtual DataObject* record(int rowNum) const ;
 virtual bool insertRecord(DataObject* newRecord,
 int position = -1,
 const QModelIndex& = QModelIndex());
 QStringList toStringList() const;
 QString toString() const;
 virtual int fieldIndex(QString fieldName) const;
 virtual ~DataObjectTableModel();
[ . . . . ]

 public slots:
 void reset();
 void checkDirty();
 protected slots:
 void changeProperty(const QString&, const QVariant&);

 protected:
 QList m_Data;
 QStringList m_Headers;
 DataObject* m_Original;
 QTimer m_Timer;
 bool m_Dirty;
 void extractHeaders(DataObject* hmodel);
 public:
 DataObjectTableModel& operator<<(DataObject* newObj) {
 insertRecord(newObj);
 return *this;
 }
};

The name, DataObjectTableModel, is quite self-descriptive: a table of DataObjects. Using this class to group DataObjects makes creating editable views reasonably simple. Example 17.18 shows the client code that produced the screen-shot above.

Example 17.18. src/modelview/tablemodel/tablemodel.cpp

#include "dataobjecttablemodel.h"
#include "customerfactory.h"
#include "country.h"

DataObjectTableModel* model() {
 CustomerFactory* fac = CustomerFactory::instance();
 Customer* cust1 = fac->newCustomer("luke skywalker", Country::USA);
 DataObjectTableModel* retval = new
 DataObjectTableModel(cust1); <-- 1
 cust1->setId("14123");
 *retval << cust1; <-- 2
 *retval << fac->newCustomer("Ben Kenobi", Country::Canada);
 *retval << fac->newCustomer("Princess Leia", Country::USA);
 return retval;
}

#include 
#include 
#include 
#include 

int main(int argc, char** argv) {
 QApplication app(argc, argv);
 DataObjectTableModel *mod = model();
 QMainWindow mainwin;
 QTableView view ;
 view.setModel(mod);
 mainwin.setCentralWidget(&view);
 mainwin.setVisible(true);
 int retval = app.exec();
 qDebug() << "Application Exited. " << endl;
 qDebug() << mod->toString() << endl;
 delete mod;
 return retval;
}
 

(1)header model

(2)Insert row into table.

In the public interface, we want convenient functions for operating on rows as DataObject records. See Example 17.19.

Example 17.19. src/libs/dataobjects/dataobjecttablemodel.cpp

[ . . . . ]

bool DataObjectTableModel::
insertRecord(DataObject* newRow, int position,
 const QModelIndex &parent) {
 if (position==-1)
 position=rowCount()-1;
 connect (newRow, SIGNAL(propertyChanged(const QString&,
 const QVariant&)),
 this, SLOT(changeProperty(const QString&,
 const QVariant&)));
 beginInsertRows(parent, position, position);
 m_Data.insert(position, newRow);
 endInsertRows();
 return true;
}

DataObject* DataObjectTableModel::
record(int rowNum) const {
 return m_Data.at(rowNum);
}

But How Does It Work?

QAbstractTableModel has a series of pure virtual functions, declared in Example 17.20, which must be overridden, because they are invoked by QTableView to get and set data.

Example 17.20. src/libs/dataobjects/dataobjecttablemodel.h

[ . . . . ]

 /* Methods which are required to be overridden
 because of QAbstractTableModel */
 int rowCount(const QModelIndex& parent = QModelIndex()) const;
 int columnCount(const QModelIndex& parent = QModelIndex())
 const;
 QVariant data(const QModelIndex& index, int role) const;
 QVariant headerData(int section, Qt::Orientation orientation,
 int role = DisplayRole) const;
 ItemFlags flags(const QModelIndex &index) const;
 bool setData(const QModelIndex &index, const QVariant &value,
 int role = EditRole);
 bool insertRows(int position, int rows,
 const QModelIndex &index = QModelIndex());
 bool removeRows(int position, int rows,
 const QModelIndex &index = QModelIndex());

Example 17.21 shows the methods used to get data in and out of the model.

Example 17.21. src/libs/dataobjects/dataobjecttablemodel.cpp

[ . . . . ]

QVariant DataObjectTableModel::
data(const QModelIndex &index, int role) const {
 if (!index.isValid())
 return QVariant();
 if (role == DisplayRole) {
 int row(index.row()), col(index.column());
 DataObject* lineItem(m_Data.at(row));
 return lineItem->property(m_Headers.at(col));
 } else
 return QVariant();
}

bool DataObjectTableModel::
setData(const QModelIndex &index, const QVariant &value,
 int role) {
 bool changed=false;
 if (index.isValid() && role == EditRole) {
 int row(index.row()), col(index.column());
 DataObject* lineItem(m_Data.at(row));
 changed = lineItem->setProperty(m_Headers.at(col), value);
 if(changed)
 emit dataChanged(index, index);
 }
 return changed;
}

This is a mapping layer from objects to tables. Since the tables need to show header data, the table model has one DataObject, designated the header model, which it uses to obtain headers. Example 17.22 defines headerData, the method that table models call, which we can override to provide the proper header names.

Example 17.22. src/libs/dataobjects/dataobjecttablemodel.cpp

[ . . . . ]

QVariant DataObjectTableModel::
headerData(int section, Qt::Orientation orientation,
 int role) const {
 if (role != DisplayRole)
 return QVariant();
 if(orientation == Qt::Vertical)
 return QVariant(section);
 if (m_Headers.size() ==0)
 return QVariant();
 return m_Headers.at(section);
}
int DataObjectTableModel::rowCount(const QModelIndex&) const {
 return m_Data.count();
}

int DataObjectTableModel::
columnCount(const QModelIndex &parent) const {
 Q_UNUSED(parent);
 return m_Headers.size();
}

The other methods in the abstract interface, shown in Example 17.23, tell views which items are editable (flags()), or allow client code to insert and remove rows.

Example 17.23. src/libs/dataobjects/dataobjecttablemodel.cpp

[ . . . . ]

ItemFlags DataObjectTableModel::
flags(const QModelIndex &index) const {
 if (!index.isValid())
 return ItemIsEnabled;
 // TODO - check the metaProperty to see if it is read/write
 return QAbstractItemModel::flags(index) | ItemIsEditable;
}

void DataObjectTableModel::
reset() {
 QModelIndex br = index(rowCount()-1, columnCount()-1);
 QModelIndex tl = index(0,0);
 emit dataChanged(tl, br);
 m_Dirty = false;
}

bool DataObjectTableModel::
insertRows(int position, int rows, const QModelIndex &parent) {
 beginInsertRows(parent, position, position+rows-1);
 for (int row = 0; row < rows; ++row) {
 DataObject* dobj = m_Original->clone();
 m_Data.insert(position, dobj);
 }
 endInsertRows();
 return true;
}

bool DataObjectTableModel::
removeRows(int position, int rows, const QModelIndex& parent) {
 for (int row = 0; row < rows; ++row) {
 delete m_Data.at(position);
 m_Data.removeAt(position);
 }
 QModelIndex topLeft(index(position, 0, parent));
 QModelIndex bottomRight(index(position + 1, columnCount(), parent));
 emit dataChanged(topLeft, bottomRight);
 return true;
}


Part I: Introduction to C++ and Qt 4

C++ Introduction

Classes

Introduction to Qt

Lists

Functions

Inheritance and Polymorphism

Part II: Higher-Level Programming

Libraries

Introduction to Design Patterns

QObject

Generics and Containers

Qt GUI Widgets

Concurrency

Validation and Regular Expressions

Parsing XML

Meta Objects, Properties, and Reflective Programming

More Design Patterns

Models and Views

Qt SQL Classes

Part III: C++ Language Reference

Types and Expressions

Scope and Storage Class

Statements and Control Structures

Memory Access

Chapter Summary

Inheritance in Detail

Miscellaneous Topics

Part IV: Programming Assignments

MP3 Jukebox Assignments

Part V: Appendices

MP3 Jukebox Assignments

Bibliography

MP3 Jukebox Assignments



An Introduction to Design Patterns in C++ with Qt 4
An Introduction to Design Patterns in C++ with Qt 4
ISBN: 0131879057
EAN: 2147483647
Year: 2004
Pages: 268

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net