第58课 - 自定义模型类 ( 数据应用的实现 )

1、自定义模型类 

QStandardltemModel是一个通用的模型类

         -能够以任意的方式组织数据(线性,非线性) 

         -数据组织的基本单位为数据项(QStandardltem

         -每一个数据项能够存储多个具体数据(附加数据角色) 

         -每一个数据项能够对数据状态进行控制(可编辑,可选,... ) 

 

Qt中的通用模型类QStandardltemModel 

                                           注意其中的聚合关系

Qt中的变体类型QVariant 

         -QVariant是一个用于封装的类型 

         -QVariant能够表示大多数常见的值类型 

         -QVariant每次只能封装(保存)单一类型的值 

         -QVariant的意义在于能够设计”返回类型可变的函数” 

变体类型QVariant中的常用成员函数 

2、编程实验 

变体类型QVariant的应用   58-1.cpp 

main.cpp

#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QPoint>
#include <QDebug>

QVariant func()
{
    QVariant ret;

    return ret;
}

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

    QVariant v1(1);
    QVariant v2(3.14);
    QVariant v3("520");
    QVariant v4(QPoint(15, 15));
    QVariant v5;

    qDebug() << v1.type();
    qDebug() << v1.typeName(); //返回值类型 const char*
    qDebug() << v1.toInt();

    qDebug() << endl;

    qDebug() << v2.type();
    qDebug() << v2.typeName();
    qDebug() << v2.toInt();
    qDebug() << v2.toDouble();
    qDebug() << v2.value<double>();

    qDebug() << endl;

    qDebug() << v3.type();
    qDebug() << v3.typeName();
    qDebug() << v3.toInt();
    qDebug() << v3.value<QString>();

    qDebug() << endl;

    bool ok = true;

    qDebug() << v4.type();
    qDebug() << v4.typeName();
    qDebug() << v4.toInt(&ok);
    qDebug() << ok;
    qDebug() << v4.toPoint();

    qDebug() << endl;

    qDebug() << v5.isValid();

    
    return a.exec();
}

3、自定义模型类 

工程中的常用模型设计 

         -解析数据源中的数据(数据库,网络,串口,等) 

         -将解析后的数据存入QStandardltem对象中 

         -根据数据间的关系在QStandardltemModel对象中组织数据项 

         -选择合适的视图显示数据值 

实例分析: 

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

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

             ✔ 计算平均成绩 

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

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

  

系统架构图 

系统核心类图 

4、系统实现 

DataSource类的设计与实现 

      - 设置数据源读取数据 

      - 对数据进行解析后生成数据对象 

               

Scorelnfo类的设计与实现 

      - 封装数据源中的一组完整数据 

      - 提供返回具体数据值的接口函数 

                   

ScorelnfoModel类的设计与实现 

      - 使用标准模型类QStandardltemModel作为成员 

      - Scorelnfo类对象为最小单位进行数据组织 

                 

数据交互流程图 

 

 

5、界面设计

               

 

右键上下文菜单的实现 

      -定义菜单对象(QMenu

      -连接菜单中的QAction对象到槽函数 

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

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

6、编程实验 

数据应用的实现    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);
    }

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

6、小结 

QStandardltemModel是一个通用的模型类 

QStandardltemModel能够以任意的方式组织数据 

使用QVariant能够设计”返回类型可变的函数” 

工程中常用数据应用架构为4层结构: 

     ★ 数据层,数据表示层,数据组织层,数据显示层 

工程中的架构图用于定义模块功能 

工程中的类图用于定义具体功能的接口 

工程中的流程图用于定义类对象间的交互 

模块实现结束后需要进行单元测试 

 

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

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_39654127/article/details/81774413