上一章咱們已經瞭解到有關 list、table 和 tree 三個最經常使用的視圖類的便捷類的使用。前面也提到過,因爲這些類僅僅是提供方便,功能、實現天然不如真正的 model/view 強大。從本章起,咱們將瞭解最基本的 model/view 模型的使用。數據結構
既然是 model/view,咱們也會分爲兩部分:model 和 view。本章咱們將介紹 Qt 內置的最簡單的一個模型:QStringListModel
。接下來,咱們再介紹另外的一些內置模型,在此基礎上,咱們將瞭解到 Qt 模型的基本架構,以便爲最高級的應用——自定義模型——打下堅實的基礎。架構
QStringListModel
是最簡單的模型類,具有向視圖提供字符串數據的能力。QStringListModel
是一個可編輯的模型,能夠爲組件提供一系列字符串做爲數據。咱們能夠將其看做是封裝了QStringList
的模型。QStringList
是一種很經常使用的數據類型,其實是一個字符串列表(也就是QList<QString>
)。既然是列表,它也就是線性的數據結構,所以,QStringListModel
不少時候都會做爲QListView
或者QComboBox
這種只有一列的視圖組件的數據模型。函數
下面咱們經過一個例子來看看QStringListModel
的使用。首先是咱們的構造函數:this
//MyListView.h #ifndef MYLISTVIEW_H #define MYLISTVIEW_H #include <QWidget> #include <QStringListModel> #include <QListView> class MyListView : public QWidget { Q_OBJECT public: MyListView(QWidget *parent = 0); ~MyListView(); private: QStringListModel *model; QListView *listView; private slots: void insertData(); void deleteData(); void showData(); }; #endif // MYLISTVIEW_H //MyListView.cpp #include "MyListView.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QPushButton> #include <QInputDialog> #include <QLineEdit> #include <QMessageBox> MyListView::MyListView(QWidget *parent) : QWidget(parent) { QStringList data; data << "Letter A" << "Letter B" << "Letter C"; model = new QStringListModel(this); model->setStringList(data); listView = new QListView(this); listView->setModel(model); QHBoxLayout *btnLayout = new QHBoxLayout; QPushButton *insertBtn = new QPushButton(tr("insert"), this); connect(insertBtn, SIGNAL(clicked()), this, SLOT(insertData())); QPushButton *delBtn = new QPushButton(tr("Delete"), this); connect(delBtn, SIGNAL(clicked()), this, SLOT(deleteData())); QPushButton *showBtn = new QPushButton(tr("Show"), this); connect(showBtn, SIGNAL(clicked()), this, SLOT(showData())); btnLayout->addWidget(insertBtn); btnLayout->addWidget(delBtn); btnLayout->addWidget(showBtn); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(listView); mainLayout->addLayout(btnLayout); setLayout(mainLayout); }
首先,咱們建立了一個QStringList
對象,向其中插入了幾個數據;而後將其做爲QStringListModel
的底層數據。這樣,咱們能夠理解爲,QStringListModel
將QStringList
包裝了起來。剩下來的只是簡單的界面代碼,這裏再也不贅述。試運行一下,程序應該是這樣的:code
接下來咱們來看幾個按鈕的響應槽函數。orm
void MyListView::insertData() { bool isOK; QString text = QInputDialog::getText(this, "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); } }
首先是insertData()
函數。咱們使用QInputDialog::getText()
函數要求用戶輸入數據。這是 Qt 的標準對話框,用於獲取用戶輸入的字符串。這部分在前面的章節中已經講解過。當用戶點擊了 OK 按鈕,咱們使用listView->currentIndex()
函數,獲取QListView
當前行。這個函數的返回值是一個QModelIndex
類型。咱們會在後面的章節詳細講解這個類,如今只要知道這個類保存了三個重要的數據:行索引、列索引以及該數據屬於哪個模型。咱們調用其row()
函數得到行索引,該返回值是一個 int,也就是當前是第幾行。而後咱們向模型插入新的一行。insertRows()
函數簽名以下:對象
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
該函數會將 count 行插入到模型給定的 row 的位置,新行的數據將會做爲 parent 的子元素。若是 row 爲 0,新行將被插入到 parent 的全部數據以前,不然將在指定位置的數據以前。若是 parent 沒有子元素,則會新插入一個單列數據。函數插入成功返回 true,不然返回 false。咱們在這段代碼中調用的是insertRows(row, 1)
。這是QStringListModel
的一個重載。參數 1 說明要插入 1 條數據。記得以前咱們已經把 row 設置爲當前行,所以,這行語句其實是在當前的 row 位置插入 count 行,這裏的 count 爲 1。因爲咱們沒有添加任何數據,實際效果是,咱們在 row 位置插入了 1 個空行。而後咱們使用 model 的index()
函數獲取當前行的QModelIndex
對象,利用setData()
函數把咱們用QInputDialog
接受的數據設置爲當前行數據。接下來,咱們使用setCurrentIndex()
函數,把當前行設爲新插入的一行,並調用edit()
函數,使這一行能夠被編輯。索引
以上是咱們提供的一種插入數據的方法:首先插入空行,而後選中新插入的空行,設置新的數據。這實際上是一種冗餘操做,由於currentIndex()
已經獲取到當前行。在此,咱們僅僅是爲了介紹這些函數的使用。所以,除去這些冗餘,咱們可使用一種更簡潔的寫法:rem
void MyListView::insertData() { bool isOK; QString text = QInputDialog::getText(this, "Insert", "Please input new data:", QLineEdit::Normal, "You are inserting new data.", &isOK); if (isOK) { QModelIndex currIndex = listView->currentIndex(); model->insertRows(currIndex.row(), 1); model->setData(currIndex, text); listView->edit(currIndex); } }
接下來是刪除數據:字符串
void MyListView::deleteData() { if (model->rowCount() > 1) { model->removeRows(listView->currentIndex().row(), 1); } }
使用模型的removeRows()
函數能夠輕鬆完成這個操做。這個函數同前面所說的insertRows()
很相似,這裏再也不贅述。須要注意的是,咱們用rowCount()
函數判斷了一下,要求最終始終保留 1 行。這是由於咱們寫的簡單地插入操做所限制,若是把數據所有刪除,就不能再插入數據了。因此,前面所說的插入操做實際上還須要再詳細考慮才能夠解決這一問題。
最後是簡單地將全部數據都顯示出來:
void MyListView::showData() { QStringList data = model->stringList(); QString str; foreach(QString s, data) { str += s + "\n"; } QMessageBox::information(this, "Data", str); }
這段代碼沒什麼好說的。
關於QStringListModel
咱們簡單介紹這些。從這些示例中能夠看到,幾乎全部操做都是針對模型的,也就是說,咱們直接對數據進行操做,當模型檢測到數據發生了變化,會馬上通知視圖進行刷新。這樣,咱們就能夠把精力集中到對數據的操做上,而不用擔憂視圖的同步顯示問題。這正是 model/view 模型所帶來的一個便捷之處。