QT学习:Qt操作数据库
本節(jié)由不同Qt類支撐的三部分組成,QtSql模塊層次結(jié)構(gòu)如下圖所示:
一、Qt操作SQLite數(shù)據(jù)庫
Qt提供了一種進程內(nèi)數(shù)據(jù)庫SQLite。它小巧靈活,無須額外安裝配置且支持大部分ANSI SQL92標準,是一個輕量級的數(shù)據(jù)庫,概括起來具有以下優(yōu)點。
(1)SQLite的設(shè)計目的是實現(xiàn)嵌入式SQL數(shù)據(jù)庫引擎,它基于純C語言代碼,已經(jīng)應(yīng)用在非常廣泛的領(lǐng)域內(nèi)。
(2)SQLite在需要持久存儲時可以直接讀寫硬盤上的數(shù)據(jù)文件,在無須持久存儲時也可以將整個數(shù)據(jù)庫置于內(nèi)存中,兩者均不需要額外的服務(wù)器端進程,即SQLite是無須獨立運行的數(shù)據(jù)庫引擎。
(3)開放源代碼,整套代碼少于3萬行,有良好的注釋和90%以上的測試覆蓋率。
(4)少于250KB的內(nèi)存占用容量(gcc編譯情況下)。
(5)支持視圖、觸發(fā)器和事務(wù),支持嵌套SQL功能。
(6)提供虛擬機用于處理SQL語句。
(7)不需要配置,不需要安裝,也不需要管理員。
(8)支持大部分ANSI SQL92標準。
(9)大部分應(yīng)用的速度比目前常見的客戶端/服務(wù)器結(jié)構(gòu)的數(shù)據(jù)庫快。
(10)編程接口簡單易用。
下面使用通過例子來學會如何操作數(shù)據(jù)庫:
使用SQLite數(shù)據(jù)庫完成大批量數(shù)據(jù)的增加、刪除、 更新和查詢操作并輸出。
操作步驟如下。
(1)在“QSQLiteEx.pro”文件中添加如下代碼:
(2)源文件“main.cpp”的具體代碼如下:
#include <QCoreApplication> #include <QTextCodec> #include <QSqlDatabase> #include <QSqlQuery> #include <QTime> #include <QSqlError> #include <QtDebug> #include <QSqlDriver> #include <QSqlRecord> int main(int argc,char * argv[]) {QCoreApplication a(argc, argv);QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());//設(shè)置中文顯示QSqlDatabase db =QSqlDatabase::addDatabase("QSQLITE");//以“QSQLITE”為數(shù)據(jù)庫類型,在本進程地址空間內(nèi)創(chuàng)建一個SQLite數(shù)據(jù)庫。db.setHostName("easybook-3313b0"); //設(shè)置數(shù)據(jù)庫主機名db.setDatabaseName("qtDB.db"); //以上創(chuàng)建的數(shù)據(jù)庫以“qtDB.db”為數(shù)據(jù)庫名。它是SQLite在建立內(nèi)存數(shù)據(jù)庫時唯一可用的名字。 db.setUserName("wang"); //設(shè)置數(shù)據(jù)庫用戶名db.setPassword("123456"); //設(shè)置數(shù)據(jù)庫密碼db.open(); //打開連接//創(chuàng)建數(shù)據(jù)庫表QSqlQuery query; //創(chuàng)建QSqlQuery對象。QtSql模塊中的QSqlQuery類提供了一個執(zhí)行SQL語句的接口,并且可以遍歷執(zhí)行的返回結(jié)果集。除QSqlQuery類外,Qt還提供了三種用于訪問數(shù)據(jù)庫的高層類,即QSqlQueryModel、QSqlTableModel和QSqlRelationTableModel。它們無須使用SQL語句就可以進行數(shù)據(jù)庫操作,而且可以很容易地將結(jié)果在表格中表示出來。bool success=query.exec("create table automobile(id int primary key,attribute varchar,type varchar,kind varchar,nation int,carnumber int,elevaltor int,distance int,oil int,temperature int)"); //創(chuàng)建數(shù)據(jù)庫表“automobil”,該表具有10個字段。在執(zhí)行exec()函數(shù)調(diào)用后,就可以操作返回的結(jié)果了。 if(success)qDebug()<<QObject::tr("數(shù)據(jù)庫表創(chuàng)建成功!\n");elseqDebug()<<QObject::tr("數(shù)據(jù)庫表創(chuàng)建失敗!\n");//查詢query.exec("select * from automobil");QSqlRecord rec = query.record();qDebug() << QObject::tr("automobil表字段數(shù):" )<< rec.count();//插入記錄QTime t;t.start(); //啟動一個計時器,統(tǒng)計操作耗時query.prepare("insert into automobil values(?,?,?,?,?,?,?,?,?,?)"); //如果要插入多條記錄,或者避免將值轉(zhuǎn)換為字符串(即正確地轉(zhuǎn)義),則可以首先調(diào)用prepare()函數(shù)指定一個包含占位符的query,然后綁定要插入的值。Qt對所有數(shù)據(jù)庫均可以支持Qracle類型的占位符和ODBC類型的占位符。此處使用了ODBC類型的定位占位符long records=100; //向表中插入任意的100條記錄for(int i=0;i<records;i++){query.bindValue(0,i); //調(diào)用bindValue()或addBindValue()函數(shù)綁定要插入的值。 query.bindValue(1,"四輪");query.bindValue(2,"轎車");query.bindValue(3,"富康");query.bindValue(4,rand()%100);query.bindValue(5,rand()%10000);query.bindValue(6,rand()%300);query.bindValue(7,rand()%200000);query.bindValue(8,rand()%52);query.bindValue(9,rand()%100);success=query.exec(); //調(diào)用exec()函數(shù)在query中插入對應(yīng)的值,之后,可以繼續(xù)調(diào)用bindValue() 或addBindValue()函數(shù)綁定新值,然后再次調(diào)用exec()函數(shù)在query中插入新值。 if(!success){QSqlError lastError=query.lastError();qDebug()<<lastError.driverText()<<QString(QObject::tr("插入失敗"));}}qDebug()<<QObject::tr("插入 %1 條記錄,耗時:%2 ms").arg(records). arg(t.elapsed()); //向表中插入任意的100條記錄,操作成功后輸出操作消耗的時間。//排序t.restart(); //重啟計時器success=query.exec("select * from automobil order by id desc");//按id字段的降序?qū)⒉樵儽碇袆倓偛迦氲?00條記錄進行排序。 if(success)qDebug()<<QObject::tr("排序 %1 條記錄,耗時:%2 ms").arg(records).arg(t. elapsed()); //輸出操作耗時elseqDebug()<<QObject::tr("排序失敗!");//更新記錄t.restart(); //重啟計時器for(int i=0;i<records;i++){query.clear();query.prepare(QString("update automobil set attribute=?,type=?,""kind=?,nation=?,""carnumber=?,elevaltor=?,""distance=?,oil=?,""temperature=? where id=%1").arg(i));//更新操作與插入操作類似,只是使用的SQL語句不同。 query.bindValue(0,"四輪");query.bindValue(1,"轎車");query.bindValue(2,"富康");query.bindValue(3,rand()%100);query.bindValue(4,rand()%10000);query.bindValue(5,rand()%300);query.bindValue(6,rand()%200000);query.bindValue(7,rand()%52);query.bindValue(8,rand()%100);success=query.exec();if(!success){QSqlError lastError=query.lastError();qDebug()<<lastError.driverText()<<QString(QObject::tr("更新失敗"));}}qDebug()<<QObject::tr("更新 %1 條記錄,耗時:%2 ms").arg(records).arg(t.elapsed());//刪除t.restart(); //重啟計時器query.exec("delete from automobil where id=15"); //執(zhí)行刪除id為15的記錄的操作。 //輸出操作耗時qDebug()<<QObject::tr("刪除一條記錄,耗時:%1 ms").arg(t.elapsed());return 0;//return a.exec(); }在本進程地址空間內(nèi)創(chuàng)建一個SQLite數(shù)據(jù)庫時。要注意兩點:
① 在進行數(shù)據(jù)庫操作之前,必須首先建立與數(shù)據(jù)庫的連接。數(shù)據(jù)庫連接由任意字符串標識。在沒有指定連接的情況下,QSqlDatabase可以提供默認連接供Qt其他的SQL類使用。建立一條數(shù)據(jù)庫連接的代碼如下:
其中,靜態(tài)函數(shù)QSqlDatabase::addDatabase()返回一條新建立的數(shù)據(jù)庫連接,其原型為:
QSqlDatabase::addDatabase ( const QString &type, const QString &connectionName=QLatin1String(defaultConnection) )(1)參數(shù)type為驅(qū)動名,本例使用的是QSQLITE 驅(qū)動。
(2)參數(shù)connectionName為連接名,默認值為默認連接,本例的連接名為connect。如果沒有指定此參數(shù),則新建立的數(shù)據(jù)庫連接將成為本程序的默認連接,并且可以被后續(xù)不帶參數(shù)的函數(shù)database()引用。如果指定了此參數(shù)(連接名),則函數(shù)database(connectionName)將獲取這個指定的數(shù)據(jù)庫連接。
② QtSql模塊使用驅(qū)動插件(driver plugins)與不同的數(shù)據(jù)庫接口通信。由于QtSql模塊的應(yīng)用程序接口是與具體數(shù)據(jù)庫無關(guān)的,所以所有與數(shù)據(jù)庫相關(guān)的代碼均包含在這些驅(qū)動插件中。目前,Qt支持的數(shù)據(jù)庫驅(qū)動插件見下圖:
(3)打開“QSQLiteEx.pro”文件,添加語句:
(4)運行結(jié)果如圖所示:
二、Qt操作主/從視圖及XML
以主/從視圖的模式展現(xiàn)汽車制造商與生產(chǎn)汽車的關(guān)系。當在汽車制造商表中選 中某一個制造商時,下面的汽車表中將顯示出該制造商生產(chǎn)的所有產(chǎn)品。當在汽車表中選中某個車型時,右邊的列表將顯示出該車的車型和制造商的詳細信息,所不同的是,車型的相關(guān)信息存儲在XML文件中。
1、主界面布局
(1)主窗口MainWindow類繼承自QMainWindow類,定義了主顯示界面,頭文件“mainwindow.h” 的具體代碼如下:
#include <QMainWindow> #include <QGroupBox> #include <QTableView> #include <QListWidget> #include <QLabel> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); private: QGroupBox *createCarGroupBox(); QGroupBox *createFactoryGroupBox(); QGroupBox *createDetailsGroupBox(); void createMenuBar(); QTableView *carView; //顯示汽車的視圖 QTableView *factoryView; //顯示汽車制造商的視圖 QListWidget *attribList; //顯示車型的詳細信息列表 /* 聲明相關(guān)的信息標簽 */ QLabel *profileLabel; QLabel *titleLabel; };(2)源文件“mainwindow.cpp”的具體內(nèi)容如下:
#include "mainwindow.h" #include <QGridLayout> #include <QAbstractItemView> #include <QHeaderView> #include <QAction> #include <QMenu> #include <QMenuBar> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QGroupBox *factory = createFactoryGroupBox(); QGroupBox *cars = createCarGroupBox(); QGroupBox *details = createDetailsGroupBox(); uniqueCarId = carModel->rowCount(); uniqueFactoryId = factoryModel->rowCount(); //布局 QGridLayout *layout = new QGridLayout; layout->addWidget(factory, 0, 0); layout->addWidget(cars, 1, 0); layout->addWidget(details, 0, 1, 2, 1); layout->setColumnStretch(1, 1); layout->setColumnMinimumWidth(0, 500); QWidget *widget = new QWidget; widget->setLayout(layout); setCentralWidget(widget); createMenuBar(); resize(850, 400); setWindowTitle(tr("主從視圖")); }createFactoryGroupBox()函數(shù)的具體內(nèi)容如下:
QGroupBox* MainWindow::createFactoryGroupBox() { factoryView = new QTableView; factoryView->setEditTriggers(QAbstractItemView::NoEditTriggers); //對于可讀寫的模型類QSqlTableModel 和QSqlRelationalTableModel,視圖允許用戶編輯其中的字段,也可以通過調(diào)用此語句禁止用戶編輯。 factoryView->setSortingEnabled(true); factoryView->setSelectionBehavior(QAbstractItemView::SelectRows); factoryView->setSelectionMode(QAbstractItemView::SingleSelection); factoryView->setShowGrid(false); factoryView->setAlternatingRowColors(true); factoryView->setModel(factoryModel); connect(factoryView, SIGNAL(clicked (QModelIndex )), this, SLOT(changeFactory(QModelIndex ))); QGroupBox *box = new QGroupBox(tr("汽車制造商")); QGridLayout *layout = new QGridLayout; layout->addWidget(factoryView, 0, 0); box->setLayout(layout); return box; }createCarGroupBox()函數(shù)的具體代碼如下:
QGroupBox* MainWindow::createCarGroupBox() { QGroupBox *box = new QGroupBox(tr("汽車")); carView = new QTableView; carView->setEditTriggers(QAbstractItemView::NoEditTriggers); carView->setSortingEnabled(true); carView->setSelectionBehavior(QAbstractItemView::SelectRows); carView->setSelectionMode(QAbstractItemView::SingleSelection); carView->setShowGrid(false); carView->verticalHeader()->hide(); carView->setAlternatingRowColors(true); carView->setModel(carModel); connect(carView, SIGNAL(clicked(QModelIndex)), this, SLOT(showCarDetails(QModelIndex))); connect(carView, SIGNAL(activated(QModelIndex)), this, SLOT(showCarDetails(QModelIndex))); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(carView, 0, 0); box->setLayout(layout); return box; }createDetailsGroupBox()函數(shù)的具體代碼如下:
QGroupBox* MainWindow::createDetailsGroupBox() { QGroupBox *box = new QGroupBox(tr("詳細信息")); profileLabel = new QLabel; profileLabel->setWordWrap(true); profileLabel->setAlignment(Qt::AlignBottom); titleLabel = new QLabel; titleLabel->setWordWrap(true); titleLabel->setAlignment(Qt::AlignBottom); attribList = new QListWidget; QGridLayout *layout = new QGridLayout; layout->addWidget(profileLabel, 0, 0, 1, 2); layout->addWidget(titleLabel, 1, 0, 1, 2); layout->addWidget(attribList, 2, 0, 1, 2); layout->setRowStretch(2, 1); box->setLayout(layout); return box; }createMenuBar()函數(shù)的具體代碼如下:
void MainWindow::createMenuBar() { QAction *addAction = new QAction(tr("添加"), this); QAction *deleteAction = new QAction(tr("刪除"), this); QAction *quitAction = new QAction(tr("退出"), this); addAction->setShortcut(tr("Ctrl+A")); deleteAction->setShortcut(tr("Ctrl+D")); quitAction->setShortcut(tr("Ctrl+Q")); QMenu *fileMenu = menuBar()->addMenu(tr("操作菜單")); fileMenu->addAction(addAction); fileMenu->addAction(deleteAction); fileMenu->addSeparator(); fileMenu->addAction(quitAction); connect(addAction, SIGNAL(triggered(bool)), this, SLOT(addCar())); connect(deleteAction, SIGNAL(triggered(bool)), this, SLOT(delCar())); connect(quitAction, SIGNAL(triggered(bool)), this, SLOT(close())); }(3)運行結(jié)果如下圖所示:
2、連接數(shù)據(jù)庫
添加類“ConnDlg”,在“頭文件”文本框中輸入“connectdlg.h”;在“源文件”文本框中輸入 “connectdlg.cpp”;在“界面文件”文本框中輸入“connectdlg.ui”。
ui界面如圖所示:
(2)在頭文件“connectdlg.h”中,ConnDlg類繼承自QDialog類,主要完成從界面獲取用戶設(shè)置的連接參數(shù)信息。ConnDlg類的定義中聲明了需要的各種函數(shù),其具體代碼如下:
(3)在源文件“connectdlg.cpp”中,ConnDlg類的構(gòu)造函數(shù)完成了初始化ui界面及查找當前所有可用的Qt數(shù)據(jù)庫驅(qū)動,并將其加入ui界面的驅(qū)動組合框中,以及其他一些功能,其具體代碼如下:
#include "connectdlg.h" #include "ui_connectdlg.h" #include <QSqlDatabase> #include <QtSql> ConnDlg::ConnDlg(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QStringList drivers = QSqlDatabase::drivers(); //查找數(shù)據(jù)庫驅(qū)動,以QStringList的形式返回所有可用驅(qū)動名。 ui.comboDriver->addItems(drivers); //將這些驅(qū)動名加入ui界面的組合框。 connect(ui.comboDriver,SIGNAL(currentIndexChanged( const QString & )),this, SLOT(driverChanged(const QString &))); //關(guān)聯(lián)這個組合框的信號current IndexChanged(const QString&)與槽函數(shù)driverChanged(const QString &),以便每當用戶在這個組合框中選取了不同的驅(qū)動時,槽函數(shù)driverChanged()都會被調(diào)用。 ui.status_label->setText(tr("準備連接數(shù)據(jù)庫!")); //設(shè)置當前程序運行狀態(tài)。 }槽函數(shù)driverChanged()的具體代碼如下:
void ConnDlg::driverChanged(const QString &text) { if(text =="QSQLITE") { ui.editDatabase->setEnabled(false); ui.editUsername->setEnabled(false); ui.editPassword->setEnabled(false); ui.editHostname->setEnabled(false); ui.portSpinBox->setEnabled(false); } else { ui.editDatabase->setEnabled(true); ui.editUsername->setEnabled(true); ui.editPassword->setEnabled(true); ui.editHostname->setEnabled(true); ui.portSpinBox->setEnabled(true); } }driverName()函數(shù)的具體代碼如下:
QString ConnDlg::driverName() const { return ui.comboDriver->currentText(); }databaseName()函數(shù)的具體代碼如下:
QString ConnDlg::databaseName() const { return ui.editDatabase->text(); }userName()函數(shù)的具體代碼如下:
QString ConnDlg::userName() const { return ui.editUsername->text(); }password()函數(shù)的具體代碼如下:
QString ConnDlg::password() const { return ui.editPassword->text(); }hostName()函數(shù)的具體代碼如下:
QString ConnDlg::hostName() const { return ui.editHostname->text(); }port()函數(shù)的具體代碼如下:
int ConnDlg::port() const { return ui.portSpinBox->value(); }當用戶單擊“連接”按鈕時,調(diào)用on_okButton_clicked()函數(shù),其具體實現(xiàn)代碼如下:
void ConnDlg::on_okButton_clicked() { if(ui.comboDriver->currentText().isEmpty()) //檢查用戶是否選擇了一個數(shù)據(jù)庫驅(qū)動。 { ui.status_label->setText(tr("請選擇一個數(shù)據(jù)庫驅(qū)動!")); ui.comboDriver->setFocus(); } else if(ui.comboDriver->currentText() =="QSQLITE") //根據(jù)驅(qū)動類型進行處理。如果是QSQLITE驅(qū)動, 則調(diào)用addSqliteConnection()函數(shù)創(chuàng)建一個內(nèi)存數(shù)據(jù)庫。 { addSqliteConnection(); //創(chuàng)建數(shù)據(jù)庫表,如已存在則無須執(zhí)行 creatDB(); //當打開數(shù)據(jù)庫連接成功時,程序使用SQL語句創(chuàng)建相關(guān)數(shù)據(jù)表,并插入記錄信息。 accept(); } else { QSqlError err = addConnection(driverName(), databaseName(), hostName(), userName(), password(), port()); //如果是其他驅(qū)動,則調(diào)用addConnection()函數(shù)創(chuàng)建一個其他所選類型數(shù)據(jù)庫的 連接。 if(err.type() != QSqlError::NoError) //在連接出錯時顯示錯誤信息。使用QSqlError類處理連接錯誤,QSqlError類提供與具體數(shù)據(jù)庫相關(guān)的錯誤信息。 ui.status_label->setText(err.text()); else //當連接沒有錯誤時,在狀態(tài)欄顯示數(shù)據(jù)庫連接成功信息。 ui.status_label->setText(tr("連接數(shù)據(jù)庫成功!")); //創(chuàng)建數(shù)據(jù)庫表,如已存在則無須執(zhí)行 accept(); } }addConnection()函數(shù)用來建立一條數(shù)據(jù)庫連接,其具體實現(xiàn)內(nèi)容如下:
QSqlError ConnDlg::addConnection(const QString &driver, const QString &dbName, const QString &host,const QString &user, const QString &passwd, int port) { QSqlError err; QSqlDatabase db = QSqlDatabase::addDatabase(driver); db.setDatabaseName(dbName); db.setHostName(host); db.setPort(port); if(!db.open(user, passwd)) { err = db.lastError(); } return err; //返回這個錯誤信息 }addSqliteConnection()函數(shù)建立一條QSQLITE數(shù)據(jù)庫驅(qū)動對應(yīng)的sqlite數(shù)據(jù)庫連接,其具體內(nèi)容如下:
void ConnDlg::addSqliteConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("databasefile"); if(!db.open()) { ui.status_label->setText(db.lastError().text()); return; } ui.status_label->setText(tr("創(chuàng)建sqlite數(shù)據(jù)庫成功!")); }ConnDlg::creatDB()函數(shù)創(chuàng)建了相關(guān)的兩張數(shù)據(jù)表,并在其中插入適當信息。其具體代碼如下:
void ConnDlg::creatDB() { QSqlQuery query; //創(chuàng)建QSqlQuery對象。一旦數(shù)據(jù)庫連接建立后,就可以使用QSqlQuery對象執(zhí)行底層數(shù)據(jù)庫支持的SQL語句,此方法所要做的僅是首先創(chuàng)建一個QSqlQuery對象,然后再調(diào)用QSqlQuery::exec()函數(shù)。 query.exec("create table factory (id int primary key,manufactory varchar (40),address varchar(40))"); //此處是將SQL語句作為QSqlQuery::exec()的參數(shù),但是它同樣可以直接傳給構(gòu)造函數(shù),從而使該語句立即被執(zhí)行。 query.exec(QObject::tr("insert into factory values(1, '一汽大眾', '長春')")); query.exec(QObject::tr("insert into factory values(2, '二汽神龍', '武漢')")); query.exec(QObject::tr("insert into factory values(3, '上海大眾', '上海')")); query.exec("create table cars (carid int primary key, name varchar(50), factoryid int, year int, foreign key(factoryid) references factory)"); //汽車表“cars”中有一個表示生產(chǎn)廠家的字段factoryid指向factory的id字段,即factoryid是一個外鍵。一些數(shù)據(jù)庫不支持外鍵,如果將此語句去掉,程序仍然可以運行,但數(shù)據(jù)庫將不強制執(zhí)行參照完整性。 query.exec(QObject::tr("insert into cars values(1,'奧迪A6',1,2005)")); query.exec(QObject::tr("insert into cars values(2,'捷達', 1, 1993)")); query.exec(QObject::tr("insert into cars values(3,'寶來', 1, 2000)")); query.exec(QObject::tr("insert into cars values(4,'畢加索',2, 1999)")); query.exec(QObject::tr("insert into cars values(5,'富康', 2, 2004)")); query.exec(QObject::tr("insert into cars values(6,'標致307',2, 2001)")); query.exec(QObject::tr("insert into cars values(7,'桑塔納',3, 1995)")); query.exec(QObject::tr("insert into cars values(8,'帕薩特',3, 2000)")); }(4)修改“main.cpp”的代碼如下:
#include "mainwindow.h" #include <QApplication> #include <QDialog> #include "connectdlg.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); ConnDlg dialog; if(dialog.exec() != QDialog::Accepted) return -1; dialog.show(); return a.exec(); }(5)在“SQLEx.pro”文件中添加如下內(nèi)容:
QT += sql(6)運行程序,出現(xiàn)如下圖所示的界面:
在“驅(qū)動:”欄中選擇“QSQLITE”,單擊“連接”按鈕,在“狀態(tài):”欄中將顯示“創(chuàng)建sqlite數(shù)據(jù)庫成功!”,這說明之前編寫的創(chuàng)建及連接數(shù)據(jù)庫的代碼是正確的,接下來實現(xiàn)用主/從視圖模式瀏覽數(shù)據(jù)庫中的信息。
3、主/從視圖應(yīng)用
(1)在頭文件“mainwindow.h”中添加如下代碼:
#include <QFile> #include <QSqlRelationalTableModel> #include <QSqlTableModel> #include <QModelIndex> #include <QDomNode> #include <QDomDocument> public: MainWindow(const QString &factoryTable, const QString &carTable, QFile *carDetails,QWidget *parent = 0); ~MainWindow(); private slots: void addCar(); void changeFactory(QModelIndex index); void delCar(); void showCarDetails(QModelIndex index); void showFactorytProfile(QModelIndex index); private: void decreaseCarCount(QModelIndex index); void getAttribList(QDomNode car); QModelIndex indexOfFactory(const QString &factory); void readCarData(); void removeCarFromDatabase(QModelIndex index); void removeCarFromFile(int id); QDomDocument carData; QFile *file; QSqlRelationalTableModel *carModel; QSqlTableModel *factoryModel;(2)在源文件“mainwindow.cpp”中添加如下代碼:
#include <QMessageBox> #include <QSqlRecord> MainWindow::MainWindow(const QString &factoryTable, const QString &car Table, QFile *carDetails, QWidget *parent) : QMainWindow(parent) { file = carDetails; readCarData(); //將XML文件里的車型信息讀入QDomDocument類實例carData中,以便后面的操 作 carModel = new QSqlRelationalTableModel(this); //為汽車表“cars”創(chuàng)建一個QSqlRelationalTableModel模型。 carModel->setTable(carTable); carModel->setRelation(2, QSqlRelation(factoryTable, "id", "manufactory")); //說明上面創(chuàng)建的QSqlRelationalTableModel模型的第二個字段(即汽車表“cars”中的factoryid字段)是汽車制造商表 “factory”中id字段的外鍵,但其顯示為汽車制造商表“factory”的manufactory字段,而不是id字段。 carModel->select(); factoryModel = new QSqlTableModel(this); //為汽車制造商表“factory”創(chuàng)建一個QSqlTableModel模 型 factoryModel->setTable(factoryTable); factoryModel->select(); }changeFactory()函數(shù)的具體代碼如下:
void MainWindow::changeFactory(QModelIndex index) { QSqlRecord record = factoryModel->record(index.row());//取出用戶選擇的這條汽車制造商記錄。 QString factoryId = record.value("id").toString(); //獲取以上選擇的汽車制造商的主鍵。QSqlRecord::value() 需要指定字段名或字段索引。 carModel->setFilter("id = '"+ factoryId +"'") ; //在汽車表模型“carModel”中設(shè)置過濾器,使其只顯示所選汽車制造商的車型。 showFactorytProfile(index); //在“詳細信息”中顯示所選汽車制造商的信息。 }在“詳細信息”中顯示所選汽車制造商的信息函數(shù)showFactorytProfile()的具體代碼如下:
void MainWindow::showFactorytProfile(QModelIndex index) { QSqlRecord record = factoryModel->record(index.row()); //取出用戶選擇的這條汽車制造商記錄。 QString name = record.value("manufactory").toString(); //從汽車制造商模型“factoryModel”中獲得制造商的名稱。 int count = carModel->rowCount(); //從汽車表模型“carModel”中獲得車型數(shù)量 profileLabel->setText(tr("汽車制造商:%1\n產(chǎn)品數(shù)量: %2").arg(name).arg(count)); //在“詳細信息”的 profileLabel標簽中顯示這兩部分信息 profileLabel->show(); titleLabel->hide(); attribList->hide(); }showCarDetails()函數(shù)的具體代碼如下:
void MainWindow::showCarDetails(QModelIndex index) { QSqlRecord record = carModel->record(index.row()); //首先從汽車表模型“carModel”中獲取所選記錄。 QString factory = record.value("manufactory").toString(); //獲得所選記錄的制造商名factory字段。 QString name = record.value("name").toString(); QString year = record.value("year").toString(); QString carId = record.value("carid").toString(); showFactorytProfile(indexOfFactory(factory)); //重復顯示制造商信息 titleLabel->setText(tr("品牌: %1 (%2)").arg(name).arg(year));// 在“詳細信息”的titleLabel標簽中顯示該車型的品牌名和生產(chǎn)時間 titleLabel->show(); QDomNodeList cars = carData.elementsByTagName("car");//記錄了車型信息的XML文件中搜索匹配的車型 for(int i = 0; i < cars.count(); i++) //找出所有car標簽 { QDomNode car = cars.item(i); if(car.toElement().attribute("id") == carId) //在這些標簽中找出id屬性與所選車型主鍵carId相同的屬性id。 { getAttribList(car.toElement()); //顯示這個匹配的car標簽中的相關(guān)信息(如信息編號number和該編號下的信息內(nèi)容)。 break; } } if(!attribList->count() == 0) attribList->show(); }getAttribList()函數(shù)檢索以上獲得的car標簽下的所有子節(jié)點,將這些子節(jié)點的信息在“詳細信息”的QListWidget窗體中顯示。這些信息包括信息編號number和該編號下的信息內(nèi)容,其具體代碼如
下:
因為addCar()函數(shù)在此時還沒有實現(xiàn)具體的功能,所以代碼部分暫時為空:
void MainWindow::addCar(){}delCar()函數(shù)的具體代碼如下:
void MainWindow::delCar() { QModelIndexList selection = carView->selectionModel() ->selectedRows(0); if (!selection.empty()) //判斷用戶是否在汽車表中選中了一條記錄 { QModelIndex idIndex = selection.at(0); int id = idIndex.data().toInt(); QString name = idIndex.sibling(idIndex.row(), 1).data(). toString(); QString factory = idIndex.sibling(idIndex.row(), 2).data(). toString(); QMessageBox::StandardButton button; button = QMessageBox::question(this, tr("刪除汽車記錄"), QString(tr("確認刪除由'%1'生產(chǎn)的'%2'嗎?").arg(factory).arg (name)),QMessageBox::Yes | QMessageBox::No); //如果是,則彈出一個確認對話框,提示用戶是否刪除該記錄。 if (button == QMessageBox::Yes) //得到用戶確認 { removeCarFromFile(id); //從XML文件中刪除相關(guān)內(nèi)容 removeCarFromDatabase(idIndex); //從數(shù)據(jù)庫表中刪除相關(guān)內(nèi)容 decreaseCarCount(indexOfFactory(factory)); //調(diào)整汽車制造商表中的內(nèi)容。 } else //如果用戶沒有在汽車表中選中記錄,則提示用戶進行選擇 { QMessageBox::information(this, tr("刪除汽車記錄"),tr("請選擇要刪除的記錄。")); } } }removeCarFromFile()函數(shù)遍歷XML文件中的所有car標簽,首先找出id屬性與汽車表中所選記錄主鍵相同的節(jié)點,然后將其刪除。其具體代碼如下:
void MainWindow::removeCarFromFile(int id) { QDomNodeList cars = carData.elementsByTagName("car"); for(int i = 0; i< cars.count(); i++) { QDomNode node = cars.item(i); if(node.toElement().attribute("id").toInt() == id) { carData.elementsByTagName ("archive").item(0).removeChild(node); break; } } }removeCarFromDatabase()函數(shù)將汽車表中所選中的行從汽車表模型“carModel”中移除,這個模型將自動刪除數(shù)據(jù)庫表中的對應(yīng)記錄,其具體代碼如下:
void MainWindow::removeCarFromDatabase(QModelIndex index) { carModel->removeRow(index.row()); }刪除了某個汽車制造商的全部產(chǎn)品后,需要刪除這個汽車制造商,decreaseCarCount()函數(shù)實現(xiàn)了此功能,其具體代碼如下:
void MainWindow::decreaseCarCount(QModelIndex index) { int row = index.row(); int count = carModel->rowCount(); //汽車表中的當前記錄數(shù)。 if(count == 0) //判斷這個記錄數(shù),如果為0,則從汽車制造商表中刪除對應(yīng)的制造商。 factoryModel->removeRow(row); }readCarData()函數(shù)的具體代碼如下:
void MainWindow::readCarData() { if(!file->open(QIODevice::ReadOnly)) return; if(!carData.setContent(file)) { file->close(); return; } file->close(); }indexOfFactory()函數(shù)通過制造商的名稱進行檢索,并返回一個匹配的模型索引QModelIndex,供汽車制造商表模型的其他操作使用,其具體代碼如下:
QModelIndex MainWindow::indexOfFactory(const QString &factory) { for(int i = 0; i < factoryModel->rowCount(); i++) { QSqlRecord record = factoryModel->record(i); if(record.value("manufactory") == factory) return factoryModel->index(i, 1); } return QModelIndex(); }(3)源文件“main.cpp”的具體代碼如下:
#include <QDialog> #include <QFile> #include "connectdlg.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); //MainWindow w; //w.show(); ConnDlg dialog; if(dialog.exec() != QDialog::Accepted) return -1; QFile *carDetails = new QFile("attribs.xml"); MainWindow window("factory", "cars", carDetails); window.show(); return a.exec(); }(4)新建一個XML文件,將該文件存放在該工程的目錄下,以下是“attribs.xml”文件的詳細內(nèi)容。
<?xml version="1.0" encoding="gb2312"?> <archive><car id="1" ><attrib number="01" >排量:2393ml</attrib><attrib number="02" >價格:43.26萬元</attrib><attrib number="03" >排放:歐4</attrib><attrib number="04" >油耗:7.0l(90km/h) 8.3l(120km/h) </attrib><attrib number="05" >功率:130/6000</attrib></car><car id="2" ><attrib number="01" >排量:1600ml</attrib><attrib number="02" >價格:8.98萬元</attrib><attrib number="03" >排放:歐3</attrib><attrib number="04" >油耗:6.1l(90km/h)</attrib><attrib number="05" >功率:68/5800</attrib></car><car id="3" ><attrib number="01" >排量:1600ml</attrib><attrib number="02" >價格:11.25萬元</attrib><attrib number="03" >排放:歐3帶OBD</attrib><attrib number="04" >油耗:6.0l(90km/h)8.1l(120km/h)</attrib><attrib number="05" >功率:74/6000</attrib></car><car id="4" ><attrib number="01" >排量:1997ml</attrib><attrib number="02" >價格:15.38萬元</attrib><attrib number="03" >排放:歐3帶OBD</attrib><attrib number="04" >油耗:6.8l(90km/h)</attrib><attrib number="05" >功率:99/6000</attrib></car><car id="5" ><attrib number="01" >排量:1600ml</attrib><attrib number="02" >價格:6.58萬元</attrib><attrib number="03" >排放:歐3</attrib><attrib number="04" >油耗:6.5l(90km/h)</attrib><attrib number="05" >功率:65/5600</attrib></car><car id="6" ><attrib number="01" >排量:1997ml</attrib><attrib number="02" >價格:16.08萬元</attrib><attrib number="03" >排放:歐4</attrib><attrib number="04" >油耗:7.0l(90km/h)</attrib><attrib number="05" >功率:108/6000</attrib></car><car id="7" ><attrib number="01" >排量:1781ml</attrib><attrib number="02" >價格:7.98萬元</attrib><attrib number="03" >排放:國3</attrib><attrib number="04" >油耗:≤7.2l(90km/h)</attrib><attrib number="05" >功率:70/5200</attrib></car><car id="8" ><attrib number="01" >排量:1984ml</attrib><attrib number="02" >價格:19.58萬元</attrib><attrib number="03" >排放:歐4</attrib><attrib number="04" >油耗:7.1l(90km/h)</attrib><attrib number="05" >功率:85/5400</attrib></car></archive>(5)在“SQLEx.pro”文件中添加如下內(nèi)容:
QT += xml(6)運行程序,選擇驅(qū)動QSQLITE,單擊“連接”按鈕,彈出如下圖所示的主界面:
當用戶在“操作菜單”中選擇“刪除”子菜單時,彈出如下圖所示的“刪除汽車記錄”對話框。
4、添加記錄功能
(1)Dialog類繼承自QDialog類,該類定義了“添加產(chǎn)品”對話框的界面及完成將新加入的記錄分別插入汽車制造商表和汽車表,并且將詳細的車型信息寫入XML文件中的功能。
(2)源文件“editdialog.cpp”的具體代碼如下:
findFactoryId()函數(shù)的具體代碼如下:
int Dialog::findFactoryId(const QString &factory) { int row = 0; while (row < factoryModel->rowCount()) { QSqlRecord record = factoryModel->record(row); //檢索制造商模型factoryModel中的全部記錄。 if(record.value("manufactory") == factory) //找出與制造商參數(shù)匹配的記錄。 return record.value("id").toInt(); //將該記錄的主鍵返回 else row++; } return -1; //如果未查詢到則返回“-1” }addNewFactory ()函數(shù)的具體代碼如下:
int Dialog::addNewFactory(const QString &factory,const QString &address) { QSqlRecord record; int id = generateFactoryId(); //生成一個汽車制造商表的主鍵值 /* 在汽車制造商表中插入一條新記錄,廠名和地址由參數(shù)傳入 */ QSqlField f1("id", QVariant::Int); QSqlField f2("manufactory", QVariant::String); QSqlField f3("address", QVariant::String); f1.setValue(QVariant(id)); f2.setValue(QVariant(factory)); f3.setValue(QVariant(address)); record.append(f1); record.append(f2); record.append(f3); factoryModel->insertRecord(-1, record); return id; //返回新記錄的主鍵值}
addNewCar()函數(shù)與addNewFactory()函數(shù)的操作類似,其具體代碼如下:
addAttribs()函數(shù)實現(xiàn)了將錄入的車型信息寫入XML文件的功能,其具體代碼如下:
void Dialog::addAttribs(int carId, QStringList attribs) { /* 創(chuàng)建一個car標簽 */ QDomElement carNode = carDetails.createElement("car"); carNode.setAttribute("id", carId); //將id屬性設(shè)置為傳入的車型主鍵carId。 for(int i = 0; i < attribs.count(); i++) //將每一條信息作為子節(jié)點插入。 { QString attribNumber = QString::number(i+1); if(i < 10) attribNumber.prepend("0"); QDomText textNode = carDetails.createTextNode(attribs.at(i)); QDomElement attribNode = carDetails.createElement("attrib"); attribNode.setAttribute("number", attribNumber); attribNode.appendChild(textNode); carNode.appendChild(attribNode); } QDomNodeList archive = carDetails.elementsByTagName("archive"); archive.item(0).appendChild(carNode); if(!outputFile->open(QIODevice::WriteOnly)) //通過輸出文件指針outputFile將修改后的文件寫回磁盤 { return; } else { QTextStream stream(outputFile); archive.item(0).save(stream, 4); outputFile->close(); } } revert()函數(shù)實現(xiàn)了撤銷用戶在界面中的錄入信息功能,其具體代碼如下: void Dialog::revert() { factoryEditor->clear(); addressEditor->clear(); carEditor->clear(); yearEditor->setValue(QDate::currentDate().year()); attribEditor->clear(); }createInputWidgets()函數(shù)實現(xiàn)了輸入界面的完成,其具體代碼如下:
QGroupBox *Dialog::createInputWidgets() {QGroupBox *box = new QGroupBox(tr("添加產(chǎn)品"));QLabel *factoryLabel = new QLabel(tr("制造商:"));QLabel *addressLabel = new QLabel(tr("廠址:")); QLabel *carLabel = new QLabel(tr("品牌:"));QLabel *yearLabel = new QLabel(tr("上市時間:"));QLabel *attribLabel = new QLabel(tr("產(chǎn)品屬性 (由分號;隔開):"));factoryEditor = new QLineEdit;carEditor = new QLineEdit;addressEditor = new QLineEdit;yearEditor = new QSpinBox;yearEditor->setMinimum(1900);yearEditor->setMaximum(QDate::currentDate().year());yearEditor->setValue(yearEditor->maximum());yearEditor->setReadOnly(false);attribEditor = new QLineEdit;QGridLayout *layout = new QGridLayout;layout->addWidget(factoryLabel, 0, 0);layout->addWidget(factoryEditor, 0, 1);layout->addWidget(addressLabel, 1, 0);layout->addWidget(addressEditor, 1, 1);layout->addWidget(carLabel, 2, 0);layout->addWidget(carEditor, 2, 1);layout->addWidget(yearLabel, 3, 0);layout->addWidget(yearEditor, 3, 1);layout->addWidget(attribLabel, 4, 0, 1, 2);layout->addWidget(attribEditor, 5, 0, 1, 2);box->setLayout(layout);return box; }createButtons()函數(shù)完成了按鈕的組合功能,其具體代碼如下:
QDialogButtonBox *Dialog::createButtons() { QPushButton *closeButton = new QPushButton(tr("關(guān)閉")); QPushButton *revertButton = new QPushButton(tr("撤銷")); QPushButton *submitButton = new QPushButton(tr("提交")); closeButton->setDefault(true); connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); connect(revertButton, SIGNAL(clicked()), this, SLOT(revert())); connect(submitButton, SIGNAL(clicked()), this, SLOT(submit())); QDialogButtonBox *buttonBox = new QDialogButtonBox; buttonBox->addButton(submitButton, QDialogButtonBox::ResetRole); buttonBox->addButton(revertButton, QDialogButtonBox::ResetRole); buttonBox->addButton(closeButton, QDialogButtonBox::RejectRole); return buttonBox; }generateFactoryId()函數(shù)將全局變量uniqueFactoryId以順序加1的方式生成一個不重復的主鍵值,并將其返回供添加操作使用,其具體代碼如下:
int Dialog::generateFactoryId() { uniqueFactoryId += 1; return uniqueFactoryId; }generateCarId()函數(shù)將全局變量uniqueCarId以順序加1的方式生成一個不重復的主鍵值,并將其返回供添加操作使用,其具體內(nèi)容如下:
int Dialog::generateCarId() { uniqueCarId += 1; return uniqueCarId; }(3)在源文件“mainwindow.cpp”中添加的代碼如下:
#include "editdialog.h" extern int uniqueCarId; extern int uniqueFactoryId;MainWindow::addCar()函數(shù)啟動了一個添加記錄的對話框,具體添加操作由該對話框完成,添加完成后進行顯示,其具體實現(xiàn)內(nèi)容如下:
void MainWindow::addCar() { Dialog *dialog = new Dialog(carModel, factoryModel,carData, file, this); int accepted = dialog->exec(); if(accepted == 1) { int lastRow = carModel->rowCount() -1; carView->selectRow(lastRow); carView->scrollToBottom(); showCarDetails(carModel->index(lastRow, 0)); } }(4)當用戶選擇“添加”菜單時,彈出如圖所示的“添加產(chǎn)品”對話框,在其中輸入新添加的汽車品牌信息。
操作之后,在主界面中就立即能夠看到新加入的新品牌汽車的記錄信息,如下圖所示:
總結(jié)
以上是生活随笔為你收集整理的QT学习:Qt操作数据库的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT学习:数据操作
- 下一篇: PHP-mysql基础