Qt程序设计:多个View共享数据

当多个 View 展示同一个数据集中的部分数据项时,其中一个 View 进行了增删改操作,也需要同步到其他的 View。有两种比较简单的解决方案:

方案一:如果都是同一种 View(如都是 ListView),那可以共用一个 Model 实例,通过 Proxy 来辅助完成过滤和选择等操作,实践起来也比较简单,几乎不用自己做额外的同步。

方案二:如果数据项需要同时在 ListView、TreeView 等不同的场景使用,那单个 Model 实例的方式实现起来就很麻烦了,这时可以让多个 Model 持有同一组数据项指针(建议用智能指针),更新之后通知所有关联的 View 进行同步刷新。

本文 Demo 采用方案二共享数据项指针的方式,UI 使用 QML 实现。通过 Model 的 data 接口访问数据时,是可以返回指针的,然后 Delegate 通过指针访问数据项的属性。但是 QML 中没法识别智能指针(至少 Qt5 下测试识别不了),只能从智能指针取出原始指针后返回给 QML。但是 QML Delegate 访问原始指针的属性还有问题,就是初始化或者增删时,可能报属性未定义或者是不存在,所以还是先老老实实从 data 接口返回数据项的属性值,而不是返回指针,这样排查问题也容易。

除了数据项指针,还需要一个对象(DataSource)来关联各个 Model/View,这样在一个 Model/View 中的增删改操作,就能同步到其他 Model/View 中。修改时调用 DataSource 接口修改,同时触发更新信号,关联 Model 收到信号后进行同步。考虑到一般编辑接口是需要 Model 的行列号来定位当前项的,QML 中可以通过 Model 来间接调用 DataSource 的增删改,而不是直接操作 DataSource 的增删改。

DataSource 接口定义如下:

#pragma once
#include <QObject>
#include <QSharedPointer>

struct DataInfo
{
    QString key;
    int value = 0;
};

class DataSource : public QObject
{
    Q_OBJECT
private:
    explicit DataSource(QObject *parent = nullptr);
public:
    ~DataSource();
    static DataSource* getInstance();

    //增删改接口
    void deleteItem(QSharedPointer<DataInfo> item);
    void appendItem(const QString &key, int value);
    void appendItem(QSharedPointer<DataInfo> item);
    void updateItem(QSharedPointer<DataInfo> item);
    //根据value范围查询数据
    QList<QSharedPointer<DataInfo>> searchItemByValue(int min, int max);

signals:
    //增删改通知其他view更新
    void itemDeleteNotify(QSharedPointer<DataInfo> item);
    void itemAppendNotify(QSharedPointer<DataInfo> item);
    void itemUpdateNotify(QSharedPointer<DataInfo> item);

private:
    //存储着所有的数据
    QList<QSharedPointer<DataInfo>> dataList;
};

View 中操作 Model 的增删改接口:

Button {
    text: "update"
    onClicked: {
        //update更新所有view中的该item
        data_model.updateItemByRow(model.index, "key", 123)
    }
}
Button {
    text: "delete"
    onClicked: {
        //删除item
        data_model.deleteItemByRow(model.index)
    }
}

void DataModel::deleteItemByRow(int row)
{
    if(row < 0 || row >= dataList.size())
        return;
    DataSource::getInstance()->deleteItem(dataList.at(row));
}

void DataModel::updateItemByRow(int row, const QString &key, int value)
{
    if(row < 0 || row >= dataList.size())
        return;
    auto item = dataList.at(row);
    item->key = key;
    item->value = value;
    DataSource::getInstance()->updateItem(item);
}

DataSource 信号通知关联 Model 刷新: 

DataModel::DataModel(QObject *parent)
    : QAbstractListModel(parent)
{
    auto source = DataSource::getInstance();
    connect(source, &DataSource::itemAppendNotify,
            this, [this](QSharedPointer<DataInfo> item){
        //符合条件,插入
        if(item->value >= searchMin && item->value <= searchMax){
            beginInsertRows(QModelIndex(), rowCount(), rowCount());
            dataList.append(item);
            endInsertRows();
        }
    });
    connect(source, &DataSource::itemDeleteNotify,
            this, [this](QSharedPointer<DataInfo> item){
        //存在则移除
        int row = dataList.indexOf(item);
        if(row >= 0){
            beginRemoveRows(QModelIndex(), row, row);
            dataList.removeAt(row);
            endRemoveRows();
        }
    });
    connect(source, &DataSource::itemUpdateNotify,
            this, [this](QSharedPointer<DataInfo> item){
        int row = dataList.indexOf(item);
        if(row >= 0){
            //已存在,判断是否需要移除
            if(item->value >= searchMin && item->value <= searchMax){
                emit dataChanged(index(row, 0), index(row, 0));
            }else{
                beginRemoveRows(QModelIndex(), row, row);
                dataList.removeAt(row);
                endRemoveRows();
            }
        }else if(item->value >= searchMin && item->value <= searchMax){
            //不存在,判断是否需要重新查询
            searchItemByValue(searchMin, searchMax);
        }
    });
}

通知关联 View 刷新的时候,每个 Model 都需要去查询是否存在这个数据项,以及他在列表中的位置,但这种查询都是线性遍历一次,应该不会有太大的损耗,即使数据量很大。

实现效果:

完整代码:https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20220709_CommonData

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值