CopperSpice API  1.9.1
Model/View Examples

Multiple Views with a Single Model

In the following code two table views are created and both are configured to use the same table model.

QTableView *viewOne = new QTableView;
QTableView *viewTwo = new QTableView;
viewOne->setModel(model);
viewTwo->setModel(model);

Both views show the same data since they are using the same model however each view maintains its own internal selection model.

Updating the Editor Widget Geometry

The delegate is responsible for managing the geometry of an editor widget. The geometry must be set when the editor is created and also when the item's size or position in the view is changed. The view provides all the necessary geometry information inside a view option object.

In this example we can use the geometry provided by the view option. A custom delegate which renders items with several elements might not be able to use the item rectangle directly. It might be necessary to position the editor in relation to other elements.

void MySpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const
{
editor->setGeometry(option.rect);
}

Drag and Drop in a View

Since access to the data displayed by a view is controlled by the model, the model must support drag and drop operations. This is done by reimplementing the QAbstractItemModel::supportedDropActions() method in your application. As an example, copy and move operations are enabled with the following code.

QListView *listView = new QListView(this);
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
listView->setDragEnabled(true);
listView->setAcceptDrops(true);
listView->setDropIndicatorShown(true);
Qt::DropActions DragDropListModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}

Although any combination of values from the enum Qt::DropAction can be specified, the model needs to be written to implement the corresponding method. For example to support Qt::MoveAction in a list model, the model must provide an implementation of QAbstractItemModel::removeRows().

Read Only Model with Other Roles

In addition to controlling the actual text the view displays the model can also modify the appearance of the text. This is accomplished by changing the implementation of the data() method in a custom model. Features like the font, background color, alignment, or displaying a checkbox can be modified to improve or adjust the user interface.

In this example the role parameter is leveraged to return different settings depending on the Qt::ItemDataRole enum value. The declaration for MyStringListModel is located in the model/view Custom Model documentation. Refer to Read Only Model.

QVariant MyStringListModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
switch(role) {
case Qt::DisplayRole:
return m_stringList.at(index.row());
case Qt::FontRole:
if (row % 2 == 1) {
QFont boldFont;
boldFont.setBold(true);
return boldFont;
}
break;
case Qt::BackgroundRole:
if (row == 3) {
QBrush redBackground(Qt::red);
return redBackground;
}
break;
case Qt::TextAlignmentRole:
if (row == 1) {
return Qt::AlignRight + Qt::AlignVCenter;
}
break;
default:
return QVariant();
}
return QVariant();
}

The view will call the data() method once for every role.

Qt::ItemDataRole Description Data Type
Qt::DisplayRole Text QString
Qt::FontRole Font QFont
Qt::BackgroundRole Brush for the background color QBrush
Qt::TextAlignmentRole Text alignment enum Qt::AlignmentFlag

Current Time Displayed in a Table Cell

For this example there is still a read only table, however the content will change every second because the current time is being displayed.

QVariant MyModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
int col = index.column();
if (role == Qt::DisplayRole)
{
if (row == 0 && col == 0)
{
}
}
return QVariant();
}

In order to make the clock tick we need to tell the view every second that the model has changed so the view should be updated. This is implemented using a timer. In the constructor the interval is set to 1 second and connect the timeout() signal to a slot.

MyModel::MyModel(QObject *parent) : QAbstractTableModel(parent)
{
timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(timerHit()));
timer->start();
}

This is the corresponding slot. Emitting the dataChanged() signal will trigger the view to re-read the specified model index data and then update the display. We did not need to connect the dataChanged() signal to the view. This happens automatically when calling setModel().

void MyModel::timerHit()
{
// identify the top left cell
QModelIndex topLeft = createIndex(0,0);
// emit a signal to make the view reread identified data
emit dataChanged(topLeft, topLeft);
}

Setting up Headers for Columns and Rows

The header information is stored in the model. If you have a custom model you may need to provide an implementation of the headerData() method. Headers can be hidden by calling the method verticalHeader()->hide() in the view. The method headerData() is accepts three parameters and the last one indicates the role.

QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole) {
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return QString("first");
case 1:
return QString("second");
case 2:
return QString("third");
}
}
}
return QVariant();
}

Editing Data in a Table

This example shows how to set up a table model where the data can be modified by the user. Our custom model inherits from QAbstractTableModel. The methods rowCount(), columnCount(), and data() must be implemented. For the model to support the ability to edit the data, the methods setData() and flags() must also be implemented. A two dimensional array of type QString stores our data in the model.

The method setData() will be called each time the user edits a cell. The index parameter indicates which item has been edited and value is the result of the editing process. The role will always be set to Qt::EditRole because our cells only contain text. If a checkbox were present and user permissions are set to allow the checkbox to be selected, calls would also be made with the role set to Qt::CheckStateRole.

Various properties of a cell can be adjusted with the flags() method. Returning the three enum values indicates the table cell can be selected and edited.

The editCompleted() signal emits a signal when the data has been changed. A slot method can be connected to take some action after the new data has been saved to the model.

constexpr const int COLS = 3;
constexpr const int ROWS = 2;
class MyTableModel : public QAbstractTableModel
{
CS_OBJECT(MyModel)
public:
MyTableModel(QObject *parent);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex & index) const;
CS_SIGNAL_1(Public, void editCompleted(const QString &data, int row, int column))
CS_SIGNAL_2(editCompleted, data, row, column)
private:
QString m_gridData[ROWS][COLS]; // stores the data
};
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::EditRole) {
QString data = value.toString();
// save the new data
m_gridData[index.row()][index.column()] = data;
emit editCompleted(data, index.row(), index.column());
}
return true;
}
Qt::ItemFlags MyTableModel::flags(const QModelIndex & /*index*/) const
{
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}

Editing Data in a TreeView

You can econvert the prior example from a QTableView to a QTreeView. No changes to the model are required. The tree will not have any branches because none of the elements in the model have children.

This image shows a representation of the data in our model with two rows and three columns. Each row is a child of the same root item. The root element is never displayed in a view.

If we want to display the data in a proper tree view with parents and children it would require changing the design of the custom model. Another approach would involve inheriting from a different model. The QStandardItemModel is designed to handle parent child data relationships. This model must be populated using the QStandardItem class.