前面兩節講的model是一維的,這次開始二維的也就是我們常說的Table,相對與list,我們多了一個列的概念。
下面講解一個例子。我先說明一下我們這個例子,在程式目錄下,我們有一個文字檔,其中存放的學生資訊。
資料存放的格式
學號 姓名 性別
xxx xxx x
每個學生的資訊佔一行,現在我們需要將這個檔案中的所有學生資訊載入顯示。
在例子中主要涉及這幾個類,Student,FileReader和DataTableModel類。
Student是學生實體類,FileRead從檔案載入資料,後面的一個就是我們的model了。
下面代碼貼出來
Student
/**************************************************author:周翔*e-mail:604487178@qq.com*blog:http://blog.csdn.net/zhx6044***************************************************/#ifndef STUDENT_HPP#define STUDENT_HPP#include <QString>#include <QTextStream>class Student{ friend QTextStream& operator >>(QTextStream &in,Student &t) { in >> t.m_no >> t.m_name >> t.m_sex; return in; }public: Student(){} Student(const QString &no, const QString &name,const QString &sex); const QString& getNo() const; const QString& getName() const; const QString& getSex() const; static int members() { return 3; } QString operator [](int i) const{ switch (i) { case 0: return m_no; case 1: return m_name; case 2: return m_sex; default: break; } return QString(); } static QString describe(int i) { switch (i) { case 0: return "No."; case 1: return "Name"; case 2: return "Sex"; default: break; } return QString(); }private: QString m_no; QString m_name; QString m_sex;};#endif // STUDENT_HPP/**************************************************author:周翔*e-mail:604487178@qq.com*blog:http://blog.csdn.net/zhx6044***************************************************/#include "student.hpp"Student::Student(const QString &no, const QString &name, const QString &sex): m_no(no), m_name(name), m_sex(sex){}inlineconst QString& Student::getNo() const{ return m_no;}inlineconst QString& Student::getName() const{ return m_name;}inlineconst QString& Student::getSex() const{ return m_sex;}
Student類有三個資料成員,m_no學號,m_name姓名,m_sex性別。兩個靜態成員函數對類屬性的一些描述,這個是侵入性的,後面最佳化可以是非侵入式的。
重載的[]是為了使用方便,>>是為了支援流。
FileReader是一個模板類
/**************************************************author:周翔*e-mail:604487178@qq.com*blog:http://blog.csdn.net/zhx6044***************************************************/#ifndef FILEREADER_HPP#define FILEREADER_HPP#include <QFile>#include <QTextStream>#include "student.hpp"//you also can use QDataStreamtemplate <typename T>class FileReader{public: FileReader(const QString &name); ~FileReader(); bool open(); bool hasNext(); T getNext();private: QString m_fileName; QFile m_file; QTextStream m_stream;};template <typename T>inlineFileReader<T>::FileReader(const QString &name):m_fileName(name){}template <typename T>FileReader<T>::~FileReader(){ if (m_file.isOpen()) { m_file.close(); }}template <typename T>bool FileReader<T>::open(){ m_file.setFileName(m_fileName); if (m_file.open(QIODevice::ReadWrite)) { m_stream.setDevice(&m_file); return true; } return false;}template <typename T>bool FileReader<T>::hasNext(){ if (m_file.isOpen()) { return !m_stream.atEnd(); } return false;}template <typename T>T FileReader<T>::getNext(){ T t; m_stream >> t; return t;}#endif // FILEREADER_HPP
構造參數是載入資料的檔案名稱,提供了open操作,hasNext是判斷是否結束,getNext得到一個載入的對象。
下面是我們的models,其中我使用了我的蹩腳的英文寫了注釋
/**************************************************author:周翔*e-mail:604487178@qq.com*blog:http://blog.csdn.net/zhx6044***************************************************/#ifndef DATATABLEMODEL_HPP#define DATATABLEMODEL_HPP#include <QAbstractTableModel>#include <vector>#include "student.hpp"#include "filereader.hpp"//Here,I want to use 'template class',but is not supported by the macor 'Q_OBJECT',//so I use typedef,of cause you also use macor//If you want to use it,the type of T use should implement the two static function//and overload operator[] and >>--int members(),it return T numbers of members,//QString describe(int ),the describe about T.operator[](int i),return the i-st member data.//the >> you knowtypedef Student T;class DataTableModel : public QAbstractTableModel{ Q_OBJECTpublic: explicit DataTableModel(const QString &fileName,QObject *parent = 0); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const;signals: void sig_error(const QString &info);public slots:private: QString m_fileName; //use vector,loading data form file may low speed,but seaching is fast! std::vector<T> m_data; void load();};#endif // DATATABLEMODEL_HPP/**************************************************author:周翔*e-mail:604487178@qq.com*blog:http://blog.csdn.net/zhx6044***************************************************/#include "datatablemodel.hpp"DataTableModel::DataTableModel(const QString &fileName, QObject *parent): QAbstractTableModel(parent), m_fileName(fileName){ load();}int DataTableModel::rowCount(const QModelIndex &/*parent*/) const{ return m_data.size();}int DataTableModel::columnCount(const QModelIndex &/*parent*/) const{ return T::members();}Qt::ItemFlags DataTableModel::flags(const QModelIndex &/*index*/) const{ return Qt::ItemIsEnabled;}QVariant DataTableModel::data(const QModelIndex &index, int role) const{ if (index.isValid() && role == Qt::DisplayRole) { return m_data[index.row()][index.column()]; } return QVariant();}QVariant DataTableModel::headerData(int section, Qt::Orientation orientation, int role) const{ if (role != Qt::DisplayRole) { return QVariant(); } if (orientation == Qt::Vertical) { return QString("row %1").arg(section); } else { return T::describe(section); } return QVariant();}/** * @brief DataTableModel::load load data from file */void DataTableModel::load(){ //create a file reader that can read object about type of T from file //which name is m_fileName,the type of T should overload the operator >> on QTextStream. //A bug about FileReader,the last read is empty because the file is end with empty line. //But I think it is not a problem. FileReader<T> fr(m_fileName); if (fr.open()) { while (fr.hasNext()) { m_data.push_back(fr.getNext()); } } else { emit sig_error("load data failure!"); }}
本來是想也做成模板的,可是Qt元對象系統和模板機制是存在衝突的,以後講為什麼衝突。這裡我使用了typedef來提前做了編譯器應該做的事避免了衝突
data,rowCounts,flag,headerData這些都是需要重新實現的,在二維中columnCount就需要重新實現了。
只是為了顯示所以flag很簡單。
大家也看見了我在Student中實現的members,describe的用處,和重載[]所帶來的便捷。
我們的列數就是需要對外實現的成員數,describe就是得到成員的描述。其實我感覺已經有點架構的味道了...算了不說大話了
其中的一個bug我已經用英文說了。
無圖無真相