Qt model/view理解 01

在Qt中对数据处理主要有两种方式:1)直接对包含数据的的数据项item进行操作,这种方法简单、易操作,现实方式单一的缺点,特别是对于大数据或在不同位置重复出现的数据必须依次对其进行操作,如果现实方式改变,则在改动程序过程中还需对数据进行重新编码操作,费工费资源。2)采用model/view模型,将数据--模型--视图三者串起来,通过约定的接口保证数据的正确显示和显示方式的多样性,当需要重新调整显示时,只需修改视图,保证接口不变,即可以新view显示数据。

1/2两种处理数据方式:

在此,我主要介绍model/view模式,在此以QAbstractTableModel/QTableView为例。

如果是只读模式,model只要重写以下三个方法:

//a方法:返回模型行数。
int rowCount(const QModelIndex &parent) const//b方法:返回模型列数。
int columnCount(const QModelIndex &parent) const//c方法:返回index项的role角色的数据。其中index项中可以有多个角色,每个角色都可以有一个数据。
QVariant data(const QModelIndex &index, int role) const;

如果用户要能够编辑数据(编辑模式),model还需要重写以下两个方法:

//d方法:设置模型中项的内容。
bool QAbstractItemModel::setData(const QModelIndex & index, 
                                 const QVariant & value, 
                                 int role= Qt::EditRole);

//e方法:返回项的编辑形式,默认情况下只有ItemIsSelectable和ItemIsEnabled,如果要可编辑,需要
//添加ItemIsEditable属性。
Qt::ItemFlags QAbstractTableModel::flags(const QModelIndex & index) const

其中a/b/c方法为纯虚函数(pure virtual method),继承的类必须由coder自己实现此方法。d/e方法为虚函数(virtual),coder在继承了此方法,实现自定义内容后可直接调用基类方法。

在此我们以《c++ gui programming with Qt4》中的trackEditor例子作一讲解。

class MyTableModel : public QAbstractTableModel
{
public:
    explicit MyTableModel(QList<Track>* tracks, QWidget *parent = 0);

    virtual int rowCount(const QModelIndex &parent) const;
    virtual int columnCount(const QModelIndex &parent) const;
    virtual QVariant data(const QModelIndex &index, int role) const;
    virtual QVariant headerData(int section,                                 Qt::Orientation orientation,                                 int role) const;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role);

private:
    QList<Track>* pTracks;
};

其中“QList<Track>* tracks”是用来保存model所显示的数据集合,在model初始化时被赋值,根据data方法的算法调用其中数据显示。

MyTableModel::MyTableModel(QList<Track>* tracks, QWidget *parent)
{
    Q_UNUSED(parent);

    pTracks = tracks;
}

构造函数对tracks进行赋值。

int MyTableModel::rowCount(const QModelIndex &parentconst
{
    Q_UNUSED(parent);

    return pTracks->count();
}

int MyTableModel::columnCount(const QModelIndex &parentconst
{
    Q_UNUSED(parent);

    return 2;
}

以上2个方法返回model的行/列数,因为tracks中的数据量不确定,所以直接返回其count方法,保证每次都是最新值;

QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if ( !index.isValid()) {
        return QVariant();
    }

    if (Qt::DisplayRole == role || Qt::EditRole == role) {
        if (0 == index.column()) {
            return pTracks->at(index.row()).getTitle();
        } else if (1 == index.column()) {
            return pTracks->at(index.row()).getDuration();
        }
    }

    return QVariant();
}

根据数据类型和所在列,返回不同的数据信息,当index都不属于以上两种情况时,返回QVariant对象。EditRole保证了当用户编辑数据时,数据显示的是被选中模式而不是直接消失。

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if ( !index.isValid()) {
        return false;
    }

    if (Qt::EditRole == role) {
        (*pTracks)[index.row()].setTitle(value.toString());
        emit dataChanged(index, index);

        return true;
    } else {
        return QAbstractTableModel::setData(index, value, role);
    }
}

根据coder处理和传入角色,设置index处项的值及tracks中对应处的数据并更新index的数据显示。当传入数据coder不处理时,则直接调用基类方法处理,再辞没有特别指明第几列进行编辑,因为是后面通过flags方法来设定了可编辑的列,故此处不用再特别指明。在此请注意,setData方法是判断的是Qt::EditRole角色,data方法是Qt::DisplayRole,但是保存、读取的都是tracks,这正是coder要注意的,在不同的过程时,程序所处于的角色不同,但是都操作同样的数据库(tracks),这点要注意。

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
    if (0 == index.column()) {
        return (QAbstractTableModel::flags(index) | Qt::ItemIsEditable);
    } else {
        return QAbstractTableModel::flags(index);
    }
}

model中每个项的处理标志位默认为(ItemIsEnabled | ItemIsSelectable),coder可根据要求对不同属性的项进行设置。在此设定第0列可编辑,其余列不可编辑。

程序运行结果,分别用TableView和ListView显示相同的数据:

具体代码如下:共有maindialg,mytablemodel,tack三个类,一个main运行类。具体界面文件是由Qt自己生成的ui。

track.h

#ifndef TRACK_H
#define TRACK_H

#include <QString>

class Track
{
public:
    explicit Track(const QString& title = ""int duration = 0);

    QString getTitle() const;
    int getDuration() const;

    void setDuration(int duration);
    void setTitle(QString title);

private:
    QString mTitle;
    int mDuration;
};

#endif // TRACK_H

track.cpp

#include "track.h"

Track::Track(const QString &title, int duration) :
    mTitle(title),
    mDuration(duration)
{

}

QString Track::getTitle() const
{
    return mTitle;
}

int Track::getDuration() const
{
    return mDuration;
}

void Track::setDuration(int duration)
{
    mDuration = duration;
}

void Track::setTitle(QString title)
{
    mTitle = title;
}

mytablemodel.h

#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H

#include <QWidget>
#include <QAbstractTableModel>
#include "track.h"

class MyTableModel : public QAbstractTableModel
{
public:
    explicit MyTableModel(QList<Track>* tracks, QWidget *parent = 0);

    virtual int rowCount(const QModelIndex &parent) const;
    virtual int columnCount(const QModelIndex &parent) const;
    virtual QVariant data(const QModelIndex &index, int role) const;
    virtual QVariant headerData(int section,                                 Qt::Orientation orientation,                                 int role) const;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role);

private:
    QList<Track>* pTracks;
};

#endif // MYTABLEMODEL_H

mytablemodel.cpp

#include "mytablemodel.h"

MyTableModel::MyTableModel(QList<Track>* tracks, QWidget *parent)
{
    Q_UNUSED(parent);

    pTracks = tracks;
}

int MyTableModel::rowCount(const QModelIndex &parentconst
{
    Q_UNUSED(parent);

    return pTracks->count();
}

int MyTableModel::columnCount(const QModelIndex &parentconst
{
    Q_UNUSED(parent);

    return 2;
}

QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if ( !index.isValid()) {
        return QVariant();
    }

    if (Qt::DisplayRole == role) {
        if (0 == index.column()) {
            return pTracks->at(index.row()).getTitle();
        } else if (1 == index.column()) {
            return pTracks->at(index.row()).getDuration();
        }
    }

    return QVariant();
}

QVariant MyTableModel::headerData(int section,
                                  Qt::Orientation orientation,
                                  int role) const
{
    /*if (Qt::Vertical == orientation) {
        return QVariant();
    }*/

    if (Qt::DisplayRole == role && Qt::Horizontal == orientation) {
        switch (section) {
        case 0:
            return "first";

        case 1:
            return "second";
        }
    }

    return QVariant();
}

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
    if (0 == index.column()) {
        return (QAbstractTableModel::flags(index) | Qt::ItemIsEditable);
    } else {
        return QAbstractTableModel::flags(index);
    }
}

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if ( !index.isValid()) {
        return false;
    }

    if (Qt::EditRole == role) {
        (*pTracks)[index.row()].setTitle(value.toString());
        emit dataChanged(index, index);

        return true;
    } else {
        return QAbstractTableModel::setData(index, value, role);
    }
}

maindialog.h

#ifndef MAINDIALOG_H
#define MAINDIALOG_H

#include <QDialog>
#include <QTableView>
#include <QListView>
#include "mytablemodel.h"

namespace Ui {
class MainDialog;
}

class MainDialog : public QDialog
{
    Q_OBJECT

public:
    explicit MainDialog(QWidget *parent = 0);
    ~MainDialog();

    void setTableModel(MyTableModel* model);

    void setListModel(MyTableModel* model);

private:
    Ui::MainDialog *ui;

    QTableView* pTableView;
    QListView* pListView;
};

#endif // MAINDIALOG_H

maindialog.cpp

#include<QGridLayout>
#include "maindialog.h"
#include "ui_maindialog.h"

MainDialog::MainDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MainDialog),
    pTableView(new QTableView(this)),
    pListView(new QListView(this))
{
    ui->setupUi(this);

    QVBoxLayout* layout(new QVBoxLayout(this));
    layout->addWidget(pTableView);
    layout->addWidget(pListView);
    setLayout(layout);

    setAttribute(Qt::WA_DeleteOnClose);
}

MainDialog::~MainDialog()
{
    delete ui;
}

void MainDialog::setTableModel(MyTableModel *model)
{
    pTableView->setModel(model);
}

void MainDialog::setListModel(MyTableModel* model)
{
    pListView->setModel(model);
}

main.cpp

#include <QApplication>
#include "maindialog.h"
#include "track.h"
#include "mytablemodel.h"

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

    QList<Track> tracks;
        tracks << Track("The Flying Dutchman: Overture"630)
               << Track("The Flying Dutchman: Wie aus der Fern laengst "
                        "vergangner Zeiten"374)
               << Track("The Flying Dutchman: Steuermann, lass die Wacht",
                        152)
               << Track("Die Walkuere: Ride of the Valkyries"286)
               << Track("Tannhaeuser: Freudig begruessen wir die edle "
                        "Halle"384)
               << Track("Tannhaeuser: Wie Todesahnung - O du mein holder "
                        "Abendstern"257)
               << Track("Lohengrin: Treulich gefuert ziehet dahnin"294)
               << Track("Lohengrin: In fernem Land"383)
               << Track("Die Meistersinger von Nuernberg: Overture"543)
               << Track("Die Meistersinger von Nuernberg: Verachtet mir "
                        "die Meister nicht"200)
               << Track("Die Meistersinger von Nuernberg: Ehrt eure "
                        "deutschen Meister"112)
               << Track("Goetterdaemmerung: Funeral Music"469)
               << Track("Tristan und Isolde: Mild und leise, wie er "
                        "laechelt"375);

    MyTableModel model(&tracks);

    MainDialog* w(new MainDialog(0));
    w->setTableModel(&model);
    w->setListModel(&model);
    w->show();

    return a.exec();
}

完整代码:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值