#(43):QStringListModel
上一章我們已經了解到有關 list、table 和 tree 三個最常用的視圖類的便捷類的使用。前面也提到過,由于這些類僅僅是提供方便,功能、實現自然不如真正的 model/view 強大。從本章起,我們將了解最基本的 model/view 模型的使用。
既然是 model/view,我們也會分為兩部分:model 和 view。本章我們將介紹 Qt 內置的最簡單的一個模型:`QStringListModel`。接下來,我們再介紹另外的一些內置模型,在此基礎上,我們將了解到 Qt 模型的基本架構,以便為最高級的應用——自定義模型——打下堅實的基礎。
`QStringListModel`是最簡單的模型類,具備向視圖提供字符串數據的能力。`QStringListModel`是一個可編輯的模型,可以為組件提供一系列字符串作為數據。我們可以將其看作是封裝了`QStringList`的模型。`QStringList`是一種很常用的數據類型,實際上是一個字符串列表(也就是`QList<QString>`)。既然是列表,它也就是線性的數據結構,因此,`QStringListModel`很多時候都會作為`QListView`或者`QComboBox`這種只有一列的視圖組件的數據模型。
下面我們通過一個例子來看看`QStringListModel`的使用。首先是我們的構造函數:
~~~
MyListView::MyListView()
{
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`包裝了起來。剩下來的只是簡單的界面代碼,這里不再贅述。試運行一下,程序應該是這樣的:
[](http://files.devbean.net/images/2013/02/qstringlistmodel-demo.png)
接下來我們來看幾個按鈕的響應槽函數。
~~~
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()`已經獲取到當前行。在此,我們僅僅是為了介紹這些函數的使用。因此,除去這些冗余,我們可以使用一種更簡潔的寫法:
~~~
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 模型所帶來的一個便捷之處。
- (1)序
- (2)Qt 簡介
- (3)Hello, world!
- (4)信號槽
- (5)自定義信號槽
- (6)Qt 模塊簡介
- (7)MainWindow 簡介
- (8)添加動作
- (9)資源文件
- (10)對象模型
- (11)布局管理器
- (12)菜單欄、工具欄和狀態欄
- (13)對話框簡介
- (14)對話框數據傳遞
- (15)標準對話框 QMessageBox
- (16)深入 Qt5 信號槽新語法
- (17)文件對話框
- (18)事件
- (19)事件的接受與忽略
- (21)事件過濾器
- (22)事件總結
- (23)自定義事件
- (24)Qt 繪制系統簡介
- (25)畫刷和畫筆
- (26)反走樣
- (27)漸變
- (28)坐標系統
- (29)繪制設備
- (30)Graphics View Framework
- (31)貪吃蛇游戲(1)
- (32)貪吃蛇游戲(2)
- (33)貪吃蛇游戲(3)
- (34)貪吃蛇游戲(4)
- (35)文件
- (36)二進制文件讀寫
- (37)文本文件讀寫
- (38)存儲容器
- (39)遍歷容器
- (40)隱式數據共享
- (41)model/view 架構
- (42)QListWidget、QTreeWidget 和 QTableWidget
- (43)QStringListModel
- (44)QFileSystemModel
- (45)模型
- (46)視圖和委托
- (47)視圖選擇
- (48)QSortFilterProxyModel
- (49)自定義只讀模型
- (50)自定義可編輯模型
- (51)布爾表達式樹模型
- (52)使用拖放
- (53)自定義拖放數據
- (54)剪貼板
- (55)數據庫操作
- (56)使用模型操作數據庫
- (57)可視化顯示數據庫數據
- (58)編輯數據庫外鍵
- (59)使用流處理 XML
- (60)使用 DOM 處理 XML
- (61)使用 SAX 處理 XML
- (62)保存 XML
- (63)使用 QJson 處理 JSON
- (64)使用 QJsonDocument 處理 JSON
- (65)訪問網絡(1)
- (66)訪問網絡(2)
- (67)訪問網絡(3)
- (68)訪問網絡(4)
- (69)進程
- (70)進程間通信
- (71)線程簡介
- (72)線程和事件循環
- (73)Qt 線程相關類
- (74)線程和 QObject
- (75)線程總結
- (76)QML 和 QtQuick 2
- (77)QML 語法
- (78)QML 基本元素
- (79)QML 組件
- (80)定位器
- (81)元素布局
- (82)輸入元素
- (83)Qt Quick Controls
- (84)Repeater
- (85)動態視圖
- (86)視圖代理
- (87)模型-視圖高級技術
- (88)Canvas
- (89)Canvas(續)