1 Star 11 Fork 6

胡歌-此生不换/Qt-Project

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

Qt MVC 结构之 Model 模型介绍

MVC 简介

MVC 就是 Model-View-Control 模式的简称,包括模型层(Model), 视图层(View), 控制层(Controller)。

Model 主要负责管理数据,View 主要用来显示数据,Controller 主要用来操作数据,控制 View 联动。

Qt 也采用了这个模式,模型层用 Model,视图层用 View,控制层改名叫了代理 Delegate

特定模型

除了 QStandardItemModel 之外,还有一些其他集成好的特殊模型,如果我们要实现树形模型就子类化 QStandardItemModel

如果想实现列表模型就子类化 QAbstractListModel,如果像实现表格模型就子类化 QAbstractTableModel

我们子类化 QAbstractListModel,实现一个列表模型。

class StringListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    StringListModel (const QStringList& strigns, QObject* parent = 0);
    // explicit StringListModel(const QStringList& strings, QObject *parent = nullptr);

    int rowCount(const QModelIndex& parent = QModelIndex()) const;

    // 注意虚函数是的参数是下面的写法的哦,int 类型的参数是在后面的哦。不然会爆下面的 error 的
    // error: main.cpp:19:21: Variable type 'StringListModel' is an abstract class 并且 qabstractitemmodel.h:191:34: unimplemented pure virtual method 'data' in 'StringListModel'
    // QVariant data(int role, const QModelIndex& index) const;
    QVariant data(const QModelIndex& index, int role) const;

    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

private:
    QStringList stringlist;
};

这个模型类包含一个 QStringList,用来管理数据:

int StringListModel::rowCount(const QModelIndex &parent) const
{
    return stringlist.count();
}

// QVariant StringListModel::data(int role, const QModelIndex &index) const
QVariant StringListModel::data(const QModelIndex& index, int role) const
{
    if(index.isValid() == false)
        return QVariant();

    if(index.row() >= stringlist.size())
        return QVariant();

    if(role == Qt::DisplayRole){
        return stringlist.at(index.row());
    }else{
        return QVariant();
    }
}

QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role != Qt::DisplayRole){
        return QVariant();
    }

    if(orientation == Qt::Horizontal){
        return QString("Column %1").arg(section);
    }else{
        return QString("Row %1").arg(section);
    }
}

headerData 函数内根据水平还是垂直判断,显示表头。

data 函数内根据角色返回索引对应的数据。

main 函数中可以分别用一个 listviewtreeview 显示:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // MainWindow w;
    // w.show();

    QStringList list;
    list << "a" << "b" << "c"; // QStringList重载了 << 运算符

    StringListModel model(list);
    QListView listView;
    listView.setModel(&model);
    listView.show();

    QTableView tableView;
    tableView.setModel(&model);
    tableView.show();

    return a.exec();
}

Alt text

设置可编辑选项

我们为自定义的 listmodel 模型添加两个函数 flags()setData() 函数。

flags 函数用来判断模型索引对应的项目的属性,通过标记按位或的方式获取。

setData 用来设置模型索引对应的项,并且设置他的编辑属性。

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
    if(!index.isValid()){
        return Qt::ItemIsEnabled;
    }

    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

如果是无效的数据则返回 ItemIsEnabled 标记,否则在原来的标记基础上增加 ItemIsEditable

当我们修改数据时,会触发 setData 函数, 该函数根据项的角色为 EditRole 替换原来的字符串。

bool StringListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(index.isValid() && role == Qt::EditRole){
        stringlist.replace(index.row(), value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

并且发送了 dataChanged,这个信号第一个参数为左上角的 index,第二个参数为右下角 index

dataChanged 通知 View 视图刷新数据,从而完成数据的修改。

另外 Views 显示数据时会根据 data 返回的数据显示,所以要将 data 函数的显示逻辑中添加 Qt::EditRole

// QVariant StringListModel::data(int role, const QModelIndex &index) const
QVariant StringListModel::data(const QModelIndex& index, int role) const
{
    if(index.isValid() == false)
        return QVariant();

    if(index.row() >= stringlist.size())
        return QVariant();

    if(role == Qt::DisplayRole || role == Qt::EditRole){
        return stringlist.at(index.row());
    }else{
        return QVariant();
    }
}

添加行与删除行

添加行和删除行都需要在添加和删除之前调用 begin 操作,操作完之后调用 end 操作:

// 添加行与删除行
bool StringListModel::insertRows(int position, int rows, const QModelIndex& index){
    beginInsertRows(QModelIndex(), position, position + rows - 1);
    for(int row = 0; row < rows; row++){
        stringlist.insert(position, "");
    }
    endInsertRows();
    return true;
}

bool StringListModel::removeRows(int position, int rows, const QModelIndex& index){
    beginRemoveRows(QModelIndex(), position, position + rows - 1);
    for(int row = 0; row < rows; row++){
        stringlist.removeAt(position);
    }
    endRemoveRows();
    return true;
}

接下来可以调用一下测试:

    model.insertRows(3,2);
    model.removeRows(0,1);

Alt text

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/ax020913/qt-project.git
git@gitee.com:ax020913/qt-project.git
ax020913
qt-project
Qt-Project
master

搜索帮助