今天開始我們要看看 Qt 的 model-view 類了。正如前面說的那樣,之前三節(jié)的 item class 類只是Qt 為了方便我們使用而封裝了的一些操作。比起真正的 model-view 類來,那些類更易于使用,但是功能也會更簡單,并且缺少實時性的支持,比如我們并不方便實現(xiàn)插入、刪除等一些常見操作。而現(xiàn)在我們要說的 model-view 類使用起來可能會復雜一些,但是功能強大,并且在 model 更新時會自動更新view,而 model 多是一些數(shù)據(jù)集合,因此比較便于操作。
model-view 類中,view 大致有三種:list、tree 和 table,但是 model 千奇百怪,不同的業(yè)務,甚至同樣的業(yè)務不同的建模都會有不同的 model。為了方便使用,Qt 提供了一些預定義好的 model 供我們使用。QStringListModel 是其中最簡單的一種。
顧名思義,QStringListModel 就是封裝了 QStringList 的 model。QStringList 是一種很常用的數(shù)據(jù)類型,它實際上是一個字符串列表。我們可以想象,對于一個 list 來說,如果提供一個字符串列表形式的數(shù)據(jù),就應該能夠把這個數(shù)據(jù)展示出來。因為二者是一致的:QStringList 是線性的,而list 也是線性的。所以,QStringListModel 很多時候都會作為 QListView 的 model。
下面我們來看怎么使用它們。比起前面的 QListWidget,這里要使用兩個類:QStringListModel 和QListView,并且還有一些輔助類。不過你可以看到,即便這樣復雜的工作,我們的代碼也不會很多的:
mylistview.h
#ifndef MYLISTVIEW_H
#define MYLISTVIEW_H
#include <QtGui>
class MyListView : public QWidget
{
Q_OBJECT
public:
MyListView();
private:
QStringListModel *model;
QListView *listView;
private slots:
void insertData();
void deleteData();
void showData();
};
#endif // MYLISTVIEW_H
mylistview.cpp
#include "mylistview.h"
MyListView::MyListView()
{
model = new QStringListModel(this);
QStringList data;
data << "Letter A" << "Letter B" << "Letter C";
model->setStringList(data);
listView = new QListView(this);
listView->setModel(model);
QHBoxLayout *btnLayout = new QHBoxLayout;
QPushButton *insertBtn = new QPushButton(tr("insert"), this);
QPushButton *delBtn = new QPushButton(tr("Delete"), this);
QPushButton *showBtn = new QPushButton(tr("Show"), this);
btnLayout->addWidget(insertBtn);
btnLayout->addWidget(delBtn);
btnLayout->addWidget(showBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(listView);
mainLayout->addLayout(btnLayout);
this->setLayout(mainLayout);
connect(insertBtn, SIGNAL(clicked()), this, SLOT(insertData()));
connect(delBtn, SIGNAL(clicked()), this, SLOT(deleteData()));
connect(showBtn, SIGNAL(clicked()), this, SLOT(showData()));
}
void MyListView::insertData()
{
bool isOK;
QString text = QInputDialog::getText(NULL, "Insert", "Please input new data:",
QLineEdit::Normal, "You are inserting new data.", &isOK);
if(isOK) {
int row = listView->currentIndex().row();
model->insertRows(row, 1);
QModelIndex index = model->index(row);
model->setData(index, text);
listView->setCurrentIndex(index);
listView->edit(index);
}
}
void MyListView::deleteData()
{
if(model->rowCount() > 1) {
model->removeRows(listView->currentIndex().row(), 1);
}
}
void MyListView::showData()
{
QStringList data = model->stringList();
QString str;
foreach(QString s, data) {
str += s + "\n";
}
QMessageBox::information(this, "Data", str);
}
來看看我們的代碼吧。
首先我們創(chuàng)建一個 QStringListModel 的對象。然后創(chuàng)建一個 QStringList 對象,并且把這個對象設置為 model 的數(shù)據(jù)。此時,這個 model 已經(jīng)擁有數(shù)據(jù)了。然后,我們創(chuàng)建一個 QListView 的對象,并把 model 設置為它的 model。后面是三個按鈕的創(chuàng)建以及信號槽的連接,這里就不再贅述。
先來運行一下看看結(jié)果吧!
我們只是把 QStringListModel 設置為 QListView 的 model,QListView 就已經(jīng)可以把 model 里面的數(shù)據(jù)展示出來了。下面我們看看增、刪、改的操作。
先來看增加數(shù)據(jù)的操作。這部分是在代碼中的 insertData()函數(shù)實現(xiàn)的。先把那個函數(shù)拿出來看看:
void MyListView::insertData()
{
bool isOK;
QString text = QInputDialog::getText(NULL, "Insert", "Please input new data:",
QLineEdit::Normal, "You are inserting new data.", &isOK);
if(isOK) {
int row = listView->currentIndex().row();
model->insertRows(row, 1);
QModelIndex index = model->index(row);
model->setData(index, text);
listView->setCurrentIndex(index);
listView->edit(index);
}
}
我們使用 QInputDialog::getText()函數(shù)要求用戶輸入數(shù)據(jù)。這部分在前面講過,這里也不再贅述。如果用戶點擊了 OK 按鈕,首先,我們使用 listView()->currentIndex()函數(shù),獲取 QListView當前行。注意,這個函數(shù)的返回值是一個 QModelIndex 類型。這個類我們以后再說,只要知道這個類保存了三個重要的數(shù)據(jù):行、列以及屬于哪一個 model。我們調(diào)用其 row()函數(shù)獲得行,這個返回值是一個 int,也就是第幾行。然后 model 插入一行。insertRows()函數(shù)簽名如下:
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
這個函數(shù)原本是 QAbstractListModel 類的函數(shù),而 QStringListModel 把它覆蓋了。所以我們會發(fā)現(xiàn)它還需要另外的一個參數(shù)。我們調(diào)用 insertRows(row, 1); ,所謂1就是指插入1條數(shù)據(jù),而前面又把 row 保存成當前行,因此,這行語句實際上是在當前的 row 行插入 count 行,這里的 count = 1。然后我們使用 model 的 index()函數(shù)獲取當前行的 QModelIndex 對象,使用 setData()函數(shù)把我們用 QInputDialog 接受的數(shù)據(jù)插入。這里其實是一個冗余的操作,因為用 currentIndex()函數(shù)已經(jīng)獲取當前行了。這么寫僅僅是為了展示如何使用這個函數(shù)。不過,你知道了 insertRow()函數(shù),就可以很容易的做出插入空白行的效果了。然后我們把當前行設為新插入的一行,并調(diào)用 edit()函數(shù),這個函數(shù)使得這一行可以被編輯。就這樣,我們向 model 插入了數(shù)據(jù)。
然后來看刪除數(shù)據(jù)的操作:
void MyListView::deleteData()
{
if(model->rowCount() > 1) {
model->removeRows(listView->currentIndex().row(), 1);
}
}
使用 model 的 removeRows()函數(shù)可以輕松的完成這個功能。這個函數(shù)同前面所說的 insertRows()很類似,就不再多說了。需要注意的是,我們用 rowCount()函數(shù)判斷了一下,要求最終始終保留1行。這是因為如果你把數(shù)據(jù)全部刪除,你就不能再插入數(shù)據(jù)了,因為那時侯按照我們所寫的插入邏輯就不對了。所以,前面所說的插入操作實際上還需要再詳細考慮。
最后那個 showData()僅僅為了查看 model 的數(shù)據(jù),沒有什么要說的東西。你可以在 insert 或者remove 完成后查看一下 model 里面的數(shù)據(jù)是不是真的被修改了。
關于 QStringListModel 就說這么多。你可以看到,我們的幾乎所有操作都是針對 model 的,也就是說,我們直接針對的是數(shù)據(jù),而 model 偵測到數(shù)據(jù)發(fā)生了變化,會立刻通知 view 刷新。這樣,我們就可以把精力集中到對數(shù)據(jù)的操作上,而不用擔心 view 的同步等操作。這也是 model-view 模型的一個便捷之處。
本文出自 “豆子空間” 博客,請務必保留此出處 http://devbean.blog.51cto.com/448512/193918
更多建議: