Qt5 Tutorial ModelView with QTableView and QItemDelegate - 2020
In this tutorial, we will learn about ModelView QTableView and QItemDelegate.
The QTableView class provides a default model/view implementation of a table view. The QItemDelegate class provides display and editing facilities for data items from a model.
In this example, we'll use Qt Gui application with QDialog:
As we discussed in other ModelView tutorials, Qt's MVC may not be the same as the conventional MVC.
If the view and the controller objects are combined, the result is the model/view architecture. This still separates the way that data is stored from the way that it is presented to the user, but provides a simpler framework based on the same principles. This separation makes it possible to display the same data in several different views, and to implement new types of views, without changing the underlying data structures. To allow flexible handling of user input, we introduce the concept of the delegate. The advantage of having a delegate in this framework is that it allows the way items of data are rendered and edited to be customized.However, it's worth investigating their approach of using "delegate" with their Model/View pattern.
QItemDelegate can be used to provide custom display features and editor widgets for item views based on QAbstractItemView subclasses. Using a delegate for this purpose allows the display and editing mechanisms to be customized and developed independently from the model and view.
The QItemDelegate class is one of the Model/View Classes and is part of Qt's model/view framework. Note that QStyledItemDelegate has taken over the job of drawing Qt's item views. We recommend the use of QStyledItemDelegate when creating new delegates.
When displaying items from a custom model in a standard view, it is often sufficient to simply ensure that the model returns appropriate data for each of the roles that determine the appearance of items in views. The default delegate used by Qt's standard views uses this role information to display items in most of the common forms expected by users. However, it is sometimes necessary to have even more control over the appearance of items than the default delegate can provide.
In this tutorial, we'll use Model-Based TableView:
Since we finished layout, now is the time for coding.
Let's make our model in delegatetableviewdialog.h:
#ifndef DELEGATETABLEVIEWDIALOG_H #define DELEGATETABLEVIEWDIALOG_H #include <QDialog> #include <QTableView> #include <QItemDelegate> #include <QStandardItemModel> namespace Ui { class DelegateTableViewDialog; } class DelegateTableViewDialog : public QDialog { Q_OBJECT public: explicit DelegateTableViewDialog(QWidget *parent = 0); ~DelegateTableViewDialog(); private: Ui::DelegateTableViewDialog *ui; // QStandardItemModel provides a classic // item-based approach to working with the model. QStandardItemModel *model; }; #endif // DELEGATETABLEVIEWDIALOG_H
Move on to the implementation file, delegatetableviewdialog.cpp:
#include "delegatetableviewdialog.h" #include "ui_delegatetableviewdialog.h" DelegateTableViewDialog ::DelegateTableViewDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DelegateTableViewDialog) { ui->setupUi(this); // Create a new model // QStandardItemModel(int rows, int columns, QObject * parent = 0) model = new QStandardItemModel(4,2,this); // Attach the model to the view ui->tableView->setModel(model); // Generate data for(int row = 0; row < 4; row++) { for(int col = 0; col < 2; col++) { QModelIndex index = model->index(row,col,QModelIndex()); // 0 for all data model->setData(index,0); } } } DelegateTableViewDialog::~DelegateTableViewDialog() { delete ui; }
Let's run the code to see what we've done.
Looks good. We're on track!<
All data are set to zero.
Also note that Qt already provides SpinBox withing a cell of the TableView.
Let's customize the SpinBox.
To customize the SpinBox, we need to create a new class.
When a new instance of MyDelegate created, it does not have the methods to use to make it functiioning (i.e. customizing the SpinBox). As we can see from the table below, we need to re-implement couple of virtual methods inherited from QItemDelegate class.
Type | Description |
---|---|
virtual QWidget * | createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const |
virtual void | paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const |
virtual void | setEditorData(QWidget * editor, const QModelIndex & index) const |
virtual void | setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const |
virtual QSize | sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const |
virtual void | updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const |
So, let's work on the header file (mydelegate.h) to make some prototypes of those methods:
#ifndef MYDELEGATE_H #define MYDELEGATE_H #include <QItemDelegate> #include <QModelIndex> #include <QObject> #include <QSize> #include <QSpinBox> class MyDelegate : public QItemDelegate { Q_OBJECT public: explicit MyDelegate(QObject *parent = 0); // Create Editor when we construct MyDelegate QWidget createEditor(QWidget *parent, const QStyleOptionViewItem &option;, const QModelIndex &index;) const; // Then, we set the Editor void setEditorData(QWidget *editor, const QModelIndex &index;) const; // When we modify data, this model reflect the change void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index;) const; // Give the SpinBox the info on size and location void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option;, const QModelIndex &index;) const; signals: public slots: }; #endif // MYDELEGATE_H
So, the prototypes for the inherited virtual member functions are done. Now, let our dialog know we now have a new delegate class:
Go to "delegatetableviewdialog.h"
#ifndef DELEGATETABLEVIEWDIALOG_H #define DELEGATETABLEVIEWDIALOG_H #include <QDialog> #include <QTableView> #include <QItemDelegate> #include <QStandardItemModel> #include "mydelegate.h" namespace Ui { class DelegateTableViewDialog; } class DelegateTableViewDialog : public QDialog { Q_OBJECT public: explicit DelegateTableViewDialog(QWidget *parent = 0); ~DelegateTableViewDialog(); private: Ui::DelegateTableViewDialog *ui; // QStandardItemModel provides a classic // item-based approach to working with the model. QStandardItemModel *model; // Make a member pointer to a new MyDelegate instance MyDelegate *myDelegate; }; #endif // DELEGATETABLEVIEWDIALOG_H
Then, go to the dialog implementation file (delegatetableviewdialog.cpp):
#include "delegatetableviewdialog.h" #include "ui_delegatetableviewdialog.h" DelegateTableViewDialog ::DelegateTableViewDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DelegateTableViewDialog) { ui->setupUi(this); myDelegate = new MyDelegate(this); // Create a new model // QStandardItemModel(int rows, int columns, QObject * parent = 0) model = new QStandardItemModel(4,2,this); // Generate data for(int row = 0; row < 4; row++) { for(int col = 0; col < 2; col++) { QModelIndex index = model->index(row,col,QModelIndex()); // 0 for all data model->setData(index,0); } } // Attach (tie) the model to the view ui->tableView->setModel(model); // Tie the View with the new MyDelegate instance // If we don not set this, it will use default delegate ui->tableView->setItemDelegate(myDelegate); } DelegateTableViewDialog::~DelegateTableViewDialog() { delete ui; }
Now, we need to work on the inherited virtual member functions.
Note that we've already written the prototypes in mydelegate.h.
Go to the implementation file, mydelegate.cpp.
#include "mydelegate.h" MyDelegate::MyDelegate(QObject *parent) : QItemDelegate(parent) { } // TableView need to create an Editor // Create Editor when we construct MyDelegate // and return the Editor QWidget* MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option;, const QModelIndex &index;) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; } // Then, we set the Editor // Gets the data from Model and feeds the data to Editor void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index;) const { // Get the value via index of the Model int value = index.model()->data(index, Qt::EditRole).toInt(); // Put the value into the SpinBox QSpinBox *spinbox = static_cast<QSpinBox*>(editor); spinbox->setValue(value); } // When we modify data, this model reflect the change void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index;) const { } // Give the SpinBox the info on size and location void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option;, const QModelIndex &index;) const { }
If we run it now, we'll have:
Most of the things seem to work. However, note that the SpinBox we see is not properly located. That's because we haven't finished the Geometry for Editor. Let's do that now. Also, after we changed the value in the SpinBox, it's not put that value back to the Model.
Let's do the rest of the coding:
// When we modify data, this model reflect the change // Data from the delegate to the model void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index;) const { QSpinBox *spinbox = static_cast<QSpinBox*>(editor); spinbox->interpretText(); int value = spinbox->value(); model->setData(index, value, Qt::EditRole); } // Give the SpinBox the info on size and location void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option;, const QModelIndex &index;) const { editor->setGeometry(option.rect); }
Here is the file used in this tutorial: DelegateTableView.zip.
delegatetableviewdialog.h:
#ifndef DELEGATETABLEVIEWDIALOG_H #define DELEGATETABLEVIEWDIALOG_H #include <QDialog> #include <QTableView> #include <QItemDelegate> #include <QStandardItemModel> #include "mydelegate.h" namespace Ui { class DelegateTableViewDialog; } class DelegateTableViewDialog : public QDialog { Q_OBJECT public: explicit DelegateTableViewDialog(QWidget *parent = 0); ~DelegateTableViewDialog(); private: Ui::DelegateTableViewDialog *ui; // QStandardItemModel provides a classic // item-based approach to working with the model. QStandardItemModel *model; // Make a member pointer to a new MyDelegate instance MyDelegate *myDelegate; }; #endif // DELEGATETABLEVIEWDIALOG_H
delegatetableviewdialog.cpp:
#include "delegatetableviewdialog.h" #include "ui_delegatetableviewdialog.h" DelegateTableViewDialog ::DelegateTableViewDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DelegateTableViewDialog) { ui->setupUi(this); myDelegate = new MyDelegate(this); // Create a new model // QStandardItemModel(int rows, int columns, QObject * parent = 0) model = new QStandardItemModel(4,2,this); // Generate data for(int row = 0; row < 4; row++) { for(int col = 0; col < 2; col++) { QModelIndex index = model->index(row,col,QModelIndex()); // 0 for all data model->setData(index,0); } } // Attach (tie) the model to the view ui->tableView->setModel(model); // Tie the View with the new MyDelegate instance // If we don not set this, it will use default delegate ui->tableView->setItemDelegate(myDelegate); } DelegateTableViewDialog::~DelegateTableViewDialog() { delete ui; }
mydelegate.h:
#ifndef MYDELEGATE_H #define MYDELEGATE_H #include <QItemDelegate> #include <QModelIndex> #include <QObject> #include <QSize> #include <QSpinBox> class MyDelegate : public QItemDelegate { Q_OBJECT public: explicit MyDelegate(QObject *parent = 0); // Create Editor when we construct MyDelegate QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option;, const QModelIndex &index;) const; // Then, we set the Editor void setEditorData(QWidget *editor, const QModelIndex &index;) const; // When we modify data, this model reflect the change void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index;) const; // Give the SpinBox the info on size and location void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option;, const QModelIndex &index;) const; signals: public slots: }; #endif // MYDELEGATE_H
mydelegate.cpp:
#include "mydelegate.h" MyDelegate::MyDelegate(QObject *parent) : QItemDelegate(parent) { } // TableView need to create an Editor // Create Editor when we construct MyDelegate // and return the Editor QWidget* MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option;, const QModelIndex &index;) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; } // Then, we set the Editor // Gets the data from Model and feeds the data to delegate Editor void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index;) const { // Get the value via index of the Model int value = index.model()->data(index, Qt::EditRole).toInt(); // Put the value into the SpinBox QSpinBox *spinbox = static_cast<QSpinBox*>(editor); spinbox->setValue(value); } // When we modify data, this model reflect the change // Data from the delegate to the model void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index;) const { QSpinBox *spinbox = static_cast<QSpinBox*>(editor); spinbox->interpretText(); int value = spinbox->value(); model->setData(index, value, Qt::EditRole); } // Give the SpinBox the info on size and location void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option;, const QModelIndex &index;) const { editor->setGeometry(option.rect); }
Qt 5 Tutorial
- Hello World
- Signals and Slots
- Q_OBJECT Macro
- MainWindow and Action
- MainWindow and ImageViewer using Designer A
- MainWindow and ImageViewer using Designer B
- Layouts
- Layouts without Designer
- Grid Layouts
- Splitter
- QDir
- QFile (Basic)
- Resource Files (.qrc)
- QComboBox
- QListWidget
- QTreeWidget
- QAction and Icon Resources
- QStatusBar
- QMessageBox
- QTimer
- QList
- QListIterator
- QMutableListIterator
- QLinkedList
- QMap
- QHash
- QStringList
- QTextStream
- QMimeType and QMimeDatabase
- QFile (Serialization I)
- QFile (Serialization II - Class)
- Tool Tips in HTML Style and with Resource Images
- QPainter
- QBrush and QRect
- QPainterPath and QPolygon
- QPen and Cap Style
- QBrush and QGradient
- QPainter and Transformations
- QGraphicsView and QGraphicsScene
- Customizing Items by inheriting QGraphicsItem
- QGraphicsView Animation
- FFmpeg Converter using QProcess
- QProgress Dialog - Modal and Modeless
- QVariant and QMetaType
- QtXML - Writing to a file
- QtXML - QtXML DOM Reading
- QThreads - Introduction
- QThreads - Creating Threads
- Creating QThreads using QtConcurrent
- QThreads - Priority
- QThreads - QMutex
- QThreads - GuiThread
- QtConcurrent QProgressDialog with QFutureWatcher
- QSemaphores - Producer and Consumer
- QThreads - wait()
- MVC - ModelView with QListView and QStringListModel
- MVC - ModelView with QTreeView and QDirModel
- MVC - ModelView with QTreeView and QFileSystemModel
- MVC - ModelView with QTableView and QItemDelegate
- QHttp - Downloading Files
- QNetworkAccessManager and QNetworkRequest - Downloading Files
- Qt's Network Download Example - Reconstructed
- QNetworkAccessManager - Downloading Files with UI and QProgressDialog
- QUdpSocket
- QTcpSocket
- QTcpSocket with Signals and Slots
- QTcpServer - Client and Server
- QTcpServer - Loopback Dialog
- QTcpServer - Client and Server using MultiThreading
- QTcpServer - Client and Server using QThreadPool
- Asynchronous QTcpServer - Client and Server using QThreadPool
- Qt Quick2 QML Animation - A
- Qt Quick2 QML Animation - B
- Short note on Ubuntu Install
- OpenGL with QT5
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part A
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part B
- Video Player with HTML5 QWebView and FFmpeg Converter
- Qt5 Add-in and Visual Studio 2012
- Qt5.3 Installation on Ubuntu 14.04
- Qt5.5 Installation on Ubuntu 14.04
- Short note on deploying to Windows
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization