Practical Model View Programming (Roadshow Version)

Post on 19-May-2015

3.825 views 3 download

Tags:

description

An overview of the Qt Model View architecture and practical examples of how to use this architecture for presenting large data sets in your application user interface.

Transcript of Practical Model View Programming (Roadshow Version)

PRACTICAL MODEL VIEWPROGRAMMING

CODE LESS. VIEW MORE.

Marius Bugge Monsen,MSc (NTNU),Software Developer

Contents

● An Overview of the Qt Model View Architecture● An Introduction to the Item Model Interface● Customized Item Filtering● Customized Item Painting● More Models and Hierarchies

The Model View Architecture

Model

View

Change Notifications

Item Selections

User Input

Item Delegate

Data Changes

User Input

Item Selection State

Item Selections

Model

View View

Tree WidgetTable Widget List Widget

Item Based Views

Item Based View

View

● Eff ic iency

● Flexib i l i t y

● M a in ta inab i l i t y

What do you get ?

The Model Interface

400 000 000 000 000 cells

InterfaceModel

Data Structure

View

0

0

1

2

0

1

2

0 1 2

#include <QtGui>

int main(int argc, char *argv[]){ QApplication app(argc, argv);

int rows = 4; int columns = 1; QStandardItemModel model(rows, columns); for (int r = 0; r < model.rowCount(); ++r) { QModelIndex index = model.index(r, 0); model.setData(index, "hello"); }

QTableView view; view.setModel(&model);

view.show(); return app.exec();}

InterfaceModel View

hellohellohello

hello

?Data Structure

// we start with a 4x1 table QStandardItemModel model(4, 1); for (int r = 0; r < model.rowCount(); ++r) { QModelIndex index = model.index(r, 0); model.setData(index, "hello"); // let's add a 1x1 sub-table QModelIndex parent = index model.insertRow(0, parent); model.insertColumn(0, parent); // then we can set the data in cell [0,0] QModelIndex child = mode.index(0, 0, parent); model.setData(child, “world”); }

InterfaceModel

Data Structure

View

hellohellohello

worldhelloworld

world

world

?

Model Interface

Custom API

Data Structure

Model

#include <QtGui>

int main(int argc, char *argv[]){ QApplication app(argc, argv); QTreeWidget widget;

for (int i = 0; i < 4; ++i) { QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("hello")); new QTreeWidgetItem(parent, QStringList("world")); }

widget.show(); app.exec();}

Model Index

● Row● Column● Internal Identifier

Qt4 Logo

Model Index

Decoration Role

Display Role

Qt4 Logo

Type: Im age Fi le

Size: 1 0.5 kB

ToolTip Role

QIcon icon(“images/qt4-logo.png”);QStandardItemModel model(4, 1); for (int r = 0; r < model.rowCount(); ++r) { QModelIndex index = model.index(r, 0); model.setData(index, "hello", Qt::DisplayRole); model.setData(index, icon, Qt::DecorationRole);}

QIcon icon(“images/qt4-logo.png”);QTableWidget table(4, 1);

for (int i = 0; i < 4; ++i) table.setItem(i, 0, new QTableWidgetItem(icon, “hello”));

● A Generic In terfa ce to Da ta St ruc tu res

● M odels Are In terchangeab le

● Views Are In terchangeab le

● M odels and Selec t ions Are Sha rab le

The Model Interface

Customized Item Filtering

Model

Model Proxy View

Model Sorting View

Model Filtering View

class CodeModel : public QStringListModel{public: CodeModel(const QString &fileName, QObject *parent = 0) : QStringListModel(parent) { QFile source(name); source.open(QIODevice::ReadOnly); QStringList strings = QString().split("\n", QString::SkipEmptyParts); setStringList(strings); } };

CodeModel model(sourceFile);

QSortFilterProxyModel proxy;proxy.setSourceModel(&model);

QTreeView view;view.setModel(&proxy);

QObject::connect(lineEdit, SIGNAL(textChanged(const QString&)), &proxy, SLOT(setFilterRegExp(const QString&)));

class ListModel : public QAbstractListModel{

Q_OBJECTpublic:

ListModel(QObject *parent = 0);~ListModel();

int rowCount(const QModelIndex &parent) const;QVariant data(const QModelIndex &index, int role) const;

};

ListModel::ListModel(QObject *parent): QAbstractListModel(parent) {}

ListModel::~ListModel() {}

int ListModel::rowCount(const QModelIndex &) const{

return 10000;}

QVariant ListModel::data(const QModelIndex &index, int role) const{

if (role == Qt::DisplayRole)return index.row();

if (role == Qt::DecorationRole)return QColor((index.row() << 4) & 0xFF,

(index.row() << 2) & 0xFF, (index.row() & 0xFF));

return QVariant();}

class ColorFilter : public QSortFilterProxyModel{ Q_OBJECTpublic: ColorFilter(QObject *parent = 0);

bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;public slots: void setRedFilter(int value); void setGreenFilter(int value); void setBlueFilter(int value);

private: int red, green, blue;};

bool ColorFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const{ QModelIndex sourceIndex = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent); QVariant variant = sourceIndex.data(Qt::DecorationRole); if (variant.type() == QVariant::Color) { QColor c = variant.value<QColor>(); return (c.red() > red) && (c.green() > green) && (c.blue() > blue); } return true;}

...ListModel model;ColorFilter filter;filter.setSourceModel(&model);...QSlider *redSlider = new QSlider(Qt::Horizontal);redSlider->setRange(0, 255);QObject::connect(redSlider, SIGNAL(valueChanged(int)), &filter, SLOT(setRedFilter(int)));...QListView *view = new QListView;view->setModel(&filter);view->setItemDelegate(new ColorDelegate(view));...

Customized Item Painting

Model

View

Item Selections

Item Delegate

Qt4 Logo

Qt4 Logo Im age Fi le 1 0.5 kB

Qt4 Logo

class HoverDelegate : public QItemDelegate{ Q_OBJECTpublic: HoverDelegate(QObject *parent = 0) : QItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (option.state & QStyle::State_MouseOver) painter->fillRect(option.rect, Qt::lightGray); QItemDelegate::paint(painter, option, index); }};...view.viewport()->setAttribute(Qt::WA_Hover);...

Movie Title

<?xml version="1.0" standalone="no"?><svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" baseProfile="tiny" version="1.2"> <title>Play</title> <defs> <linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="100"> <stop offset="0%" stop-color="#ddddff" /> <stop offset="50%" stop-color="#000000"/> </linearGradient> </defs> <rect x="20" y="0" width="93" height="130" rx="5" fill="none" stroke="#ffffff" stroke-width="3" /> <circle cx="50" cy="50" r="50" fill="url(#gradient)" stroke="#000000" stroke-width="2"/> <path d="M 30 75 L 30 25 L 80 50 z" fill="#dddddd" stroke="#000000" stroke-width="2" /></svg>

class DemoDelegate : public QItemDelegate{public: DemoDelegate(QObject *parent = 0);

void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;

protected: void drawSvg(QPainter *painter, const QRect &rect) const;

private: mutable QSvgRenderer renderer;};

void DemoDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{ // draw the item as unselected and without focus rect QStyleOptionViewItem opt = option; opt.state &= ~QStyle::State_Selected; opt.state &= ~QStyle::State_HasFocus; QItemDelegate::paint(painter, opt, index); // draw svg over the current item if (option.state & QStyle::State_HasFocus) drawSvg(painter, option.rect);}

More Models and Hierarchies

struct SimpleNode{ SimpleNode(SimpleNode *parent) : parentNode(parent) { if (parentNode) parentNode->children.append(this); }

~SimpleNode() { foreach(SimpleNode *child, children) delete child; } QVariant data() const { return "node data"; }

SimpleNode *parentNode; QList<SimpleNode*> children;};

InterfaceModel

Data Structure

View

class SimpleModel : public QAbstractItemModel{public: SimpleModel(QObject *parent = 0); ~SimpleModel();

QModelIndex index(int row, int column, const QModelIndex &parent) const; QModelIndex parent(const QModelIndex &child) const; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const;

protected: QModelIndex indexForNode(SimpleNode *node) const; SimpleNode *nodeForIndex(const QModelIndex &index) const; int rowForNode(SimpleNode *node) const;

private: SimpleNode *root;};

int SimpleModel::rowCount(const QModelIndex &parent) const{ SimpleNode *parentNode = nodeForIndex(parent); return parentNode->children.count();}

int SimpleModel::columnCount(const QModelIndex &parent) const{ Q_UNUSED(parent); return 1;}

SimpleModel::SimpleModel(QObject *parent) : QAbstractItemModel(parent){ // Initialize the data structure root = new SimpleNode(0); for (int i = 0; i < 10; ++i) { SimpleNode *topLevel = new SimpleNode(root); for (int j = 0; j < 5; ++j) { SimpleNode *secondLevel = new SimpleNode(topLevel); for (int k = 0; k < 3; ++k) { (void) new SimpleNode(secondLevel); } } }}

SimpleModel::~SimpleModel(){ delete root;}

QVariant SimpleModel::data(const QModelIndex &index, int role) const{ if (index.isValid() && role == Qt::DisplayRole) { SimpleNode *node = nodeForIndex(index); return node->data(); } return QVariant();}

Qt4 Logo

Model Index

QModelIndex SimpleModel::index(int row, int column, const QModelIndex &parent) const{ if (hasIndex(row, column, parent)) { SimpleNode *parentNode = nodeForIndex(parent); SimpleNode *childNode = parentNode->children.at(row); return indexForNode(childNode); } return QModelIndex();}

QModelIndex SimpleModel::parent(const QModelIndex &child) const{ SimpleNode *childNode = nodeForIndex(child); SimpleNode *parentNode = childNode->parentNode; if (parentNode == root) return QModelIndex(); return indexForNode(parentNode);}

QModelIndex SimpleModel::indexForNode(SimpleNode *node) const{ if (node == root) return QModelIndex(); int row = rowForNode(node); int column = 0; return createIndex(row, column, node);}

SimpleNode *SimpleModel::nodeForIndex(const QModelIndex &index) const{ if (index.isValid()) return static_cast<SimpleNode*>(index.internalPointer()); return root;}

int SimpleModel::rowForNode(SimpleNode *node) const{ return node->parentNode->children.indexOf(node);}

int main(int argc, char *argv[]){ QApplication app(argc, argv); SimpleModel model;

QTreeView view; view.setModel(&model); view.show();

return app.exec();}

What we have covered

● The Qt Model View Architecture● The Item Model Interface● Customized Item Filtering● Customized Item Painting● Hierarchical Models

More Information

http://doc.trolltech.com/4.1/model-view-programming.htmlhttp://doc.trolltech.com/4.1/model-view.html