17. 数据库和XML

17.1 数据库

qt使用 qsqltablemodel 模型操作数据库

$ sudo apt-get install python-qt4-sql

$ sudo apt-get install libqt4-sql-sqlite libqt4-sql-mysql

17.1.1 连接到数据库

1. SQL 数据库驱动

Driver Type Description
QDB2 IBM DB2
QIBASE Borland InterBase Driver
QMYSQL MySQL Driver
QOCI Oracle Call Interface Driver
QODBC ODBC Driver (includes Microsoft SQL Server)
QPSQL PostgreSQL Driver
QSQLITE SQLite version 3 or above
QSQLITE2 SQLite version 2
QTDS Sybase Adaptive Server
#include <QApplication>
#include <QSqlDatabase>
#include <QDebug>
#include <QStringList>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug() << "Available drivers:";
    QStringList drivers = QSqlDatabase::drivers();
    foreach(QString driver, drivers)
        qDebug() << driver;

    return a.exec();
}

2. 创建数据库连接

#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>

static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if (!db.open()) {
        QMessageBox::critical(0, "Cannot open database",
            "Unable to establish a database connection.", QMessageBox::Cancel);
        return false;
    }
    QSqlQuery query;
    query.exec("create table student (id int primary key, "
               "name varchar(20))");
    query.exec("insert into student values(0, 'LiMing')");
    query.exec("insert into student values(1, 'LiuTao')");
    query.exec("insert into student values(2, 'WangHong')");
    return true;
}

#endif // CONNECTION_H
#include <QApplication>
#include <QSqlDatabase>
#include <QDebug>
#include <QStringList>
#include "connection.h"
#include <QVariant>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建数据库连接
    if (!createConnection()) return 1;
    // 使用QSqlQuery查询整张表
    QSqlQuery query;
    query.exec("select * from student");
    while(query.next())
    {
        qDebug() << query.value(0).toInt() << query.value(1).toString();
    }

    return a.exec();
}
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>

static bool createConnection()
{
    // 创建一个数据库连接,使用“connection1”为连接名
    QSqlDatabase db1 = QSqlDatabase::addDatabase("QSQLITE", "connection1");
    db1.setDatabaseName("my1.db");
    if (!db1.open()) {
        QMessageBox::critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox::Cancel);
        return false;
    }
    // 这里要指定连接
    QSqlQuery query1(db1);
    query1.exec("create table student (id int primary key, "
               "name varchar(20))");
    query1.exec("insert into student values(0, 'LiMing')");
    query1.exec("insert into student values(1, 'LiuTao')");
    query1.exec("insert into student values(2, 'WangHong')");

    // 创建另一个数据库连接,要使用不同的连接名,这里是“connection2”
    QSqlDatabase db2 = QSqlDatabase::addDatabase("QSQLITE", "connection2");
    db2.setDatabaseName("my2.db");
    if (!db2.open()) {
        QMessageBox::critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox::Cancel);
        return false;
    }
    // 这里要指定连接
    QSqlQuery query2(db2);
    query2.exec("create table student (id int primary key, "
               "name varchar(20))");
    query2.exec("insert into student values(10, 'LiQiang')");
    query2.exec("insert into student values(11, 'MaLiang')");
    query2.exec("insert into student values(12, 'ZhangBin')");
    return true;
}

#endif // CONNECTION_H
#include <QApplication>
#include <QSqlDatabase>
#include <QDebug>
#include <QStringList>
#include "connection.h"
#include <QVariant>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建数据库连接
    if (!createConnection()) return 1;

    // 使用QSqlQuery查询连接1的整张表,先要使用连接名获取该连接
    QSqlDatabase db1 = QSqlDatabase::database("connection1");
    QSqlQuery query1(db1);
    qDebug() << "connection1:";
    query1.exec("select * from student");
    while(query1.next())
    {
        qDebug() << query1.value(0).toInt() << query1.value(1).toString();
    }
     // 使用QSqlQuery查询连接2的整张表
    QSqlDatabase db2 = QSqlDatabase::database("connection2");
    QSqlQuery query2(db2);
    qDebug() << "connection2:";
    query2.exec("select * from student");
    while(query2.next())
    {
        qDebug() << query2.value(0).toInt() << query2.value(1).toString();
    }

    return a.exec();
}

17.1.2 执行SQL语句

1. 执行一个查询

2. 浏览结果集

#!/usr/bin/env python
#coding=utf-8

'''
 sudo apt-get install python-qt4-sql
'''

from PyQt4.QtGui import QMessageBox
from PyQt4.QtSql import QSqlDatabase, QSqlQuery


def  createConnection():

    # 创建一个数据库连接,使用“connection1”为连接名
    db1 = QSqlDatabase.addDatabase("QSQLITE", "connection1")
    db1.setDatabaseName("my1.db")
    if not db1.open():
        QMessageBox.critical(0, "Cannot open database1",
                "Unable to establish a database connection.",
                             QMessageBox.Cancel)
        return False

    # 这里要指定连接
    query1 = QSqlQuery(db1)
    query1.exec_("create table student (id int primary key, "
               "name varchar(20))")
    query1.exec_("insert into student values(0, 'LiMing')")
    query1.exec_("insert into student values(1, 'LiuTao')")
    query1.exec_("insert into student values(2, 'WangHong')")

    # 创建另一个数据库连接,要使用不同的连接名,这里是“connection2”
    db2 = QSqlDatabase.addDatabase("QSQLITE", "connection2")
    db2.setDatabaseName("my2.db")
    if not db2.open():
        QMessageBox.critical(0, "Cannot open database1",
            "Unable to establish a database connection.",
                             QMessageBox.Cancel)
        return False

    # 这里要指定连接
    query2 = QSqlQuery (db2)
    query2.exec_("create table student (id int primary key, "
               "name varchar(20))")
    query2.exec_("insert into student values(10, 'LiQiang')")
    query2.exec_("insert into student values(11, 'MaLiang')")
    query2.exec_("insert into student values(12, 'ZhangBin')")
    return True
#!/usr/bin/env python
#coding=utf-8
from PyQt4.QtGui import QApplication
import sys

from PyQt4.QtSql import QSqlDatabase, QSqlQuery, QSqlDriver, QSqlRecord, QSqlField

from connection import createConnection

if __name__ == "__main__":

    app = QApplication(sys.argv)

    # 创建数据库连接
    if not createConnection():
        raise Exception("createConnection faild")

    # # 使用QSqlQuery查询连接1的整张表,先要使用连接名获取该连接
    db1 = QSqlDatabase.database("connection1")
    query1 = QSqlQuery(db1)
    print "connection1:"
    query1.exec_("select * from student")
    while(query1.next()):
       print  query1.value(0).toInt(), " ", query1.value(1).toString()

    # 使用QSqlQuery查询连接2的整张表
    db2 = QSqlDatabase.database("connection2")
    query2 = QSqlQuery (db2)
    print "connection2:"
    query2.exec_("select * from student")
    while(query2.next()):
        print query2.value(0).toInt(), " ", query2.value(1).toString()

    # 以下是在例程17-4中添加的代码
    # 先判断该数据库驱动是否支持QuerySize特性,如果支持,则可以使用size()函数,
    # 如果不支持,那么就使用其他方法来获取总行数
    if (db2.driver().hasFeature(QSqlDriver.QuerySize)):
        print  "has feature: query size"
        numRows = query2.size()
    else:
        print   "no feature: query size"
        query2.last()
        numRows = query2.at() + 1

    print  "row number: %s " % numRows

    # 指向索引为1的记录,即第二条记录
    query2.seek(1)
    # 返回当前索引值
    print "current index:  %s " % query2.at()
    # 获取当前行的记录
    record = query2.record()
    # 获取记录中“id”和“name”两个属性的值
    id = record.value("id").toInt()
    name = record.value("name").toString()
    print   "id: %s name:  %s" %(id, name)
    # 获取索引为1的属性,即第二个属性
    field = record.field(1)
    # 输出属性名和属性值,结果为“name”和“MaLiang”
    print  "second field: %s field value: %s " % \
            (field.name(), field.value().toString())

    # app.exec_()

3. 插入、更新、删除记录

#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>

static bool createConnection()
{
    // 创建一个数据库连接,使用“connection1”为连接名
    QSqlDatabase db1 = QSqlDatabase::addDatabase("QSQLITE", "connection1");
    db1.setDatabaseName("my1.db");
    if (!db1.open()) {
        QMessageBox::critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox::Cancel);
        return false;
    }
    // 这里要指定连接
    QSqlQuery query1(db1);
    query1.exec("create table student (id int primary key, "
               "name varchar(20))");
    query1.exec("insert into student values(0, 'LiMing')");
    query1.exec("insert into student values(1, 'LiuTao')");
    query1.exec("insert into student values(2, 'WangHong')");

    // 创建另一个数据库连接,要使用不同的连接名,这里是“connection2”
    QSqlDatabase db2 = QSqlDatabase::addDatabase("QSQLITE", "connection2");
    db2.setDatabaseName("my2.db");
    if (!db2.open()) {
        QMessageBox::critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox::Cancel);
        return false;
    }
    // 这里要指定连接
    QSqlQuery query2(db2);
    query2.exec("create table student (id int primary key, "
               "name varchar(20))");
    query2.exec("insert into student values(10, 'LiQiang')");
    query2.exec("insert into student values(11, 'MaLiang')");
    query2.exec("insert into student values(12, 'ZhangBin')");
    return true;
}

#endif // CONNECTION_H
#include <QApplication>
#include <QSqlDatabase>
#include <QDebug>
#include <QStringList>
#include "connection.h"
#include <QVariant>
#include <QSqlDriver>
#include <QSqlRecord>
#include <QSqlField>
#include <QSqlError>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建数据库连接
    if (!createConnection()) return 1;

    // 使用QSqlQuery查询连接1的整张表,先要使用连接名获取该连接
    QSqlDatabase db1 = QSqlDatabase::database("connection1");
    QSqlQuery query1(db1);
    qDebug() << "connection1:";
    query1.exec("select * from student");
    while(query1.next())
    {
        qDebug() << query1.value(0).toInt() << query1.value(1).toString();
    }
    // 使用QSqlQuery查询连接2的整张表
    QSqlDatabase db2 = QSqlDatabase::database("connection2");
    QSqlQuery query2(db2);
    qDebug() << "connection2:";
    query2.exec("select * from student");
    while(query2.next())
    {
        qDebug() << query2.value(0).toInt() << query2.value(1).toString();
    }

    // 以下是在例程17-4中添加的代码
    int numRows;
    // 先判断该数据库驱动是否支持QuerySize特性,如果支持,则可以使用size()函数,
    // 如果不支持,那么就使用其他方法来获取总行数
    if (db2.driver()->hasFeature(QSqlDriver::QuerySize)) {
        qDebug() << "has feature: query size";
        numRows = query2.size();
    } else {
        qDebug() << "no feature: query size";
        query2.last();
        numRows = query2.at() + 1;
    }
    qDebug() << "row number: " << numRows;

    // 指向索引为1的记录,即第二条记录
    query2.seek(1);
    // 返回当前索引值
    qDebug() << "current index: " << query2.at();
    // 获取当前行的记录
    QSqlRecord record = query2.record();
    // 获取记录中“id”和“name”两个属性的值
    int id = record.value("id").toInt();
    QString name = record.value("name").toString();
    qDebug() << "id: " << id << "name: " << name;
    // 获取索引为1的属性,即第二个属性
    QSqlField field = record.field(1);
    // 输出属性名和属性值,结果为“name”和“MaLiang”
    qDebug() << "second field: " << field.name()
             << "field value: " << field.value().toString();

    // 以下是在例程17-5中添加的代码
    query2.exec("insert into student (id, name) values (100, 'ChenYun')");

    // 下面是与上面代码等价的名称绑定实现代码
    //    query2.prepare("insert into student (id, name) values (:id, :name)");
    //    int idValue = 100;
    //    QString nameValue = "ChenYun";
    //    query2.bindValue(":id", idValue);
    //    query2.bindValue(":name", nameValue);
    //    query2.exec();

    // 下面是与上面代码等价的位置绑定实现代码
    //    query2.prepare("insert into student (id, name) values (?, ?)");
    //    int idValue = 100;
    //    QString nameValue = "ChenYun";
    //    query2.addBindValue(idValue);
    //    query2.addBindValue(nameValue);
    //    query2.exec();

    // 批处理
    query2.prepare("insert into student (id, name) values (?, ?)");
    QVariantList ids;
    ids << 20 << 21 << 22;
    query2.addBindValue(ids);
    QVariantList names;
    names << "xiaoming" << "xiaoliang" << "xiaogang";
    query2.addBindValue(names);
    if(!query2.execBatch()) qDebug() << query2.lastError();

    // 更新
    query2.exec("update student set name = 'xiaohong' where id = 20");
    // 删除
    query2.exec("delete from student where id = 21");

    query2.exec("select * from student");
    while(query2.next())
    {
        qDebug() << query2.value(0).toInt() << query2.value(1).toString();
    }

    return a.exec();
}

4. 事物

17.1.3 使用SQL模型类

除了QSqlQuery, Qt还提供了3个更高级的类来访问数据库,分别是: QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel。 这三个类都是从QAbstractTableModel派生来的,可以很容易地实现将 数据库中的数据在QListView和QTableView等项视图中显示。 使用这些类的另一个好处,这样编写的代码很容易的适应其他数据源。

例如,如果开始使用了,QSqlTableModel,而后来要改为使用XML文件来存储数据, 这样需要做的仅是更换一个数据模型。

1. SQL查询模型

QSqlQueryModel 提供了一个基于SQL查询的制度模型。

alternate text
#!/usr/bin/env python
#coding=utf-8

'''
FileName : connection.py
'''

from PyQt4.QtCore import QString
from PyQt4.QtGui import QMessageBox
from PyQt4.QtSql import QSqlDatabase, QSqlQuery

def createConnection():

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("my.db")
    if  not db.open():
        QMessageBox.critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox.Cancel)
        return  False

    query = QSqlQuery()
    # 创建student表
    query.exec_(QString("create table student (id int primary key, "
                                      "name varchar, course int)"))
    query.exec_(QString("insert into student values(1, '李强', 11)"))
    query.exec_(QString("insert into student values(2, '马亮', 11)"))
    query.exec_(QString("insert into student values(3, '孙红', 12)"))
    # 创建course表
    query.exec_(QString("create table course (id int primary key, "
                                      "name varchar, teacher varchar)"))
    query.exec_(QString("insert into course values(10, '数学', '王老师')"))
    query.exec_(QString("insert into course values(11, '英语', '张老师')"))
    query.exec_(QString("insert into course values(12, '计算机', '白老师')"))
    return  True
#!/usr/bin/env python
#coding=utf-8

'''
FileName : mainwindow.py
'''

from PyQt4.QtGui import QMainWindow,QTableView
from PyQt4.QtCore import Qt
from PyQt4.QtSql import QSqlQueryModel

class MainWindow(QMainWindow) :

    def __init__(self):
        super(MainWindow, self).__init__(None)

        self.model = QSqlQueryModel(self)
        self.model.setQuery("select * from student")
        self.model.setHeaderData(0, Qt.Horizontal, self.tr("学号"))
        self.model.setHeaderData(1, Qt.Horizontal, self.tr("姓名"))
        self.model.setHeaderData(2, Qt.Horizontal, self.tr("课程"))
        self.view = QTableView(self)
        self.view.setModel(self.model)
        self.setCentralWidget(self.view)

 先创建了,QSqlQueryModel对象,然后使用setQuery()来执行SQL语句进行查询整张student表, 并使用setHeaderDate()来设置显示的表头。

后面创建了视图,并将QSqlQueryModel对象作为其要显示的的模型。 运行程序,效果上图所示。

这里要注意,其实QSqlQueryModel中存储的是执行完setQuery()函数后的结果集, 所以视图中显示的是结果集的内容。

QSqlQueryModel中还提供了:

  • columnCount() 返回一条记录中字段的个数;
  • rowCount() 返回结果集中记录的条数;
  • record() 返回第n条记录;index()返回制定记录的指定字段的索引;
  • clear() 可以清空模型中的结果集。
  • query()函数,获取QSqlQuery对象,

有query()函数,可以使用上一节讲到的QSqlQuery的相关内容来操作数据库了.

还要注意一点,就是又使用setQuery()如果又使用setQuery()进行了新的查询, 比如进行了插入操作,这时要想视图中可以显示操作后的结果,那么就必须再次查询整张表, 也就是要同时执行下面两行代码:

self.model.setQuery(QString("insert into student values(5,'薛静',10)"))
self.model.setQuery("select * from student")
#!/usr/bin/env python
#coding=utf-8

'''
FileName : main.py
'''

from PyQt4.QtCore import QTextCodec
from PyQt4.QtGui import QApplication
import sys

from connection import createConnection
from mainwindow import MainWindow

if __name__ == "__main__":

    app = QApplication(sys.argv)
    #  这两行代码要写在创建连接之前
    QTextCodec.setCodecForTr(QTextCodec.codecForName("UTF-8"))
    QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))
    if not createConnection():
        raise Exception("createConnection faild")
    win = MainWindow()
    win.show()
    app.exec_()

2. SQL表格模型

QSqlTableModel 提供了一个一次只能操作一个SQL表的读写/模型,它是QSqlQuery的更高层次的替代品,可以浏览和修改独立的SQL表,并且只需编写很少的代码,而且不需要要了解SQL语法。该模型默认是可读写的,如果想让其成为只读的模型,那么可以从视图进行设置例如:

view.setEditTriggers(QAbstractItemView.NoEditTriggers)
alternate text
#!/usr/bin/env python
#coding=utf-8
from PyQt4.QtCore import QString
from PyQt4.QtGui import QMessageBox
from PyQt4.QtSql import QSqlDatabase,QSqlQuery

'''
FileName : connection.py
'''

def createConnection():

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("my.db")
    if (not db.open()):
        QMessageBox.critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox.Cancel)
        return False
    query = QSqlQuery()
    # 创建student表
    query.exec_(QString("create table student (id int primary key, "
                                       "name varchar, course int)"))
    query.exec_(QString("insert into student values(1, '李强', 11)"))
    query.exec_(QString("insert into student values(2, '马亮', 11)"))
    query.exec_(QString("insert into student values(3, '孙红', 12)"))

    # 创建course表
    query.exec_(QString("create table course (id int primary key, "
                                      "name varchar, teacher varchar)"))
    query.exec_(QString("insert into course values(10, '数学', '王老师')"))
    query.exec_(QString("insert into course values(11, '英语', '张老师')"))
    query.exec_(QString("insert into course values(12, '计算机', '白老师')"))
    return True


#!/usr/bin/env python
#coding=utf-8

'''
FileName : mainwindow.py
'''

from PyQt4.QtCore import QString,Qt,pyqtSlot
from PyQt4.QtGui import QMainWindow
from PyQt4.QtGui import QMessageBox
from PyQt4.QtSql import  QSqlTableModel
from PyQt4 import uic

class MainWindow(QMainWindow) :
    def __init__(self):
        super(MainWindow, self).__init__(None)
        uic.loadUi("mainwindow.ui",self)

        self.model =  QSqlTableModel(self)
        self.model.setTable("student")
        self.model.select()
        # 设置编辑策略
        self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.tableView.setModel(self.model)


    # 提交修改按钮
    @pyqtSlot()
    def on_pushButton_clicked(self):
        # 开始事务操作
        self.model.database().transaction() # 启动 事物操作
        if (self.model.submitAll()):
            self.model.database().commit() #提交
        else:
            self.model.database().rollback() #回滚
            QMessageBox.warning(self, self.tr("tableModel"),
                                 self.tr("数据库错误: %1").arg(self.model.lastError().text()))

    # 撤销修改按钮
    @pyqtSlot()
    def on_pushButton_2_clicked(self):

        self.model.revertAll()

    # 查询按钮,进行筛选
    @pyqtSlot()
    def on_pushButton_7_clicked(self):

        name = self.lineEdit.text()
        #根据姓名进行筛选,一定要使用单引号
        #model.setFilter(QString("name = '%1'").arg(name)) # filter 过滤器
        self.model.setFilter(QString("name LIKE '%%%1%%'").arg(name)) # filter过滤器,模糊查询
        self.model.select()

    # 显示全表按钮
    @pyqtSlot()
    def on_pushButton_8_clicked(self):

        self.model.setTable("student")
        self.model.select()

    # 按id升序排列按钮
    @pyqtSlot()
    def on_pushButton_5_clicked(self):

        #id属性,即第0列,升序排列
        self.model.setSort(0, Qt.AscendingOrder)
        self.model.select()

    # 按id降序排列按钮
    @pyqtSlot()
    def on_pushButton_6_clicked(self):

        self.model.setSort(0, Qt.DescendingOrder)
        self.model.select()

    # 删除选中行按钮
    @pyqtSlot()
    def on_pushButton_4_clicked(self):

        # 获取选中的行
        curRow = self.tableView.currentIndex().row()
        # 删除该行
        self.model.removeRow(curRow)
        ok = QMessageBox.warning(self,self.tr("删除当前行!"),
                      self.tr("你确定删除当前行吗?"),QMessageBox.Yes, QMessageBox.No)
        if(ok == QMessageBox.No):
         # 如果不删除,则撤销
            self.model.revertAll()
        else:  # 否则提交,在数据库中删除该行
            self.model.submitAll()

    # 添加记录按钮
    @pyqtSlot()
    def on_pushButton_3_clicked(self):

        # 获得表的行数
        rowNum = self.model.rowCount()
        id = 10
        # 添加一行
        self.model.insertRow(rowNum)
        self.model.setData(self.model.index(rowNum,0), id)
        # 可以直接提交
        #model.submitAll()

 使用setTable()来指定数据库表,然后使用select()函数进行查询, 调用这两个函数就等价于: “select * from student;” 这条SQL语句。

这里还可以使用 setFilter()来制定查询时的条件,在后面会看到这个函数的使用。 在使用该模型以前,一般还要设置其编辑策略, 它由QSqlTableModel::EditStrategy枚举变量定义,一共三个值,如表17-4所列。 用来说明当数据库中的值被编辑后,什么情况下被提交修改.

Constant Description
QSqlTableModel.OnFieldChange 所有对模型的改变都会立即应用到数据库
QSqlTableModel.OnRowChange 对一条记录的改变会在用户选择另一条记录时被应用
QSqlTableModel.OnManualSubmit 所有的改变都会在模型中进行缓存,直到调用submitAll()或者revertAll()函数
#!/usr/bin/env python
#coding=utf-8

'''
FileName : main.py
'''

import sys
from PyQt4.QtCore import QTextCodec
from PyQt4.QtGui import QApplication

from connection import createConnection
from mainwindow import MainWindow

if __name__ == "__main__":

    app = QApplication (sys.argv)

    # 这行代码要写在创建连接之前, 不然,数据库中文乱码
    QTextCodec.setCodecForTr(QTextCodec.codecForName("UTF-8"))
    QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))

    if not createConnection():
        raise Exception("createConnection faild")
    win = MainWindow()
    win.show()
    app.exec_()

3. SQL关系表格模型

QSqlRelationalTableModel继承自QSqlTableModel,并且对其进行扩展,提供了对外键的支持。一个外键是表中的一个字段和其他表中的主键字段之间的一对一的映射。例如:student表中的course字段对应的是表中的id字段,那么就称字段course是一个外键。因为这里是course字段的值是一些数字,这样显示很不友好,使用关系表格模型,就可以将它显示为course表中的name字段值。
alternate text
#!/usr/bin/env python
#coding=utf-8

'''
FileName : connection.py
'''

from PyQt4.QtCore import QString
from PyQt4.QtGui import QMessageBox
from PyQt4.QtSql import QSqlDatabase, QSqlQuery

def createConnection():

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("my.db")
    if (not db.open()):
        QMessageBox.critical(0, "Cannot open database1",
            "Unable to establish a database connection.", QMessageBox.Cancel)
        return False

    query =QSqlQuery()
    # 创建student表
    print query.exec_(QString("create table student (id int primary key, "
                                      "name varchar, course int)"))
    query.exec_(QString("insert into student values(1, '李强', 11)"))
    query.exec_(QString("insert into student values(2, '马亮', 11)"))
    query.exec_(QString("insert into student values(3, '孙红', 12)"))

    # 创建course表
    print query.exec_(QString("create table course (id int primary key, "
                                      "name varchar, teacher varchar)"))
    query.exec_(QString("insert into course values(10, '数学', '王老师')"))
    query.exec_(QString("insert into course values(11, '英语', '张老师')"))
    query.exec_(QString("insert into course values(12, '计算机', '白老师')"))
    return True


#!/usr/bin/env python
#coding=utf-8

from PyQt4.QtGui import QMainWindow,QTableView
from PyQt4.QtSql import QSqlRelationalTableModel, QSqlRelation, \
                        QSqlRelationalDelegate
'''
FileName : mainwindow.py
'''

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(None)

        model = QSqlRelationalTableModel(self)

        model.setTable("student")
        model.setRelation(2, QSqlRelation("course", "id", "name"))
        model.select()
        view = QTableView(self)
        view.setModel(model)
        self.setCentralWidget(view)

        view.setItemDelegate(QSqlRelationalDelegate(view))

这里的setRelation()函数用来在两个表之间创建一个关系, 其中参数”2”表示student表中的编号为2的列,即第三个字段course是一个外键, 他映射到了course表中的id字段, 而视图需要向用户显示course表中的name字段的值。

Qt中还提供了一个QSqlRelationalDelegate委托类, 他可以为 QSqlRelationalTableModel 显示和编辑数据,这个委托为一个外键提供了一个QComboBox部件,来显示所有可选的数据,这样显得更加人性化。 使用这个委托很简单,在后续构造函数中添加如下一行代码:


        view.setItemDelegate(QSqlRelationalDelegate(view))
#!/usr/bin/env python
#coding=utf-8


'''
FileName : main.py
'''

import sys
from connection import createConnection

from PyQt4.QtCore import QTextCodec
from PyQt4.QtGui import QApplication
from mainwindow import MainWindow

if __name__ == "__main__":


    app = QApplication (sys.argv)
    # 这行代码要写在创建连接之前
    QTextCodec.setCodecForTr(QTextCodec.codecForName("UTF-8"))
    QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))

    if not createConnection():
        raise Exception("createConnection faild")
    w = MainWindow()
    w.show()
    app.exec_()
可以根据自己的需要,来选择使用哪个模型。
如果熟悉SQL语法,又不需要将所有的数据都显示出来那么只需要使用QSqlQuery就可以了,
对于QSqlTableModel,它主要是用来显示一个单独表格的,
而QSqlQueryModel可以用来显示任意一个结果集,
如果想显示任意一个结果集,如果想显示任意一个结果集,
而且想使其可读/写,那么建议子类化QSqlQueryModel,然后,重新实现flags()和setData()函数。

这部分内容可以查看 Presenting Data in a Table View 关键字对应的帮助文档,
也可以参考 Query Model示例程序。
因为这3个模型都是基于模型/视图框架的,
所以前一章讲的内容在这里都可以使用,例如可以使用 QDataWidgetMapper等。
关于数据库部分的应用,还可以参考一下SQL分类中的几个示例程序.

17.2 XML

17.2.1 DOM

1. 使用DOM读取XML文档

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <book id="01">
        <title>Qt</title>
        <author>shiming</author>
    </book>
    <book id="02">
        <title>Linux</title>
        <author>yafei</author>
    </book>
</library>
#include <QCoreApplication>
#include <QtXml>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 新建QDomDocument类对象,它代表一个XML文档
    QDomDocument doc;
    QFile file("../myDOM1/my.xml");
    if (!file.open(QIODevice::ReadOnly)) return 0;
    // 将文件内容读到doc中
    if (!doc.setContent(&file)) {
        file.close();
        return 0;
    }
    // 关闭文件
    file.close();

    /* firstChild 获得doc的第一个结点,即XML说明 */
    QDomNode firstNode = doc.firstChild();
    /* 输出XML说明,nodeName()为“xml”,nodeValue()为版本和编码信息 */
    qDebug() << qPrintable(firstNode.nodeName())  /* */
                     << qPrintable(firstNode.nodeValue());

    // 返回根元素
    QDomElement docElem = doc.documentElement();
    // 返回根节点的第一个子结点
    QDomNode n = docElem.firstChild();
//    qDebug() <<":" <<  n.nodeName() <<  n.nodeValue() ;
    // 如果结点不为空,则转到下一个节点
    while(!n.isNull())
    {
        if (n.isElement())  /* 如果结点是元素 */
        {
            QDomElement e = n.toElement();  // 将其转换为元素
            // 返回元素标记和id属性的值
            qDebug() << qPrintable(e.tagName())
                             << qPrintable(e.attribute("id"));

            // 获得元素e的所有子结点的列表
            QDomNodeList list = e.childNodes();
            // 遍历该列表
            for(int i=0; i<list.count(); i++)
            {
                QDomNode node = list.at(i);
                if(node.isElement())
                    qDebug() << "   "<< qPrintable(node.toElement().tagName())
                                                 <<qPrintable(node.toElement().text());
            }
        }
        // 转到下一个兄弟结点
        n = n.nextSibling();
    }

//    return a.exec();
}
#/usr/bin/env python
#coding=utf-8
from PyQt4.QtCore import QFile, QIODevice
from PyQt4.QtGui import QApplication
from PyQt4.QtXml import QDomDocument
import sys

if __name__ == "__main__":

    app = QApplication(sys.argv)

    # 新建QDomDocument类对象,它代表一个XML文档
    doc = QDomDocument()
    _file = QFile("../myDOM1/my.xml")
    if not _file.open(QIODevice.ReadOnly):
        raise Exception("err")

    # 将文件内容读到doc中
    status, rrorMsg, errorLine, errorColumn = doc.setContent(_file)
    if not status:
        _file.close()
        raise Exception("err")
    _file.close()

    # firstChild 获得doc的第一个结点,即XML说明
    firstNode = doc.firstChild()

    #  输出XML说明,nodeName()为“xml”,nodeValue()为版本和编码信息
    print   firstNode.nodeName(), ":  ", firstNode.nodeValue()


    docelement = doc.documentElement()  # 返回根元素

    n = docelement.firstChild()  # 返回根节点的第一个子结点

    while (not n.isNull()):           #  如果结点不为空,则转到下一个节点
        if (n.isElement()):           # 如果结点是元素
                e = n.toElement()     # 将其转换为元素
                                      # 返回元素标记和id属性的值
        print  e.tagName() ,",  ",e.attribute("id")
        list = e.childNodes()        # 获得元素e的所有子结点的列表

        for i in  range(list.count()):  # // 遍历该列表
            node = list.at(i)
            if (node.isElement()):
                print  "   " ,node.toElement().tagName(), "   ",node.toElement().text()
        n = n.nextSibling()  # // 转到下一个兄弟结点

2. 使用DOM创建和操作XML文档

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtXml>
#include <QFile>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QDomDocument doc;
    // 添加处理指令即XML说明
    QDomProcessingInstruction instruction;
    instruction = doc.createProcessingInstruction("xml",
                                                  "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(instruction);

    // 添加根元素
    QDomElement root = doc.createElement(QString("书库"));
    doc.appendChild(root);       /*  添加根元素  */

    // 添加第一个图书元素及其子元素
    QDomElement book = doc.createElement(QString("图书"));
    QDomAttr id = doc.createAttribute(QString("编号"));
    QDomElement title = doc.createElement(QString("书名"));
    QDomElement author = doc.createElement(QString("作者"));
    QDomText text;

    id.setValue(QString("1"));
    book.setAttributeNode(id);
    text = doc.createTextNode(QString("Qt"));
    title.appendChild(text);
    text = doc.createTextNode(QString("shiming"));
    author.appendChild(text);
    book.appendChild(title);        /* 图书元素 添加 书名元素 */
    book.appendChild(author);   /* 图书元素 添加 作者元素 */
    root.appendChild(book);        /* 根元素 添加 图书元素 */

    // 添加第二个图书元素及其子元素
    book = doc.createElement(QString("图书"));
    id = doc.createAttribute(QString("编号"));
    title = doc.createElement(QString("书名"));
    author = doc.createElement(QString("作者"));

    id.setValue(QString("2"));
    book.setAttributeNode(id);
    text = doc.createTextNode(QString("Linux"));
    title.appendChild(text);
    text = doc.createTextNode(QString("yafei"));
    author.appendChild(text);
    book.appendChild(title);
    book.appendChild(author);
    root.appendChild(book);

    QFile file("my.xml");
    if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return ;
    QTextStream out(&file);
    doc.save(out, 4);      // 将文档保存到文件,4为子元素缩进字符数
    file.close();
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* 显示全部按钮 */
void MainWindow::on_pushButton_5_clicked()
{

    ui->listWidget->clear();   // 先清空显示
    QFile file("my.xml");
    if (!file.open(QIODevice::ReadOnly)) return ;
    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return ;
    }
    file.close();

    QDomElement docElem = doc.documentElement();  // 返回根元素

    QDomNode n = docElem.firstChild();
    while(!n.isNull()) {
            if (n.isElement()) {
                        QDomElement e = n.toElement();
                        ui->listWidget->addItem(e.tagName() + e.attribute(QString("编号")));
                        QDomNodeList list = e.childNodes();
                        for(int i=0; i<list.count(); i++) {
                                    QDomNode node = list.at(i);
                                    if(node.isElement())
                                        ui->listWidget->addItem("   " + node.toElement().tagName()
                                                                + " : " + node.toElement().text());
                        }
            }
            n = n.nextSibling();
    }
}

// 添加按钮
void MainWindow::on_pushButton_4_clicked()
{
    // 我们先清空显示,然后显示“无法添加!”,这样如果添加失败则会显示“无法添加!”
    ui->listWidget->clear();
    ui->listWidget->addItem(QString("无法添加!"));
    QFile file("my.xml");
    if (!file.open(QIODevice::ReadOnly)) return;
    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return;
    }
    file.close();
    QDomElement root = doc.documentElement(); // 获取根元素

    QDomElement book = doc.createElement(QString("图书"));
    QDomAttr id = doc.createAttribute(QString("编号"));
    QDomElement title = doc.createElement(QString("书名"));
    QDomElement author = doc.createElement(QString("作者"));
    QDomText text;

    // 我们获得了最后一个孩子结点的编号,然后加1,便是新的编号
    QString num = root.lastChild().toElement().attribute(QString("编号"));
    int count = num.toInt() +1;
    id.setValue(QString::number(count));

    book.setAttributeNode(id);
    text = doc.createTextNode(ui->lineEdit_2->text());
    title.appendChild(text);
    text = doc.createTextNode(ui->lineEdit_3->text());
    author.appendChild(text);
    book.appendChild(title);
    book.appendChild(author);
    root.appendChild(book);

    if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return ;
    QTextStream out(&file);
    doc.save(out, 4);
    file.close();
    // 最后更改显示为“添加成功!”
    ui->listWidget->clear();
    ui->listWidget->addItem(QString("添加成功!"));
}

// 对XML文档进行查找、更新和删除操作
void MainWindow::doXml(const QString operate)
{
    ui->listWidget->clear();
    ui->listWidget->addItem(QString("没有找到相关内容!"));
    QFile file("my.xml");
    if (!file.open(QIODevice::ReadOnly)) return ;
    QDomDocument doc;
    if (!doc.setContent(&file)) {
        file.close();
        return ;
    }
    file.close();

    // 以标签名进行查找
    QDomNodeList list = doc.elementsByTagName(QString("图书")); // 获取所有图书元素的列表

    for(int i=0; i<list.count(); i++) {
        QDomElement e = list.at(i).toElement();
        if(e.attribute(QString("编号")) == ui->lineEdit->text())
        {   // 如果元素的“编号”属性值与我们所查的相同
            if(operate == "delete")  { // 如果是删除操作
                QDomElement root = doc.documentElement();
                // 从根节点上删除该节点
                root.removeChild(list.at(i));
                QFile file("my.xml");
                if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
                    return ;
                QTextStream out(&file);
                doc.save(out,4);
                file.close();
                ui->listWidget->clear();
                ui->listWidget->addItem(QString("删除成功!"));
            } else if(operate == "update") {
                // 如果是更新操作
                QDomNodeList child = list.at(i).childNodes();
                // 将它子节点的首个子节点(就是文本节点)的内容更新
                child.at(0).toElement().firstChild()
                        .setNodeValue(ui->lineEdit_2->text());
                child.at(1).toElement().firstChild()
                        .setNodeValue(ui->lineEdit_3->text());
                QFile file("my.xml");
                if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
                    return ;
                QTextStream out(&file);
                doc.save(out,4);
                file.close();
                ui->listWidget->clear();
                ui->listWidget->addItem(QString("更新成功!"));
            } else if(operate == "find")  {
                // 如果是查找操作
                ui->listWidget->clear();
                ui->listWidget->addItem(e.tagName()
                                        + e.attribute(QString("编号")));
                QDomNodeList list = e.childNodes();
                for(int i=0; i<list.count(); i++)
                {
                    QDomNode node = list.at(i);
                    if(node.isElement())
                        ui->listWidget->addItem("   "
                                                + node.toElement().tagName() + " : "
                                                + node.toElement().text());
                }
            }
        }
    }
}

// 查找按钮
void MainWindow::on_pushButton_clicked()
{
    doXml("find");
}

// 删除按钮
void MainWindow::on_pushButton_2_clicked()
{
    doXml("delete");
}

// 更新按钮
void MainWindow::on_pushButton_3_clicked()
{
    doXml("update");
}
#include <QApplication>
#include "mainwindow.h"
#include <QTextCodec>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
    MainWindow w;
    w.show();

    return a.exec();
}

PyQt4

#!/usr/bin/env python
#coding=utf-8

#include <QApplication>
#include "mainwindow.h"
#include <QTextCodec>

# int main(int argc, char *argv[])
import sys

from PyQt4.QtCore import QTextCodec
from PyQt4.QtGui import QApplication
from mainwindow import MainWindow

if __name__ == "__main__":

    app = QApplication(sys.argv)
    QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))

    win = MainWindow()
    win.show()
    app.exec_()

#!/usr/bin/env python
# coding=utf-8

from PyQt4 import uic
from PyQt4.QtCore import pyqtSlot
from PyQt4.QtCore import QFile
from PyQt4.QtCore import QIODevice
from PyQt4.QtCore import QString
from PyQt4.QtCore import QTextStream
from PyQt4.QtGui import QDialog
from PyQt4.QtXml import QDomDocument
from PyQt4.QtXml import QDomProcessingInstruction
from PyQt4.QtXml import QDomText


class MainWindow(QDialog):
    def __init__(self):
        super(MainWindow, self).__init__(None)
        uic.loadUi("./mainwindow.ui",self)

        doc = QDomDocument()
        # 添加处理指令即XML说明
        instruction = QDomProcessingInstruction()
        instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"")
        doc.appendChild(instruction)

        # 添加根元素
        root = doc.createElement(QString("书库"))
        doc.appendChild(root)         # 添加根元素

        # 添加第一个图书元素及其子元素
        book = doc.createElement(QString("图书"))
        id = doc.createAttribute(QString("编号"))
        title = doc.createElement(QString("书名"))
        author = doc.createElement(QString("作者"))
        text = QDomText()

        id.setValue(QString("1"))
        book.setAttributeNode(id)
        text = doc.createTextNode(QString("Qt"))
        title.appendChild(text)
        text = doc.createTextNode(QString("shiming"))
        author.appendChild(text)
        book.appendChild(title)   # 图书元素 添加 书名元素
        book.appendChild(author)  # 图书元素 添加 作者元素
        root.appendChild(book)  # 根元素 添加 图书元素

        # 添加第二个图书元素及其子元素
        book = doc.createElement(QString("图书"))
        id = doc.createAttribute(QString("编号"))
        title = doc.createElement(QString("书名"))
        author = doc.createElement(QString("作者"))

        id.setValue(QString("2"))
        book.setAttributeNode(id)
        text = doc.createTextNode(QString("Linux"))
        title.appendChild(text)
        text = doc.createTextNode(QString("yafei"))
        author.appendChild(text)
        book.appendChild(title)
        book.appendChild(author)
        root.appendChild(book)

        file = QFile("my.xml")
        if (not file.open(QIODevice.WriteOnly | QIODevice.Truncate)):
            raise Exception("open my.xml Err")
        out = QTextStream(file)
        doc.save(out, 4)  # 将文档保存到文件,4为子元素缩进字符数
        file.close()

    # # MainWindow::~MainWindow()
    #
    #     delete ui
    #

    # /* 显示全部按钮 */
    def on_pushButton_5_clicked(self):

        self.listWidget.clear()  # 先清空显示

        file = QFile("my.xml")
        if (not file.open(QIODevice.ReadOnly)):
            raise Exception("open my.xml Err")
        doc = QDomDocument()
        status, rrorMsg, errorLine, errorColumn = doc.setContent(file)
        if (not status):
            file.close()
            raise Exception(str(rrorMsg))
        file.close()

        docElem = doc.documentElement()  # 返回根元素

        n = docElem.firstChild()
        while (not n.isNull()):
            if (n.isElement()):
                e = n.toElement()
                self.listWidget.addItem(e.tagName() + e.attribute(QString("编号")))
                list = e.childNodes()
                for i in range(list.count()):
                    node = list.at(i)
                    if (node.isElement()):
                        self.listWidget.addItem("   " + node.toElement().tagName()
                                                + " : " + node.toElement().text())
            n = n.nextSibling()

    # 添加按钮
    def on_pushButton_4_clicked(self):

        # 我们先清空显示,然后显示“无法添加!”,这样如果添加失败则会显示“无法添加!”
        self.listWidget.clear()
        self.listWidget.addItem(QString("无法添加!"))
        file = QFile("my.xml")
        if (not file.open(QIODevice.ReadOnly)):
            raise Exception("open my.xml Err")
        doc = QDomDocument()

        status, rrorMsg, errorLine, errorColumn = doc.setContent(file)
        if not status:
            file.close()
            raise Exception(str(rrorMsg))

        file.close()
        root = doc.documentElement()  # 获取根元素
        book = doc.createElement(QString("图书"))
        id = doc.createAttribute(QString("编号"))
        title = doc.createElement(QString("书名"))
        author = doc.createElement(QString("作者"))
        text = QDomText()

        # 我们获得了最后一个孩子结点的编号,然后加1,便是新的编号
        num = root.lastChild().toElement().attribute(QString("编号"))
        count = num.toInt() + 1
        id.setValue(QString.number(count))

        book.setAttributeNode(id)
        text = doc.createTextNode(self.lineEdit_2.text())
        title.appendChild(text)
        text = doc.createTextNode(self.lineEdit_3.text())
        author.appendChild(text)
        book.appendChild(title)
        book.appendChild(author)
        root.appendChild(book)

        if (not file.open(QIODevice.WriteOnly | QIODevice.Truncate)):
            raise Exception("file open Err")

        out = QTextStream(file)
        doc.save(out, 4)
        file.close()
        # 最后更改显示为“添加成功!”
        self.listWidget.clear()
        self.listWidget.addItem(QString("添加成功!"))

    # 对XML文档进行查找、更新和删除操作
    def doXml(self, operate):

        self.listWidget.clear()
        self.listWidget.addItem(QString("没有找到相关内容!"))
        file = QFile("my.xml")
        if (not file.open(QIODevice.ReadOnly)):
            raise Exception("open file Err")
        doc = QDomDocument()
        status, rrorMsg, errorLine, errorColumn = doc.setContent(file)
        if (not status):
            file.close()
            raise Exception(str(rrorMsg))
        file.close()

        # 以标签名进行查找
        list = doc.elementsByTagName(QString("图书"))  # 获取所有图书元素的列表

        for i in range(list.count()):
            e = list.at(i).toElement()
            if (e.attribute(QString("编号")) == self.lineEdit.text()):
                # 如果元素的“编号”属性值与我们所查的相同
                if (operate == "delete"):  # 如果是删除操作
                    root = doc.documentElement()
                    # 从根节点上删除该节点
                    root.removeChild(list.at(i))
                    file = QFile("my.xml")
                    if (not file.open(QIODevice.WriteOnly | QIODevice.Truncate)):
                        raise Exception("open file Err")
                    out = QTextStream(file)
                    doc.save(out, 4)
                    file.close()
                    self.listWidget.clear()
                    self.listWidget.addItem(QString("删除成功!"))
                elif operate is "update":
                    # 如果是更新操作
                    child = list.at(i).childNodes()
                    # 将它子节点的首个子节点(就是文本节点)的内容更新
                    child.at(0).toElement().firstChild().setNodeValue(self.lineEdit_2.text())
                    child.at(1).toElement().firstChild().setNodeValue(self.lineEdit_3.text())
                    file = QFile("my.xml")
                    if (not file.open(QIODevice.WriteOnly | QIODevice.Truncate)):
                        raise Exception("Open file Err")
                    out = QTextStream(file)
                    doc.save(out, 4)
                    file.close()
                    self.listWidget.clear()
                    self.listWidget.addItem(QString("更新成功!"))
                elif operate is "find":
                    # 如果是查找操作
                    self.listWidget.clear()
                    self.listWidget.addItem(e.tagName() + e.attribute(QString("编号")))
                    list = e.childNodes()
                    for i in range(list.count()):

                        node = list.at(i)
                        if (node.isElement()):
                            self.listWidget.addItem("   "
                                                    + node.toElement().tagName() + " : "
                                                    + node.toElement().text())
    # 查找按钮
    @pyqtSlot()
    def on_pushButton_clicked(self):
        self.doXml("find")

    # 删除按钮
    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        self.fdoXml("delete")

    # 更新按钮
    @pyqtSlot()
    def on_pushButton_3_clicked(self):
        self.doXml("update")

17.2.2 SAX

#include "mysax.h"
#include <QtXml>
#include <QListWidget>

MySAX::MySAX()
{
    list = new QListWidget;
    list->show();
}

MySAX::~MySAX()
{
    delete list;
}

bool MySAX::readFile(const QString &fileName)
{
    QFile file(fileName);
    // 读取文件内容
    QXmlInputSource inputSource(&file);
    // 建立QXmlSimpleReader对象
    QXmlSimpleReader reader;
    // 设置内容处理器
    reader.setContentHandler(this);
    // 设置错误处理器
    reader.setErrorHandler(this);
    // 解析文件
    return reader.parse(inputSource);
}

// 已经解析完一个元素的起始标签
bool MySAX::startElement(const QString &namespaceURI, const QString &localName,
                         const QString &qName, const QXmlAttributes &atts)
{
    if (qName == "library")
        list->addItem(qName);
    else if (qName == "book")
        list->addItem("    " + qName + atts.value("id"));
    return true;
}

// 已经解析完一块字符数据
bool MySAX::characters(const QString &ch)
{
    currentText = ch;
    return true;
}

// 已经解析完一个元素的结束标签
bool MySAX::endElement(const QString &namespaceURI, const QString &localName,
                       const QString &qName)
{
    if (qName == "title" || qName == "author")
        list->addItem("        " + qName + " : " + currentText);
    return true;
}

// 错误处理
bool MySAX::fatalError(const QXmlParseException &exception)
{
    qDebug() << exception.message();
    return false;
}
#include "mysax.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    MySAX sax;
    sax.readFile("../mySAX/my.xml");
    return app.exec();
}

17.2.3 XML流

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("../myXmlStream/my.xml");
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        qDebug()<<"Error: cannot open file";
        return 1;
    }

    QXmlStreamReader reader;

    // 设置文件,这时会将流设置为初始状态
    reader.setDevice(&file);

    // 如果没有读到文档结尾,而且没有出现错误
    while (!reader.atEnd()) {
        // 读取下一个记号,它返回记号的类型
        QXmlStreamReader::TokenType type = reader.readNext();

        // 下面便根据记号的类型来进行不同的输出
        if (type == QXmlStreamReader::StartDocument)
            qDebug() << reader.documentEncoding() << reader.documentVersion();

        if (type == QXmlStreamReader::StartElement) {
            qDebug() << "<" << reader.name() << ">";
            if (reader.attributes().hasAttribute("id"))
                qDebug() << reader.attributes().value("id");
        }

        if (type == QXmlStreamReader::EndElement)
            qDebug() << "</" << reader.name() << ">";

        if (type == QXmlStreamReader::Characters && !reader.isWhitespace())
            qDebug() << reader.text();
    }

    // 如果读取过程中出现错误,那么输出错误信息
    if (reader.hasError()) {
        qDebug() << "error: " << reader.errorString();
    }

    file.close();

    return a.exec();
}
#include <QCoreApplication>
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("../myXmlStream/my2.xml");
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        qDebug() << "Error: cannot open file";
        return 1;
    }

    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true);
    stream.writeStartDocument();   /* 添加  XML 说明:
                                                            <?xml version="1.0" encoding="UTF-8"?>
                                                            */
    stream.writeStartElement("bookmark");
    stream.writeAttribute("href", "http://qt.nokia.com/");
    stream.writeTextElement("title", "Qt Home");
    stream.writeEndElement();
    stream.writeEndDocument();

    file.close();

    qDebug() << "write finished!";

    return a.exec();
}