Qt 是一個(gè)跨平臺(tái)C++圖形界面開(kāi)發(fā)庫(kù),利用Qt可以快速開(kāi)發(fā)跨平臺(tái)窗體應(yīng)用程序,在Qt中我們可以通過(guò)拖拽的方式將不同組件放到指定的位置,實(shí)現(xiàn)圖形化開(kāi)發(fā)極大的方便了開(kāi)發(fā)效率,本章將重點(diǎn)介紹TableView
組件與數(shù)據(jù)庫(kù)聯(lián)動(dòng)的常用方法及靈活運(yùn)用。
在Qt中,通常我們不會(huì)在TableView
等組件中保存數(shù)據(jù),一般會(huì)將這些數(shù)據(jù)存儲(chǔ)至數(shù)據(jù)庫(kù)或者是文件中保存,當(dāng)使用時(shí)則動(dòng)態(tài)的在數(shù)據(jù)庫(kù)中調(diào)出來(lái),以下案例將實(shí)現(xiàn),當(dāng)用戶(hù)點(diǎn)擊并選中TableView
組件內(nèi)的某一行時(shí),我們通過(guò)該行中的name
字段查詢(xún),并將查詢(xún)結(jié)果關(guān)聯(lián)到ListView
組件內(nèi),同時(shí)將TableView
中選中行的字段分別顯示在窗體底部的LineEdit
編輯框內(nèi)。
要實(shí)現(xiàn)聯(lián)動(dòng)涉及幾個(gè)主要步驟:建立數(shù)據(jù)庫(kù)連接、創(chuàng)建模型、設(shè)置TableView
、捕捉TableView
的選中信號(hào)、查詢(xún)并關(guān)聯(lián)數(shù)據(jù)、更新LineEdit
和ListView
,首先我們?cè)?code>UI界面中繪制所需控件,如下圖左側(cè)放一個(gè)TableView
組件,右側(cè)是一個(gè)ListView
組件,底部放三個(gè)LineEdit
組件;
接著我們需要?jiǎng)?chuàng)建兩張數(shù)據(jù)表,其中Student
表主要用來(lái)存儲(chǔ)學(xué)生信息,而StudentAddressList
用于存儲(chǔ)學(xué)生所管理的IP地址,我們將表中的name
進(jìn)行關(guān)聯(lián),每個(gè)學(xué)生名下存儲(chǔ)有不同的地址;
創(chuàng)建兩個(gè)表結(jié)構(gòu)總結(jié)起來(lái)代碼如下所示,通過(guò)分別調(diào)用多次db.exec()
函數(shù)實(shí)現(xiàn)創(chuàng)建數(shù)據(jù)表,并通過(guò)QSqlQuery
類(lèi)實(shí)現(xiàn)批量插入數(shù)據(jù)集;
void InitMultipleSQL()
{
// ----------------------------------------------
// 初始化表結(jié)構(gòu)
// ----------------------------------------------
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./database.db");
if (!db.open())
{
std::cout << db.lastError().text().toStdString()<< std::endl;
return;
}
// 執(zhí)行SQL創(chuàng)建表
db.exec("DROP TABLE Student");
db.exec("CREATE TABLE Student ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(40) NOT NULL, "
"age INTEGER NOT NULL)"
);
// 批量創(chuàng)建數(shù)據(jù)
QStringList name_list; name_list << "lyshark" << "admin" << "guest";
QStringList age_list; age_list << "25" << "34" << "45";
// 綁定并插入數(shù)據(jù)
QSqlQuery query;
query.prepare("INSERT INTO Student(name,age) ""VALUES (:name, :age)");
if(name_list.size() == age_list.size())
{
for(int x=0;x< name_list.size();x++)
{
query.bindValue(":name",name_list[x]);
query.bindValue(":age",age_list[x]);
query.exec();
}
}
// ----------------------------------------------
// 創(chuàng)建第二張表
// ----------------------------------------------
// 與第一張表通過(guò)姓名關(guān)聯(lián)起來(lái)
db.exec("DROP TABLE StudentAddressList");
db.exec("CREATE TABLE StudentAddressList("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(40) NOT NULL, "
"address VARCHAR(128) NOT NULL"
")");
// 插入數(shù)據(jù)
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.1')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.2')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('lyshark','192.168.1.3')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('admin','192.168.10.10')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('admin','192.168.10.11')");
db.exec("INSERT INTO StudentAddressList(name,address) VALUES ('guest','192.168.100.100')");
}
1.1 初始化組件
接著我們需要在構(gòu)造函數(shù)MainWindow::MainWindow(QWidget *parent)
內(nèi)初始化``TableView表格,查詢(xún)
Student表內(nèi)記錄,將查詢(xún)到的指針綁定到
theSelection模型上,綁定后再將綁定指針加入到
dataMapper組件映射中,即可實(shí)現(xiàn)初始化,這里有必要介紹一下
QSqlQueryModel、
QItemSelectionModel、
QDataWidgetMapper`這三個(gè)模型類(lèi)。
QSqlQueryModel
用于與數(shù)據(jù)庫(kù)交互的模型類(lèi)之一,它繼承自 QAbstractTableModel
。QSqlQueryModel
通過(guò)執(zhí)行 SQL 查詢(xún)語(yǔ)句,將查詢(xún)結(jié)果作為表格數(shù)據(jù)提供給 Qt 的視圖組件,如 QTableView
等。
以下是 QSqlQueryModel
的一些常用方法,概述成表格形式:
方法 | 描述 |
---|---|
setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase()) |
設(shè)置要執(zhí)行的 SQL 查詢(xún)和數(shù)據(jù)庫(kù)連接。查詢(xún)執(zhí)行后,結(jié)果將被提供給模型。 |
clear() |
清除模型中的數(shù)據(jù)。 |
lastError() const |
返回最后一次執(zhí)行的查詢(xún)的錯(cuò)誤。 |
record() const |
返回包含查詢(xún)結(jié)果字段信息的 QSqlRecord 對(duì)象。 |
data(const QModelIndex &item, int role = Qt::DisplayRole) const |
返回與給定索引處的項(xiàng)相關(guān)聯(lián)的數(shù)據(jù),用于提供給視圖請(qǐng)求的數(shù)據(jù)。 |
rowCount(const QModelIndex &parent = QModelIndex()) const |
返回模型中的行數(shù)。 |
columnCount(const QModelIndex &parent = QModelIndex()) const |
返回模型中的列數(shù)。 |
上述方法提供了一般性的查詢(xún)執(zhí)行、錯(cuò)誤處理、結(jié)果處理等功能,使得通過(guò) QSqlQueryModel
能夠方便地將數(shù)據(jù)庫(kù)中的查詢(xún)結(jié)果集與 Qt 的視圖組件進(jìn)行關(guān)聯(lián)。使用這些方法,你可以在應(yīng)用中執(zhí)行 SQL 查詢(xún),并將結(jié)果顯示在相應(yīng)的視圖組件中。
QItemSelectionModel
用于管理項(xiàng)選擇的模型類(lèi),它是 QAbstractItemModel
類(lèi)的衍生類(lèi)。QItemSelectionModel
用于追蹤一個(gè)或多個(gè)視圖中的選擇項(xiàng),同時(shí)允許對(duì)這些選擇項(xiàng)進(jìn)行查詢(xún)和修改。
以下是 QItemSelectionModel
的一些常用方法,概述成表格形式:
方法 | 描述 |
---|---|
QItemSelectionModel(QAbstractItemModel *model) |
構(gòu)造函數(shù),創(chuàng)建一個(gè)選擇模型并關(guān)聯(lián)指定的數(shù)據(jù)模型。 |
setModel(QAbstractItemModel *model) |
設(shè)置關(guān)聯(lián)的數(shù)據(jù)模型。 |
model() const |
返回與此選擇模型相關(guān)聯(lián)的數(shù)據(jù)模型。 |
currentIndex() const |
返回當(dāng)前焦點(diǎn)的項(xiàng)的索引。 |
selectedIndexes() const |
返回當(dāng)前選擇的項(xiàng)的索引列表。 |
clear() |
清除模型中的所有選擇項(xiàng)。 |
select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) |
根據(jù)給定的 QModelIndex 對(duì)象和選擇標(biāo)志執(zhí)行選擇操作。 |
select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) |
根據(jù)給定的 QItemSelection 對(duì)象和選擇標(biāo)志執(zhí)行選擇操作。 |
reset() |
重置選擇模型,清除所有選擇和當(dāng)前索引。 |
setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) |
設(shè)置當(dāng)前焦點(diǎn)項(xiàng)。 |
currentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous) |
當(dāng)前焦點(diǎn)項(xiàng)變化時(shí)發(fā)出的信號(hào)。 |
selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) |
選擇發(fā)生變化時(shí)發(fā)出的信號(hào)。 |
這些方法允許你在一個(gè)或多個(gè)視圖中管理選擇項(xiàng),進(jìn)行選擇的查詢(xún)、修改,以及處理選擇變化的信號(hào)。通過(guò)使用這些方法,你可以實(shí)現(xiàn)對(duì)模型中的項(xiàng)進(jìn)行靈活的選擇操作,并及時(shí)響應(yīng)選擇的變化。
QDataWidgetMapper
用于實(shí)現(xiàn)數(shù)據(jù)和小部件之間雙向映射的類(lèi),使得數(shù)據(jù)模型的變化能夠反映在界面上,同時(shí)用戶(hù)界面的修改也能夠同步到數(shù)據(jù)模型中。
以下是 QDataWidgetMapper
的一些主要方法,概述成表格形式:
方法 | 描述 |
---|---|
QDataWidgetMapper(QObject *parent = nullptr) |
構(gòu)造函數(shù),創(chuàng)建一個(gè)數(shù)據(jù)映射器對(duì)象。 |
setModel(QAbstractItemModel *model) |
設(shè)置映射的數(shù)據(jù)模型。 |
model() const |
返回與此數(shù)據(jù)映射器相關(guān)聯(lián)的數(shù)據(jù)模型。 |
addMapping(QWidget *widget, int section) |
將指定的小部件和數(shù)據(jù)模型的字段進(jìn)行映射。 |
removeMapping(QWidget *widget) |
移除與指定小部件的映射關(guān)系。 |
mappedWidgetAt(int section) const |
返回與數(shù)據(jù)模型的指定字段映射的小部件。 |
mappedSection(QWidget *widget) const |
返回與指定小部件映射的數(shù)據(jù)模型字段的索引。 |
setCurrentIndex(int index) |
將映射的數(shù)據(jù)移動(dòng)到指定的索引。 |
currentIndex() const |
返回當(dāng)前映射的數(shù)據(jù)的索引。 |
toFirst(), toLast(), toNext(), toPrevious() |
分別將映射的數(shù)據(jù)移動(dòng)到第一行、最后一行、下一行、上一行。 |
submit() |
將界面上的更改提交到模型。 |
revert() |
撤銷(xiāo)所有未提交的更改。 |
setSubmitPolicy(QDataWidgetMapper::SubmitPolicy policy) |
設(shè)置更改提交策略。 |
submitPolicy() const |
返回當(dāng)前的更改提交策略。 |
addMapping(QWidget *widget, int section, const QByteArray &propertyName) |
將小部件和數(shù)據(jù)模型字段以及小部件屬性進(jìn)行映射。 |
toFirst(), toLast(), toNext(), toPrevious() |
分別將映射的數(shù)據(jù)移動(dòng)到第一行、最后一行、下一行、上一行。 |
這些方法使得在 Qt 應(yīng)用程序中更容易實(shí)現(xiàn)數(shù)據(jù)模型和用戶(hù)界面的交互,通過(guò)將數(shù)據(jù)模型字段映射到用戶(hù)界面的小部件上,實(shí)現(xiàn)了數(shù)據(jù)的顯示和編輯的同步。通過(guò)調(diào)用 setCurrentIndex
、toNext
、toPrevious
等方法,你可以在數(shù)據(jù)模型中移動(dòng),并自動(dòng)更新映射的小部件上顯示的數(shù)據(jù)。最后,通過(guò)調(diào)用 submit
將界面上的更改提交到模型,而 revert
則撤銷(xiāo)未提交的更改。
初始化UI界面很容易實(shí)現(xiàn),首席按初始化表結(jié)構(gòu),通過(guò)調(diào)用封裝好的InitMultipleSQL
可以直接初始化并將數(shù)據(jù)保存至database.db
文件中,在主程序中我們一次執(zhí)行如下操作來(lái)實(shí)現(xiàn)數(shù)據(jù)的初始化與展現(xiàn)。
打開(kāi)數(shù)據(jù)庫(kù)
使用 SQLite 數(shù)據(jù)庫(kù),并嘗試打開(kāi)名為 "database.db" 的數(shù)據(jù)庫(kù)文件。如果打開(kāi)失敗,將輸出錯(cuò)誤信息并返回。需要注意確保數(shù)據(jù)庫(kù)文件存在且可訪(fǎng)問(wèn)。
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("./database.db");
if (!db.open())
{
std::cout << db.lastError().text().toStdString()<< std::endl;
return;
}
查詢(xún)數(shù)據(jù)表中記錄
通過(guò) QSqlQueryModel
查詢(xún) Student 表中的所有記錄,并按 id 排序。如果查詢(xún)過(guò)程中出現(xiàn)錯(cuò)誤,需要處理錯(cuò)誤。
qryModel=new QSqlQueryModel(this);
qryModel->setQuery("SELECT * FROM Student ORDER BY id");
設(shè)置 TableView 表頭數(shù)據(jù)
設(shè)置查詢(xún)模型的表頭數(shù)據(jù),分別為 "ID"、"Name"、"Age"。
qryModel->setHeaderData(0,Qt::Horizontal,"ID");
qryModel->setHeaderData(1,Qt::Horizontal,"Name");
qryModel->setHeaderData(2,Qt::Horizontal,"Age");
綁定數(shù)據(jù)到模型和 TableView
創(chuàng)建一個(gè) QItemSelectionModel
對(duì)象 theSelection
,并將其綁定到查詢(xún)模型 qryModel
上。然后將模型和選擇模型分別綁定到 ui->tableView
上,設(shè)置選擇行為為按行選擇。
theSelection=new QItemSelectionModel(qryModel);
ui->tableView->setModel(qryModel);
ui->tableView->setSelectionModel(theSelection);
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
創(chuàng)建數(shù)據(jù)映射器 QDataWidgetMapper
創(chuàng)建 QDataWidgetMapper
對(duì)象 dataMapper
,設(shè)置提交策略為自動(dòng)提交。然后將映射器和模型綁定,并將三個(gè)文本框小部件與模型的相應(yīng)字段進(jìn)行映射。最后,將映射器移動(dòng)到第一行。
dataMapper= new QDataWidgetMapper();
dataMapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
dataMapper->setModel(qryModel);
dataMapper->addMapping(ui->lineEdit_id,0);
dataMapper->addMapping(ui->lineEdit_name,1);
dataMapper->addMapping(ui->lineEdit_age,2);
dataMapper->toFirst();
綁定信號(hào)
連接 theSelection
的 currentRowChanged
信號(hào)到槽函數(shù) on_currentRowChanged
。這樣,當(dāng)用戶(hù)在表格中選擇不同行時(shí),將觸發(fā)槽函數(shù)執(zhí)行相應(yīng)的操作。
connect(theSelection,SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this,SLOT(on_currentRowChanged(QModelIndex,QModelIndex)));
上述代碼實(shí)現(xiàn)了組件初始化,使用數(shù)據(jù)庫(kù)表格中的數(shù)據(jù)填充了一個(gè) QTableView
,并通過(guò) QDataWidgetMapper
將選中行的數(shù)據(jù)映射到三個(gè)文本框中,同時(shí)通過(guò)信號(hào)槽機(jī)制實(shí)現(xiàn)了在底部編輯框中顯示當(dāng)前選中行的功能。
1.2 綁定事件
接著我們需要綁定TableView
表格的on_currentRowChanged()
事件,當(dāng)用戶(hù)點(diǎn)擊TableView
表格中的某個(gè)屬性時(shí)則自動(dòng)觸發(fā)該函數(shù),在此函數(shù)內(nèi)我們完成對(duì)其他組件的填充,如下是對(duì)綁定事件的具體分析。
如下這部分代碼使用了 Q_UNUSED
宏,用于標(biāo)記 previous
未使用,以避免編譯器產(chǎn)生未使用變量的警告。接著判斷 current
是否有效,如果無(wú)效則直接返回,避免出現(xiàn)錯(cuò)誤。
Q_UNUSED(previous);
if (!current.isValid())
{
return;
}
dataMapper->setCurrentModelIndex(current);
這段代碼判斷當(dāng)前行是否為表格的第一行或最后一行,并輸出相應(yīng)的信息。
// 獲取到記錄開(kāi)頭結(jié)尾
bool first=(current.row()==0); // 是否首記錄
bool last=(current.row()==qryModel->rowCount()-1);// 是否尾記錄
std::cout << "IsFirst: " << first << "IsLast: " << last << std::endl;
獲取當(dāng)前選擇行的 name
字段的數(shù)據(jù),并輸出到標(biāo)準(zhǔn)輸出流。
// 獲取name字段數(shù)據(jù)
int curRecNo=theSelection->currentIndex().row(); // 獲取當(dāng)前行號(hào)
QSqlRecord curRec=qryModel->record(curRecNo); // 獲取當(dāng)前記錄
QString uname = curRec.value("name").toString();
std::cout << "Student Name = " << uname.toStdString() << std::endl;
代碼查詢(xún)名為 StudentAddressList
的表中與當(dāng)前用戶(hù)名匹配的所有數(shù)據(jù),并將 address
字段的數(shù)據(jù)提取出來(lái)存儲(chǔ)在 the_data
容器中。
// 查StudentAddressList表中所有數(shù)據(jù)
// 根據(jù)姓名過(guò)濾出該用戶(hù)的所有數(shù)據(jù)
QSqlQuery query;
query.prepare("select * from StudentAddressList where name = :x");
query.bindValue(":x",uname);
query.exec();
// 循環(huán)獲取該用戶(hù)的數(shù)據(jù),并將address字段提取出來(lái)放入QStringList容器
QSqlRecord rec = query.record();
QStringList the_data;
while(query.next())
{
int index = rec.indexOf("address");
QString data = query.value(index).toString();
std::cout << "User Address = " << data.toStdString() << std::endl;
the_data.append(data);
}
最后,將 the_data
中的數(shù)據(jù)關(guān)聯(lián)到 QListView
控件上,并設(shè)置為不可編輯。這樣,用戶(hù)就可以在 QListView
中看到與當(dāng)前表格行對(duì)應(yīng)的地址信息。
// 關(guān)聯(lián)到ListView數(shù)據(jù)表中
QStringListModel *model;
model = new QStringListModel(the_data);
ui->listView->setModel(model);
ui->listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
至此核心功能的實(shí)現(xiàn)就結(jié)束了,通過(guò)對(duì)信號(hào)的綁定,當(dāng)讀者運(yùn)行程序并選中TableView
組件中的任意一行是,其右側(cè)ListView
與底部的LineEdit
編輯框均會(huì)實(shí)現(xiàn)聯(lián)動(dòng)效果,如下圖所示;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-760414.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-760414.html
到了這里,關(guān)于C++ Qt開(kāi)發(fā):數(shù)據(jù)庫(kù)與TableView多組件聯(lián)動(dòng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!