how do I adjust a model depending on the view it is set to?

78 Views Asked by At

Situation

I have a single Qt model, in particular, a StringModel which derives from QStringListModel called "model".

I have three views, in particular, three QListViews which are called "listView_1", "listView_2" and "listView_3".

All three of these QListViews are set to the one model, that is:

(ui->listView_1)->setModel(model);
(ui->listView_2)->setModel(model);
(ui->listView_3)->setModel(model);

Complication

Whilst I would like all three views to refer to the same model, the data which they show from that model should be slightly different. The function in the model which dictates what data is shown in the view is the "data" member function inherited from QStringListModel and is defined as:

QVariant StringModel::data(const QModelIndex &index, int role) const
{
        if (!index.isValid()){
            return QVariant();
        }
        if (role == Qt::DisplayRole)
        {
            int col = index.column();
            int row = index.row();
            if (col == 0){
                QList<Contact*> list = contactList.findChildren<Contact*>();
                return list.at(row)->toString();//<<THIS STATEMENT MUST BE VARIABLE
            }
        }
        QVariant v;
        return v;
}

Line 12 in the code above returns the data to show on the view and thats the return statement I would like to vary depending on the view.

Question

Model View Controller best practice states that we should be able to keep the model the same and change the views using minor tweaks. Therefore, without defining 3 models for 3 views, how would I tweak my data function to return a varying statement depending on the view that it is set to? Or, summarising in once sentence, how do I adjust a model depending on the view it is set to?

1

There are 1 best solutions below

0
Aleph0 On

For simple use cases I suggest, that you use QIdentityProxyModel. Below is a small example with two views, where one view displays the strings in a reversed order.

You have to consider careful the two roles Qt::ItemDataRole::EditRole and Qt::ItemDataRole::DisplayRole to make it work seemless.

main.cpp

#include <cmath>
#include <QApplication>
#include <QHBoxLayout>
#include <QStandardItemModel>
#include "ReverseModel.h"
#include <QListView>
#include <QFrame>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    auto frame = new QFrame;
    auto model = new QStandardItemModel;
    auto view1 = new QListView;
    auto view2 = new QListView;
    view1->setModel(model);
    auto reverseModel = new ReverseModel;
    reverseModel->setSourceModel(model);
    view2->setModel(reverseModel);
    frame->setLayout(new QHBoxLayout);
    frame->layout()->addWidget(view1);
    frame->layout()->addWidget(view2);
    model->appendRow(new QStandardItem("Test"));
    frame->show();
    return a.exec();
}

ReverseModel.h

#pragma once

#include <QIdentityProxyModel>
#include <algorithm>

class ReverseModel : public QIdentityProxyModel {
    Q_OBJECT
public: 
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override {
        if (role == Qt::DisplayRole || role==Qt::EditRole) {
            auto data = QIdentityProxyModel::data(index);
            auto string = data.toString();
            std::reverse(string.begin(), string.end());
            return string;
        }
        else {
            return QIdentityProxyModel::data(index, role);
        }

    }

    bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override {
        if (role == Qt::EditRole ) {
            auto string = value.toString();
            std::reverse(string.begin(), string.end());
            QVariant reversedValue =string;
            return QIdentityProxyModel::setData(index, reversedValue, role);
        }
        else {
            return QIdentityProxyModel::setData(index, value, role);
        }
    }
};