MVC简介

MVC 就是Model-View-Control模式的简称,包括模型层(Model), 视图层(View), 控制层(Controller). Model主要负责管理数据,View主要用来显示数据,Controller主要用来操作数据,控制View联动。 Qt也采用了这个模式,模型层用Model,视图层用View,控制层改名叫了代理Delegate。

QFileSystemModel

我们可以举个简单的例子,用QFileSystemModel来实现文件夹内容的展示,QFileSystemModel是Qt给我们提供的处理本地文件系统的文件和目录。

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QSplitter * splitter  = new QSplitter;
    QFileSystemModel * model = new QFileSystemModel;
    model->setRootPath(QDir::currentPath());

    QTreeView * tree = new QTreeView(splitter);
    tree->setModel(model);
    tree->setRootIndex(model->index(QDir::currentPath()));

    QListView * list = new QListView(splitter);
    list->setModel(model);
    list->setRootIndex(model->index(QDir::currentPath()));

    splitter->setWindowTitle("Two views onto the same file system model");
    splitter->resize(1000,800);
    splitter->show();
    return a.exec();
}

为model设置根目录,目录为当前文件夹。 创建树型视图,视图加载model,并且设置视图的根索引为model的当前目录的索引。

模型介绍

模型分为几种,有列表模型,表格模型,以及树模型。详细可以参考Qt的帮主文档,搜索Model/View Programming。 https://cdn.llfc.club/1671158788602.jpg

模型有一个最基本的类QStandardItemModel,我们基于这个模型类实现一个树形模型。

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建标准itemmodel
    QStandardItemModel model;
    //获取模型根项,根项不可见
    QStandardItem * parentItem = model.invisibleRootItem();
    //创建文本显示,图标,和工具显示
    QStandardItem * item0 = new QStandardItem();
    item0->setText("item0");
    QPixmap pixmap0(50,50);
    pixmap0.fill(Qt::red);
    item0->setIcon(pixmap0);
    item0->setToolTip("indexA");
    parentItem->appendRow(item0);
    //将新的项设置为父节点项
    parentItem = item0;

    QStandardItem * item1 = new QStandardItem();
    item1->setText("item1");
    QPixmap pixmap1(50,50);
    pixmap1.fill(Qt::blue);
    item1->setIcon(pixmap1);
    item1->setToolTip("indexB");
    parentItem->appendRow(item1);

    QStandardItem * item2 = new QStandardItem();
    item2->setData("item2",Qt::EditRole);
    QPixmap pixmap2(50,50);
    pixmap2.fill(Qt::green);
    item2->setData(QIcon(pixmap2), Qt::DecorationRole);
    item2->setData("indexC",Qt::ToolTipRole);
    parentItem->appendRow(item2);

    //取出根节点第0行0列的模型索引
    QModelIndex indexA = model.index(0,0, QModelIndex());
    qDebug() << "model indexA row count is " << model.rowCount(indexA);

    //获取indexA节点下0行0列
    QModelIndex indexB = model.index(0,0,indexA);
    qDebug() << "indexB text is " << model.data(indexB,Qt::EditRole).toString();
    qDebug() << "indexB icon is " << model.data(indexB,Qt::DecorationRole);
    qDebug() << "indexB tool tip  is " << model.data(indexB,Qt::ToolTipRole).toString();

    QTreeView view;
    view.setModel(&model);
    view.show();
    return a.exec();
}

运行后程序效果图 https://cdn.llfc.club/1671159654393.jpg item0节点下挂载了item1和item2两个节点。程序用两种方式为item设置图标,提示,文本。 一种是setText,setIcon, setToolTip方式,另一种是setData方式。 setData的方式可以参考Qt的文档,文档里列举了一些可以设置的角色。 EditRole是可编辑角色,DecorationRole类似于图片显示,ToolTipRole类似于提示的角色。

特定模型

除了QStandardItemModel之外,还有一些其他集成好的特殊模型,如果我们要实现树形模型就子类化QStandardItemModel。 如果想实现列表模型就子类化QAbstractListModel,如果像实现表格模型就子类化QAbstractTableModel。 我们子类化QAbstractListModel,实现一个列表模型。

class StringListModel:public QAbstractListModel
{
    Q_OBJECT
public:
    StringListModel(const QStringList & strings, QObject* parent = 0);
    int rowCount(const QModelIndex& parent = QModelIndex()) 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,用来管理数据

StringListModel::StringListModel(const QStringList & strings,
                                 QObject* parent):
    QAbstractListModel(parent), stringList(strings)
{

}

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

QVariant StringListModel::data(const QModelIndex& index, int role) const{
    if(!index.isValid()){
        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函数中可以分别用一个listview和treeview显示

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QStringList list;
    list <<"a" << "b" << "c";

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

    QTableView tableView;
    tableView.setModel(&model);
    tableView.show();
    return a.exec();
}

https://cdn.llfc.club/1671174663824.jpg

设置可编辑选项

我们为自定义的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(const QModelIndex& index, int role) const{
    if(!index.isValid()){
        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();
}

接下来可以调用一下测试

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

运行的效果如下 https://cdn.llfc.club/1671256097654.jpg

源码链接

源码链接 https://gitee.com/secondtonone1/qt-learning-notes

results matching ""

    No results matching ""