Qt中的模型视图设计模式

目录

1、模型视图模式

1.1 基本概括

1.2 模型中的索引

1.3 QFileSystemModel

1.4 QStringListModel

1.5 标准模型与数据角色

1.6 自定义模型类 QStandardltemModel

1.7 自定义模型类 QAbstractTableModel

2、Qt模型视图中的委托


1、模型视图模式

1.1 基本概括

模型视图模式的工作机制 :模型(数据)与视图(显示)相分离 。当数据发生改变时 ,模型发出信号通知视图;当用户与视图进行交互时 ,视图发出信号提供交互信息 

Qt中的模型视图

-模型用于组织数据源,不关心数据的显示方式 ,视图用于定义数据的显示方式,不关心数据的组织方式

-QAbstractItemModel的各种子类代表着各种组织数据的结构的模型,QAbstractItemView的各种子类代表着各种数据显示方式的视图

在Qt中,模型必须为每一个数据提供独一无二的模型索引视图通过模型索引访问模型中的具体数据

      -模型创建模型索引: QModelIndex QAbstractItemModel::createIndex(int row, int column, void *ptr = Q_NULLPTR) const

      -模型访问模型索引关联的数据:QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const

模型定义标准接口(成员函数)对数据进行访问 ,视图通过标准接口获取数据并定义显示方式 

模型与视图都是QT对象,模型使用信号与槽的机制通知视图数据变化 ,模型中的数据都是以层次结构表示的 

1.2 模型中的索引

模型索引是数据与视图分离的重要机制 ,模型中的数据使用唯一的模型索引来访问 

QModelIndex是Qt中的模型索引类,包含具体数据的访问途径 ,含一个指向模型的指针 

索引是访问模型中具体数据的约定方式 ,获取索引的通用形式为三元组(row, column, parent) 

索引在需要时由模型实时创建 ,使用空索引作为父节点表示顶层数据元素 

特殊的模型可以自定义特殊的索引获取方式 

模型中的通用树形结构 

 

Root为虚拟节点 用于统一所有数据到同一棵树中 ,同一节点的子节点以递增的方式编号 ,通过(index, parent)的方式确定节点 

模型中数据索引的通用方式 :三元组:(row、column、parent)

理解模型索引

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPlainTextEdit>
#include <QFileSystemModel>

class Widget : public QWidget
{
    Q_OBJECT

    QPlainTextEdit m_edit;
    QFileSystemModel m_fsm;
protected slots:
    void onDirectoryLoaded(const QString& path);
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H
#include "Widget.h"
#include <QDir>
#include <QModelIndex>
#include <QByteArray>
#include <QBuffer>
#include <QTextStream>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_edit.setParent(this);
    m_edit.move(10, 10);
    m_edit.resize(500, 300);

    connect(&m_fsm, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString)));
    //This signal is emitted when the gatherer thread has finished to load the path.

    m_fsm.setRootPath(QDir::currentPath()); // 为模型设置数据源
    // 有感:所谓模型其实不过是一系列数据操作的封装
}

//目录加载完毕后槽函数调用
void Widget::onDirectoryLoaded(const QString& path)
{
    QModelIndex root = m_fsm.index(path, 0);  //Returns the model index for the given path and column.
    QByteArray array;
    QBuffer buffer(&array);

    if( buffer.open(QIODevice::WriteOnly) )
    {
        QTextStream out(&buffer);

        out << m_fsm.isDir(root) << endl;           
        out << m_fsm.data(root).toString() << endl; // 模型通过模型索引拿数据
        out << root.data().toString() << endl;      // 可以直接通过索引取数据

        out << &m_fsm << endl;
        out << root.model() << endl;         // Returns a pointer to the model containing the item that this index refers to.
        out << m_fsm.filePath(root) << endl; // Returns the path of the item stored in the model under the index given
        out << m_fsm.fileName(root) << endl; // Returns the path of the item stored in the model under the index given.
        out << endl;

        // Returns the number of rows under the given parent
        for(int i=0; i<m_fsm.rowCount(root); i++)
        {
            QModelIndex ci = m_fsm.index(i, 0, root); // 第i行第0列数据的索引

            out << ci.data().toString() << endl;      // 索引关联数据
        }

        out.flush();
        buffer.close();
    }

    if( buffer.open(QIODevice::ReadOnly) )
    {
        QTextStream in(&buffer);

        m_edit.insertPlainText(in.readAll());

        buffer.close();
    }
}

Widget::~Widget()
{

}

 

1.3 QFileSystemModel

文件系统模型用于组织文件夹数据当目录发生变化时模型发出信号通知视图 ,于是此树状视图就重新呈现数据

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFileSystemModel>
#include <QTreeView>
#include <QPushButton>

class Widget : public QWidget
{
    Q_OBJECT

    QFileSystemModel m_fsModel; // 定义文件系统模型,用于组织文件夹数据
    QTreeView m_treeView;       // 定义树形显示视图
    QPushButton newFolderBtn;
    QPushButton rmFolderBtn;
public:
    Widget(QWidget *parent = 0);
    ~Widget();

protected slots:
    void onNewFolderBtn();
    void onRmFolderBtn();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDir>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setFixedSize(800, 800);
    m_treeView.setParent(this); // QAbstractItemView 子类不过就是个组件,父组件为Widget对象
    m_treeView.move(0, 0);      // 绝对定位组件位置,大小
    m_treeView.resize(800, 300);


    newFolderBtn.setParent(this);
    newFolderBtn.setText("New Folder");
    newFolderBtn.resize(100, 40);
    newFolderBtn.move(40, 400);
    rmFolderBtn.setParent(this);
    rmFolderBtn.setText("remove Folder");
    rmFolderBtn.resize(160, 40);
    rmFolderBtn.move(400, 400);

    m_fsModel.setRootPath(QDir::currentPath()); // 将模型监视的目录设置为当前工作目录。对该目录中的文件和目录的任何更改都将反映在模型中

    m_treeView.setModel(&m_fsModel); // 为视图设置一个模型

    m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath()));
    //模型索引从模型的index()函数得到
    //为树形显示视图根部设置索引,指明数据从根开始呈现


    connect(&newFolderBtn, SIGNAL(clicked()), this, SLOT(onNewFolderBtn()));
    connect(&rmFolderBtn, SIGNAL(clicked()), this, SLOT(onRmFolderBtn()));
}

void Widget::onNewFolderBtn()
{
    QString dirName = "";
    dirName = QInputDialog::getText(this, "tip", "please input dir name");

    if(dirName == "")
        return;

    QModelIndex curIndex = m_treeView.currentIndex();

    qDebug() << "(" << curIndex.row() << "," << curIndex.column() << ")" << endl;
    qDebug() << curIndex.data() << endl;

    curIndex = curIndex.parent();
    if(!curIndex.isValid())
        return;

    // Create a directory with the name in the parent model index.
    if( !(m_fsModel.mkdir(curIndex, dirName).isValid()) )
        QMessageBox::information(this, "New Folder", "failed to new Folder");
}
void Widget::onRmFolderBtn()
{
    QModelIndex curIndex = m_treeView.currentIndex();
    if (!curIndex.isValid())
        return;

    bool ok;
    if (m_fsModel.fileInfo(curIndex).isDir())
        ok = m_fsModel.rmdir(curIndex);
    else
        ok = m_fsModel.remove(curIndex);
    if (!ok) {
        QMessageBox::information(this, "Remove", QString("Failed to remove %1").arg(m_fsModel.fileName(curIndex)));
    }
}

Widget::~Widget()
{

}

 

1.4 QStringListModel

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QStringListModel>
#include <QListView>
#include <QPushButton>

class Widget : public QWidget
{
    Q_OBJECT

    QStringListModel* strListModel;
    QListView* listView;

    QPushButton* addBtn;
    QPushButton* delBtn;
public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected slots:
    void onAddBtn();
    void onDelBtn();
    void onTest(const QModelIndex &index);
};

#endif // WIDGET_H
#include "Widget.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setFixedSize(500, 500);

    QStringList list;
    list << "a" << "b" << "c";

    strListModel = new QStringListModel(this);
    listView = new QListView(this);
    addBtn = new QPushButton(this);
    delBtn = new QPushButton(this);

    listView->resize(width(), 200);
    addBtn->setText("add");
    addBtn->resize(60 ,40);
    addBtn->move(10, 300);
    delBtn->setText("del");
    delBtn->resize(60 ,40);
    delBtn->move(100, 300);

    listView->setModel(strListModel);  // 关联模型视图
    strListModel->setStringList(list); // 设置模型数据源:
    //QStringListModel其实就是封装了QStringList, QStringListModel内部有一个QStringList lst成员

    qDebug() << strListModel->stringList() << endl; // ("a", "b", "c")
    qDebug() << strListModel->rowCount() << endl;   // 3

    connect(addBtn, SIGNAL(clicked()), this, SLOT(onAddBtn()));
    connect(delBtn, SIGNAL(clicked()), this, SLOT(onDelBtn()));
    connect(listView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(onTest(const QModelIndex&)));
}
void Widget::onTest(const QModelIndex &index)
{
    int row = index.row();

    qDebug() << "(" << row << "," << index.column() << ", " << ")" << endl;
    qDebug() << index.data() << endl;
}

void Widget::onAddBtn()
{
    QString text = "";
    text = QInputDialog::getText(this, "tip", "please input data");

    if(text != "")
    {
        // Returns the model index of the current item.
        QModelIndex curIndex = listView->currentIndex();
        int row = curIndex.row();

        if(row != -1) // QModelIndex 有isValid()
        {
            // Inserts count rows into the model, beginning at the given row
            strListModel->insertRows(row, 1);       // 在指定的行位置插入1行
            // Sets the data for the specified role in the item with the given index in the model, to the provided value.
            strListModel->setData(curIndex, text);  // 模型通过逆向索引设置数据
        }else{
            strListModel->insertRows(0, 1);
            strListModel->setData(strListModel->index(0), text);
        }
    }
}
void Widget::onDelBtn()
{
    if (strListModel->rowCount() > 0) {
          strListModel->removeRows(listView->currentIndex().row(), 1);
      }
}

Widget::~Widget()
{

}

1.5 标准模型与数据角色

标准模型定义  :QStandardItemModel是一种基于数据项的模型。 数据项由QStandardItem表示

数据角色的概念 

       -模型中的数据有附加的角色属性 ,数据角色定义了数据显示的标准方式 

       -模型中的数据在视图中的用途(显示方式)可能不同,模型必须为数据设置特定数据角色(数据属性) 

       -数据角色用于提示视图数据的作用 ,数据角色是不同视图以统一风格显示数据的标准 

数据角色的意义 :定义了数据在特定系统下的标准用途 ,不同的视图可以通过相同标准显示数据 

注意: 数据角色只是一个附加的属性,这个属性代表推荐的数据显示方式。 不同的视图完全可以自由解析或者忽略数据的角色信息

Qt中的数据角色定义 

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTableView>
#include <QListView>
#include <QTreeView>
#include <QStandardItemModel>

class Widget : public QWidget
{
    Q_OBJECT
    
    QStandardItemModel m_model;
    QTableView m_tableView;
    QListView m_listView;
    QTreeView m_treeView;

    void initModel();
    void initView();
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QStandardItem>

Widget::Widget(QWidget *parent)
    : QWidget(parent, Qt::WindowContextHelpButtonHint)
{
    setFixedSize(600, 600);
    initModel();
    initView();

    m_tableView.setModel(&m_model);  // 多个视图使用一个模型
    m_listView.setModel(&m_model);
    m_treeView.setModel(&m_model);
}

void Widget::initModel()
{
    //Returns the model's invisible root item. 不就是树形模型的统一管理嘛
    QStandardItem* root = m_model.invisibleRootItem();

    //The QStandardItem class provides an item for use with the QStandardItemModel class.
    QStandardItem* itemA = new QStandardItem();
    QStandardItem* itemB = new QStandardItem();
    QStandardItem* itemC = new QStandardItem();
    QStandardItem* itemChild = new QStandardItem();

    //Sets the item's data for the given role to the specified value.
    itemA->setData("A", Qt::DisplayRole);
    itemA->setData("Tip A", Qt::ToolTipRole);
    itemA->setData("Help A", Qt::WhatsThisRole);

    itemB->setData("B", Qt::DisplayRole);
    itemB->setData("Tip B", Qt::ToolTipRole);

    itemC->setData("C", Qt::DisplayRole);
    itemC->setData("Tip C", Qt::ToolTipRole);
    itemC->setData("Help C", Qt::WhatsThisRole);

    itemChild->setData("Child", Qt::DisplayRole);
    itemChild->setData("Tip Child", Qt::ToolTipRole);
    itemChild->setData("Help Child", Qt::WhatsThisRole);

    itemC->setChild(0, 0, itemChild);

    root->setChild(0, 0, itemA);
    root->setChild(0, 1, itemB);
    root->setChild(1, 0, itemC);
}

void Widget::initView()
{
    m_tableView.setParent(this);
    m_tableView.move(0, 0);
    m_tableView.resize(width(), height()/3);

    m_listView.setParent(this);
    m_listView.move(0, height()/3);
    m_listView.resize(width(), height()/3);

    m_treeView.setParent(this);
    m_treeView.move(0, height()/3 * 2);
    m_treeView.resize(width(), height()/3);
}

Widget::~Widget()
{

}

1.6 自定义模型类 QStandardltemModel

QStandardltemModel是一个通用的模型类

         -能够以任意的方式组织数据(线性,非线性) ,数据组织的基本单位为数据项(QStandardltem) 

         -每一个数据项能够存储多个具体数据(附加数据角色) ,每一个数据项能够对数据状态进行控制(可编辑,可选,... ) 

Qt中的通用模型类QStandardltemModel 

                                           注意其中的聚合关系

工程中的常用模型设计 

         -解析数据源中的数据(数据库,网络,串口,等) ,将解析后的数据存入QStandardltem对象中 

         -根据数据间的关系在QStandardltemModel对象中组织数据项 ,选择合适的视图显示数据值 

实例分析: 

     -在文件中以行的形式存储了考试成绩信息(ID, Name, Score) 

     -开发GUI程序显示文件中的信息 

             ✔ 计算平均成绩 

             ✔ 查找最好成绩和最差成绩 

             ✔ 可刷新显示的内容和删除内容 

  

系统架构图

     -工程中常用数据应用架构为4层结构: 数据层,数据表示层,数据组织层,数据显示层 

     -数据应用4层架构设计非常易于扩展和维护 

     -数据源类(DataSource)用于抽象表示数据的来源

     -模型类(Model)用于从数据源获取数据并组织 

     -视图类(View)用于显示模型中的数据 

系统核心类图 

                 DataSource读取文件后得到一组ScoreInfo(id,name,score)对象,这一组对象数据使用标准通用模型组织数据,QTableView显示数据

DataSource:设置数据源并读取数据 ,对数据进行解析后生成数据对象 

Scorelnfo(实体类):封装数据源中的一组完整数据 ,提供返回具体数据值的接口函数 

ScorelnfoModel(模型类):使用标准模型类QStandardltemModel作为成员 ,以Scorelnfo类对象为最小单位进行数据组织 

               

数据交互流程图 

界面设计

               

 

右键上下文菜单的实现 

      -定义菜单对象(QMenu) ,连接菜单中的QAction对象到槽函数 

      -定义事件过滤器,并处理ContextMenu事件

      -在当前鼠标的位置打开菜单对象 

数据应用的实现    DataAppDemo.pro 

ScoreInfo.h

#ifndef SCOREINFO_H
#define SCOREINFO_H

#include <QObject>

class ScoreInfo : public QObject
{
    Q_OBJECT

    QString m_id;
    QString m_name;
    int m_score;
public:
    explicit ScoreInfo(QObject* parent = 0);
    explicit ScoreInfo(QString id, QString name, int score, QObject* parent = 0);
    ScoreInfo(const ScoreInfo& obj); // QObject中的拷贝构造和赋值操作符为私有成员
    ScoreInfo& operator= (const ScoreInfo& obj);
    QString id();
    QString name();
    int score();
    
};

#endif // SCOREINFO_H

ScoreInfo.cpp

#include "ScoreInfo.h"

ScoreInfo::ScoreInfo(QObject* parent) : QObject(parent)
{
    m_id = "NULL";
    m_name = "NULL";
    m_score = -1;
}

ScoreInfo::ScoreInfo(QString id, QString name, int score, QObject* parent)
{
    m_id = id;
    m_name = name;
    m_score = score;
}

ScoreInfo::ScoreInfo(const ScoreInfo& obj)
{
    m_id = obj.m_id;
    m_name = obj.m_name;
    m_score = obj.m_score;
}

ScoreInfo& ScoreInfo::operator= (const ScoreInfo& obj)
{
    if( this != &obj )
    {
        m_id = obj.m_id;
        m_name = obj.m_name;
        m_score = obj.m_score;
    }

    return *this;
}

QString ScoreInfo::id()
{
    return m_id;
}

QString ScoreInfo::name()
{
    return m_name;
}

int ScoreInfo::score()
{
    return m_score;
}

DataSource.h

#ifndef DATASOURCE_H
#define DATASOURCE_H

#include <QObject>
#include <QList>
#include "ScoreInfo.h"


class DataSource : public QObject
{
    Q_OBJECT

    QList<ScoreInfo> m_data; 
    bool parse(QString line, ScoreInfo& info);
public:
    explicit DataSource(QObject* parent = 0);
    bool setDataPath(QString path);
    QList<ScoreInfo> fetchData();
    int count();
};

#endif // DATASOURCE_H

DataSource.cpp

#include "DataSource.h"
#include <QFile>
#include <QTextStream>
#include <QStringList>

DataSource::DataSource(QObject *parent) : QObject(parent)
{
}

// 设置数据源并读取数据 
bool DataSource::setDataPath(QString path) 
{
    bool ret = true;
    QFile file(path);

    if( file.open(QIODevice::ReadOnly | QIODevice::Text) )
    {
        QTextStream in(&file);

        while( !in.atEnd() )
        {
            ScoreInfo info;

            if( parse(in.readLine(), info) ) 
            {
                m_data.append(info); 
            }
        }

        file.close();
    }
    else
    {
        ret = false;
    }

    return ret;
}

//对数据进行解析后生成数据对象 
bool DataSource::parse(QString line, ScoreInfo& info)
{
    bool ret = true;
    QStringList list = line.split(",", QString::SkipEmptyParts);

    if( list.count() == 3 )
    {
        QString id = list[0].trimmed();
        QString name = list[1].trimmed();
        QString score = list[2].trimmed();
        int value = score.toInt(&ret);

        if( ret && (0 <= value) && (value <= 150) )
        {
            info = ScoreInfo(id, name, value);
        }
        else
        {
            ret = false;
        }
    }
    else
    {
        ret = false;
    }

    return ret;
}

QList<ScoreInfo> DataSource::fetchData()
{
    QList<ScoreInfo> ret = m_data;

    m_data.clear();

    return ret;
}

int DataSource::count()
{
    return m_data.count();
}


ScoreInfoModel.h

#ifndef SCOREINFOMODEL_H
#define SCOREINFOMODEL_H

#include <QObject>
#include <QStandardItem>
#include <QTableView>
#include <QList>
#include "ScoreInfo.h"

class ScoreInfoModel : public QObject
{
    Q_OBJECT

    QStandardItemModel m_model;
public:
    explicit ScoreInfoModel(QObject *parent = 0);
    bool add(ScoreInfo info);
    bool add(QList<ScoreInfo> list);
    bool remove(int i);
    ScoreInfo getItem(int i);
    int count();
    void clear();
    void setView(QTableView& view);
};

#endif // SCOREINFOMODEL_H

ScoreInfoModel.cpp

#include "ScoreInfoModel.h"
#include <QStandardItem>
#include <QVariant>

ScoreInfoModel::ScoreInfoModel(QObject* parent) : QObject(parent)
{
}

bool ScoreInfoModel::add(ScoreInfo info)
{
    QStandardItem* root = m_model.invisibleRootItem();
    QStandardItem* item0 = new QStandardItem();
    QStandardItem* item1 = new QStandardItem();
    QStandardItem* item2 = new QStandardItem();
    bool ret = true;

    if( m_model.rowCount() == 0 )
    {
        QStringList list;

        list.append("ID");
        list.append("Name");
        list.append("Score");

        m_model.setHorizontalHeaderLabels(list); //使用list设置水平标题标签。如有必要,将列数增加到标签的大小
    }

    if( (root != NULL) && (item0 != NULL) && (item1 != NULL) && (item2 != NULL) )
    {
        item0->setData(info.id(), Qt::DisplayRole);
        item1->setData(info.name(), Qt::DisplayRole);
        item2->setData(info.score(), Qt::DisplayRole);

        item0->setEditable(false);
        item1->setEditable(false);
        item2->setEditable(false);

        int newRow = count();

        root->setChild(newRow, 0, item0);
        root->setChild(newRow, 1, item1);
        root->setChild(newRow, 2, item2);
    }
    else
    {
        ret = false;
    }

    return ret;
}

bool ScoreInfoModel::add(QList<ScoreInfo> list)
{
    bool ret = true;

    for(int i=0; i<list.count(); i++)
    {
        ret = ret && add(list[i]);
    }

    return ret;
}

bool ScoreInfoModel::remove(int i)
{
    bool ret = true;

    if( (0 <= i) && (i < count()) )
    {
        m_model.removeRow(i);
    }
    else
    {
        ret = false;
    }

    return ret;
}

void ScoreInfoModel::clear()
{
    m_model.clear();
}

ScoreInfo ScoreInfoModel::getItem(int i)
{
    ScoreInfo ret;

    if( (0 <= i) && (i < count()) )
    {
        QModelIndex index0 = m_model.index(i, 0, QModelIndex());  // 获得i行三个数据索引
        QModelIndex index1 = m_model.index(i, 1, QModelIndex());
        QModelIndex index2 = m_model.index(i, 2, QModelIndex());
        QVariant v0 = index0.data(Qt::DisplayRole);
        QVariant v1 = index1.data(Qt::DisplayRole);
        QVariant v2 = index2.data(Qt::DisplayRole);

        ret = ScoreInfo(v0.toString(), v1.toString(), v2.toInt());
    }

    return ret;
}

int ScoreInfoModel::count()
{
    return m_model.rowCount();
}

void ScoreInfoModel::setView(QTableView& view)
{
    view.setModel(&m_model);
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QTableView>
#include <QPushButton>
#include <QMenu>
#include "ScoreInfoModel.h"

class Widget : public QWidget
{
    Q_OBJECT
    
    ScoreInfoModel m_model;
    QTableView m_view;
    QPushButton m_refreshBtn;
    QPushButton m_clearBtn;
    QPushButton m_scoreBtn;
    QMenu m_menu;
private slots:
    void onRefreshBtnClicked();
    void onClearBtnClicked();
    void onScoreBtnClicked();
    void onDeleteActionClicked();
public:
    Widget(QWidget* parent = 0);
    bool eventFilter(QObject* obj, QEvent* evt);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include "ScoreInfo.h"
#include "DataSource.h"
#include <QMessageBox>
#include <QEvent>
#include <QDebug>

Widget::Widget(QWidget *parent) : QWidget(parent)
{
    m_view.setParent(this);
    m_view.move(10, 10);
    m_view.resize(345, 180);
    m_view.installEventFilter(this);

    m_refreshBtn.setParent(this);
    m_refreshBtn.move(10, 200);
    m_refreshBtn.resize(95, 30);
    m_refreshBtn.setText("Refresh");

    m_clearBtn.setParent(this);
    m_clearBtn.move(135, 200);
    m_clearBtn.resize(95, 30);
    m_clearBtn.setText("Clear");

    m_scoreBtn.setParent(this);
    m_scoreBtn.move(260, 200);
    m_scoreBtn.resize(95, 30);
    m_scoreBtn.setText("Score");

    m_menu.addAction("Delete");



    m_model.setView(m_view);

    connect(&m_refreshBtn, SIGNAL(clicked()), this, SLOT(onRefreshBtnClicked()));
    connect(&m_clearBtn, SIGNAL(clicked()), this, SLOT(onClearBtnClicked()));
    connect(&m_scoreBtn, SIGNAL(clicked()), this, SLOT(onScoreBtnClicked()));
    connect(m_menu.actions()[0], SIGNAL(triggered()), this, SLOT(onDeleteActionClicked()));

    onRefreshBtnClicked();
}

void Widget::onDeleteActionClicked()
{
    m_model.remove(m_view.currentIndex().row());
}

void Widget::onRefreshBtnClicked()
{
    DataSource ds;

    m_model.clear(); //防止内容重复

    if( ds.setDataPath("C:/Users/wss/Desktop/test.txt") ) //设置数据源
    {
        m_model.add(ds.fetchData());
    }
    else
    {
        QMessageBox::critical(this, "Error", "Data source read error!", QMessageBox::Ok);
    }
}

void Widget::onClearBtnClicked()
{
    m_model.clear();
}

void Widget::onScoreBtnClicked()
{
    int min = 256;
    int max = 0;
    int average = 0;

    if( m_model.count() > 0 )
    {
        for(int i=0; i<m_model.count(); i++)
        {
            ScoreInfo info = m_model.getItem(i);

            if( info.score() < min )
            {
                min = info.score();
            }

            if( info.score() > max )
            {
                max = info.score();
            }

            average += info.score();
        }

        average /= m_model.count();

        QMessageBox::information(this, "Statistic", QString().sprintf("Min: %d\nMax: %d\nAverage: %d", min, max, average), QMessageBox::Ok);
    }
    else
    {
        QMessageBox::information(this, "Statistic", "No data record!", QMessageBox::Ok);
    }
}

bool Widget::eventFilter(QObject* obj, QEvent* evt)
{
    if( (obj == &m_view) && (evt->type() == QEvent::ContextMenu) )
    {
        m_menu.exec(cursor().pos());
    }

    return QWidget::eventFilter(obj, evt);
}

Widget::~Widget()
{
    
}

main.cpp

#include <QtGui/QApplication>
#include "Widget.h"

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

1.7 自定义模型类 QAbstractTableModel

QAbstractTableModel类提供了一个抽象模型,可以通过子类化创建表模型

QAbstractTableModel实现了通用的index() 和 parent() 函数

QAbstractTableModel的子类必须实现 rowCount()、columnCount()和data(),尽量实现headerData()。

若想可编辑、可调整大小等模型还应实现更多接口,参见帮助文档

#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H

#include <QObject>
#include <QAbstractTableModel>
#include <QMap>

class MyTableModel : public QAbstractTableModel
{
    QStringList headList; // 表头数据
    QStringList nameList; // 第0列数据
    QVector<int> subVec;  // 第1列真实数据
    QMap<int, QString> m_map; // 第1列显示数据

public:
    MyTableModel(QObject* parent = 0);

    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
};

#endif // MYTABLEMODEL_H
#include "MyTableModel.h"

MyTableModel::MyTableModel(QObject *parent)
{
    headList << "姓名" << "学科";
    nameList << "张三" << "李四" << "王五" << "赵六" << "田七";
    subVec << 1 << 2 << 3 << 4 << 5;
    m_map.insert(1, "C++");
    m_map.insert(2, "Java");
    m_map.insert(3, "Python");
    m_map.insert(4, "PHP");
    m_map.insert(5, "JavaScrip");
}
// 实现
int MyTableModel::rowCount(const QModelIndex &parent) const
{
    return m_map.count();
}
int MyTableModel::columnCount(const QModelIndex &parent) const
{
    return 2;
}
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    if(role == Qt::DisplayRole)
    {
        switch (index.column())
        {
        case 0:
            return nameList[index.row()];
            break;
        case 1:
            return m_map[subVec[index.row()]];
            break;
        default:
            return QVariant();
            break;
        }
    }

    return QVariant();
}
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
        return headList[section];

    return QAbstractTableModel::headerData(section, orientation, role);
}
#include "MyTableModel.h"
#include <QTableView>
#include <QApplication>

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

    MyTableModel model;
    QTableView view;
    view.setModel(&model);
    view.setAlternatingRowColors(true);
    view.setWindowTitle(QObject::tr("Student Table"));
    view.resize(400, 400);
    view.show();

    return a.exec();
}

 

2、Qt模型视图中的委托

https://blog.csdn.net/qq_39654127/article/details/81835788

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值