QT——model/view的使用

(一)Model/View 结构

Model-View-Controller(MVC), 是从Smalltalk发展而来的一种设计模式,常用于创建用户界面。 

MVC包含3个组件:

  • Model:         应用对象,用来表示数据
  • View:           模型的用户界面,用来显示数据
  • Controller:  定义了用户界面对用户输入的反应方式

Delegate(委托):为了对用户输入进行灵活处理被引入,用于定制数据的渲染和编辑方式

其中,模型与数据源进行通信,为架构中的其他组件提供了接口。视图从模型中获得模型索引(Model Index),模型索引用来表示数据项。在标准的视图中,委托渲染数据项,当编辑项目时,委托使用模型索引直接与模型进行通信。

(二)Models

1.所有模型的基类:

QAbstractItemModel

 

2.可子类化以下模型创建自定义模型:

QAbstractItemModel

QAbstractListModel

QAbstractTableModel

 

3.其他现成的模型:

QStringListModel:        用来存储一个简单的QString项目列表

QStandardItemModel: 管理复杂的树形结构数据项,每一个数据项可以包含任意的数据

QFileSystemModel:     提供了本地文件系统中文件和目录信息

QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel: 用来访问数据库

 

4.基本概念

常见的模型:列表模型、表格模型、树模型

模型索引:每一块可以通过模型获取的数据都使用一个模型索引来表示,视图和委托都使用这些索引来请求数据项并显示。模型索引包含一个指针,指向创建它们的模型。由QModelIndex类提供,它是对一块数据的临时引用,可以用来检索或修改模型中的数据。

行和列:要获得一个数据项的模型索引,必须指定模型的3个属性:行号、列号、父项的模型索引

    QModelIndex index = model->index(row, column, parent);

父项:当数据项是以根项为父项时,父项的模型索引可以用QModelIndex()表示

项角色:模型中的数据项可以作为各种角色在其他组件中会用,允许为不同的情况提供不同类型的数据

常用角色类型:

常量

描述

Qt::DisplayRole

数据被渲染为文本(数据为QString类型)

Qt::DecorationRole

数据被渲染为图标等装饰(数据为QColor/ QIcon/ QPixmap类型)

Qt::EditRole

数据可以在编辑器中进行编辑(数据为QString类型)

Qt::ToolTipRole

数据显示在数据项的工具提示中(数据为QString类型)

Qt::StatusTipRole

数据显示在状态栏中(数据为QString类型)

Qt::WhatsThisRole

数据显示在数据项的”What’s This?”模式下(数据为QString类型)

Qt::SizeHintRole

数据项的大小提示,将会应用到视图(数据为QString类型)

5.示例代码

<1>  文件模型的使用

#include <QApplication>
#include <QFileSystemModel>
#include <QTreeView>
#include <QListView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //创建文件系统模型
    QFileSystemModel model;
    //指定要监视的目录
    model.setRootPath(QDir::currentPath());
    
    //创建树视图
    QTreeView tree;
    //为视图指定模型
    tree.setModel(&model);
    //指定根索引
    tree.setRootIndex(model.index(QDir::currentPath()));
    
    
    //创建列表视图
    QListView list;
    list.setModel(&model);
    list.setRootIndex(model.index(QDir::currentPath()));
    
    tree.show();
    list.show();
    
    return a.exec();
}

<2>  模型索引的使用

#include <QApplication>
#include <QTreeView>
#include <QDebug>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QStandardItemModel model;
    //获取模型的根项,根项不可见
    QStandardItem *parentItem = model.invisibleRootItem();
    
    //创建标准项item0,并设置显示文本
    QStandardItem *item0 = new QStandardItem;
    item0->setText("A");
    QPixmap pixmap0(50, 50);
    pixmap0.fill("red");
    item0->setIcon(QIcon(pixmap0));
    item0->setToolTip("indexA");
    
    //将创建的标准项作为根项的子项
    parentItem->appendRow(item0);
    
    QStandardItem *item1 = new QStandardItem;
    item1->setText("B");
    QPixmap pixmap1(50, 50);
    pixmap1.fill("blue");
    item1->setIcon(QIcon(pixmap1));
    item1->setToolTip("indexB");
    
    item0->appendRow(item1);
    
    QStandardItem *item2 = new QStandardItem;
    QPixmap pixmap2(50, 50);
    pixmap2.fill("green");
    item2->setData("C", Qt::EditRole);
    item2->setData("indexC", Qt::ToolTipRole);
    item2->setData(QIcon(pixmap2), Qt::DecorationRole);

    item0->appendRow(item2);
    
    //在树视图中显示模型
    QTreeView view;
    view.setModel(&model);
    view.show();
    
    //获取数据项
    QModelIndex indexA = model.index(0, 0, QModelIndex());
    qDebug() << "indexA row count:" << model.rowCount(indexA);
    
    QModelIndex indexB = model.index(0, 0, indexA);
    qDebug() << "indexB text: " << model.data(indexB, Qt::EditRole).toString();
    qDebug() << "indexB toolTip:" << model.data(indexB, Qt::ToolTipRole).toString();
  
    
    return a.exec();
}

<3>  自定义模型方法

MyStrListModel.h

#include <QAbstractListModel>
#include <QStringList>

class MyStrListModel : public QAbstractListModel
{
    Q_OBJECT
public:
    MyStrListModel(const QStringList &strings, QObject *parent = 0)
        :QAbstractListModel(parent),stringList(strings){}
    
    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;
    
    //编辑
    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool          setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    
    
    //插入和删除
    bool          insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
    bool          removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
    
private:
    QStringList stringList;
    
};

MyStrListModel.cpp

#include "MyStrListModel.h"


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

QVariant MyStrListModel::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();
    }
}

QVariant MyStrListModel::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);
    }
}

Qt::ItemFlags MyStrListModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
    {
        return Qt::ItemIsEnabled;
    }
    return QAbstractItemModel::flags(index)| Qt::ItemIsEditable;
}

bool MyStrListModel::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;
}

bool MyStrListModel::insertRows(int row, int count, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), row, count + row);
    for (int n = 0; n < count; ++n)
    {
        stringList.insert(row, "");
    }
    endInsertRows();
    return true;
}

bool MyStrListModel::removeRows(int row, int count, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(),row, count + row);
    for (int n = 0; n < count; ++n)
    {
        stringList.removeAt(row);
    }
    endRemoveRows();
    return true;
}

main.cpp

#include "MyStrListModel.h"
#include <QApplication>
#include <QListView>
#include <QTableView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QStringList list;
    list << "a" << "b" << "c";
    MyStrListModel model(list);
    
    model.insertRows(3, 2);
    model.removeRows(0, 1);
    
    QListView listView;
    listView.setModel(&model);
    listView.show();
    
    QTableView tableView;
    tableView.setModel(&model);
    tableView.show();
    
    return a.exec();
}

(三)Views

1.所有视图的基类:

QAbstractItemView

 

2.可子类化以下视图创建自定义视图:

QAbstractItemView

QTableView:                将数据项显示为一个表格

QListView:                   将数据项显示为一个列表

QTreeView:                 将数据项显示在具有层次的列表中

3.视图中被选择的部分

视图中被选择的项目信息存储在一个QItemSelectionModel实例中,这样被选择的项目模型索引便保持在一个独立的模型中,与所有的视图都是独立的。当在一个模型上设置多个视图时,就可以实现在多个视图之间共享选择。

使用选择模式示例代码:

代码包含:

  1. 选择模型的使用
  2. 获得选择项目、对选择项目进行反选
  3. 选择项目改变时动态设置
  4. 当多个视图显示同一个模型的数据时,共享选择

MainWindow.h

#include <QMainWindow>

class QTableView;
class QItemSelection;
class QModelIndex;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
public slots:
    void    getCurrentItemData();
    void    toggleSelection();
    
    void    updateSelection(const QItemSelection &selected, const QItemSelection &deselected);
    void    changeCurrent(const QModelIndex &current, const QModelIndex &previous);
    
private:
    Ui::MainWindow  *ui;
    QTableView      *tableView;
    QTableView      *tableView2;
};

MainWindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QStandardItemModel>
#include <QTableView>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    //*******     1. 选择模型的使用     ********//
    //设置基础数据
    QStandardItemModel *model = new QStandardItemModel(7, 4, this);
    for (int row = 0; row < 7; ++row)
    {
        for (int column = 0; column < 4; ++column)
        {
            QStandardItem *item = new QStandardItem(QString("%1").arg(row * 4 + column));
            model->setItem(row, column, item);
        }
    }
    
    //设置视图并显示
    tableView = new QTableView;
    tableView->setModel(model);
    setCentralWidget(tableView);
    
    //获取视图的项目选择模型
    QItemSelectionModel *selectionModel = tableView->selectionModel();
    
    //使用索引创建选择
    QModelIndex topLeft;
    QModelIndex bottomRight;
    topLeft = model->index(1, 1, QModelIndex());
    bottomRight = model->index(5, 2, QModelIndex());
    QItemSelection selection(topLeft, bottomRight);
    
    //用指定的选择模式选择项目
    selectionModel->select(selection, QItemSelectionModel::Select);
    
    
    //*******     2. 获得选择项目、对选择项目进行反选     ********//
    
    //向主窗口工具栏中添加两个动作图标
    ui->mainToolBar->addAction(tr("当前项目"), this, SLOT(getCurrentItemData()));
    ui->mainToolBar->addAction(tr("切换选择"), this, SLOT(toggleSelection()));
    
    
    //*******     3. 选择项目改变时动态设置     ********//
    
    //关联选择模型改变信号
    connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection))
            ,this, SLOT(updateSelection(QItemSelection,QItemSelection)));
    
    //关联选择项改变信号
    connect(selectionModel, SIGNAL(currentChanged(QModelIndex,QModelIndex))
            ,this, SLOT(changeCurrent(QModelIndex,QModelIndex)));
    
    //*****  4. 当多个视图显示同一个模型的数据时,共享选择***//
    tableView2 = new QTableView;
    tableView2->setWindowTitle("tableView2");
    tableView2->resize(400, 300);
    tableView2->setModel(model);
    tableView2->setSelectionModel(selectionModel);
    tableView2->show();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete tableView;
    delete tableView2;
}

//输出当前项目的内容
void MainWindow::getCurrentItemData()
{
    qDebug() << tr("当前项目的内容:")
             << tableView->selectionModel()->currentIndex().data().toString();
}

void MainWindow::toggleSelection()
{
    QModelIndex topLeft = tableView->model()->index(0, 0, QModelIndex());
    QModelIndex bottomRight = tableView->model()->index(tableView->model()->rowCount(QModelIndex()) - 1
                                                        ,tableView->model()->columnCount(QModelIndex())-1
                                                        ,QModelIndex());
    QItemSelection curSelection(topLeft, bottomRight);
    tableView->selectionModel()->select(curSelection, QItemSelectionModel::Toggle);
}

void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected)
{
    QModelIndex index;
    QModelIndexList list = selected.indexes();
    
    //为现在选择的项目填充值
    foreach (index, list) 
    {
        QString text = QString("(%1, %2)").arg(index.row()).arg(index.column());
        tableView->model()->setData(index, text);
    }
    
    //清空上一次选择的项目
    list = deselected.indexes();
    foreach(index, list)
    {
        tableView->model()->setData(index, "");
    }
}

void MainWindow::changeCurrent(const QModelIndex &current, const QModelIndex &previous)
{
    qDebug() << tr("move(%1, %2) to (%3, %4)")
                .arg(previous.row()).arg(previous.column())
                .arg(current.row()).arg(current.column());
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

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

Delegates

在模型/视图结构中没有包含一个完全分离的组件来处理与用户的交互,一般视图用来将模型中的数据展示给用户,也用来处理用户的输入,为了获得更高的灵活性,交互可以交由委托来进行,于是引入了delegate

1.委托的基类:

QAbstractItemDelegate

 

2.两种委托:

①QStyledItemDelegate:

Qt4.4后,此为默认的委托实现,也被用作Qt标准视图的默认委托,其使用当前的样式绘制它的项目

②QItemDelegate

   <1>简单的基于部件的委托可以通过子类化QItemDelegate进行实现,可以使用已定义好的一些函数的默认实现

  <2>Qt中的标准视图都使用QItemDelegate的实例来提供编辑功能,这种委托接口的默认实现为QListView、QTableView、QTreeView等标准视图的每一个项目提供了普通风格的渲染

③委托的编辑器:可以通过两种方式实现:

    <1>使用部件来管理编辑过程

    <2>直接处理事件

 

 

注:以上代码实现的编译环境:

编译器:Qt 5.9 MinGW 32bit

编辑器:Qt Cteator 4.3

支持参考书: 《Qt Creator 快速入门》第2版——北京航空航天大学出版社

 

猜你喜欢

转载自blog.csdn.net/Jecklin_online/article/details/81486795