警告

本节包含从 C++ 自动翻译到 Python 的代码片段,可能存在错误。

缓存 SQL 表#

缓存表示例展示了如何使用表格视图来访问数据库,并为数据的变化提供缓存,直到用户明确地使用推送按钮提交它们。

../_images/cachedtable-example.png

示例由一个类组成,TableEditor,这是一个自定义对话框小控件,允许用户修改数据库中存储的数据。我们首先将回顾类定义和使用方法,然后我们将查看实现。

TableEditor 类定义#

TableEditor 类继承 QWidget,使表格编辑器小控件成为一个顶级对话框窗口。

class TableEditor(QWidget):

    Q_OBJECT
# public
    TableEditor = explicit(QString tableName, QWidget parent = None)
# private slots
    def submit():
# private
    submitButton = QPushButton()
    revertButton = QPushButton()
    quitButton = QPushButton()
    buttonBox = QDialogButtonBox()
    model = QSqlTableModel()

TableEditor 构造函数接受两个参数:一个是 TableEditor 对象将操作的数据表引用。另一个是一个指向父小控件的指针,并将其传递给基类构造函数。

注意 QSqlTableModel 变量的声明:如本例所示,QSqlTableModel 类可用作为像 QTableView 这样的视图类提供数据。QSqlTableModel 类提供了一个可编辑的数据模型,这使得可以从单个表中读取和写入数据库记录。它建立在较低级别的 QSqlQuery 类之上,该类提供了执行和操作 SQL 语句的方法。

我们还将展示如何使用表格视图缓存任何数据变更,直到用户明确地请求提交它们。因此,我们需要在模型和编辑器按钮之外声明一个 submit() 槽。

连接到数据库

在我们可以使用 TableEditor 类之前,我们必须创建一个连接到包含我们想要编辑的表的数据库

if __name__ == "__main__":

    app = QApplication([])
    if not createConnection():
        return 1
    editor = TableEditor("person")
    editor.show()
    sys.exit(app.exec())

createConnection() 函数是一个提供的便利辅助函数。它定义在 connection.h 文件中,该文件位于 sql 示例目录中(sql 目录中的所有示例都使用此函数连接到数据库)。

def createConnection():

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(":memory:")
    if not db.open():
        QMessageBox.critical(None, QObject.tr("Cannot open database"),
            QObject.tr("Unable to establish a database connection.\n"
                        "This example needs SQLite support. Please read "
                        "the Qt SQL driver documentation for information how "
                        "to build it.\n\n"
                        "Click Cancel to exit."), QMessageBox.Cancel)
        return False

    query = QSqlQuery()
    query.exec("create table person (id int primary key, "
               "firstname varchar(20), lastname varchar(20))")
    query.exec("insert into person values(101, 'Danny', 'Young')")
    query.exec("insert into person values(102, 'Christine', 'Holand')")
    query.exec("insert into person values(103, 'Lars', 'Gordon')")
    query.exec("insert into person values(104, 'Roberto', 'Robitaille')")
    query.exec("insert into person values(105, 'Maria', 'Papadopoulos')")
    query.exec("create table items (id int primary key,"
                                             "imagefile int,"
                                             "itemtype varchar(20),"
                                             "description varchar(100))")
    query.exec("insert into items "
               "values(0, 0, 'Qt',"
               "'Qt is a full development framework with tools designed to "
               "streamline the creation of stunning applications and "
               "amazing user interfaces for desktop, embedded and mobile "
               "platforms.')")
    query.exec("insert into items "
               "values(1, 1, 'Qt Quick',"
               "'Qt Quick is a collection of techniques designed to help "
               "developers create intuitive, modern-looking, and fluid "
               "user interfaces using a CSS  JavaScript like language.')")
    query.exec("insert into items "
               "values(2, 2, 'Qt Creator',"
               "'Qt Creator is a powerful cross-platform integrated "
               "development environment (IDE), including UI design tools "
               "and on-device debugging.')")
    query.exec("insert into items "
               "values(3, 3, 'Qt Project',"
               "'The Qt Project governs the open source development of Qt, "
               "allowing anyone wanting to contribute to join the effort "
               "through a meritocratic structure of approvers and "
               "maintainers.')")
    query.exec("create table images (itemid int, file varchar(20))")
    query.exec("insert into images values(0, 'images/qt-logo.png')")
    query.exec("insert into images values(1, 'images/qt-quick.png')")
    query.exec("insert into images values(2, 'images/qt-creator.png')")
    query.exec("insert into images values(3, 'images/qt-project.png')")
    return True

createConnection 函数打开一个连接到内存中的 SQLITE 数据库并创建一个测试表。如果你希望使用其他数据库,只需修改此函数的代码。

TableEditor 类实现#

类实现只包含两个函数:构造函数和submit()槽。在构造函数中,我们创建并定制数据模型和各种窗口元素。

def __init__(self, tableName, parent):
    super().__init__(parent)

    model = QSqlTableModel(self)
    model.setTable(tableName)
    model.setEditStrategy(QSqlTableModel.OnManualSubmit)
    model.select()
    model.setHeaderData(0, Qt.Horizontal, tr("ID"))
    model.setHeaderData(1, Qt.Horizontal, tr("First name"))
    model.setHeaderData(2, Qt.Horizontal, tr("Last name"))

首先,我们创建数据模型,并设置模型要操作的两个SQL数据库表。请注意,setTable()函数不会从表中选择数据;它只获取其字段信息。因此,我们稍后调用select()函数,用表中的数据填充模型。选择可以通过指定过滤器和排序条件进行自定义(详见QSqlTableModel类文档以获取更多详细信息)。

我们还设置了模型的编辑策略。编辑策略决定了用户对视图所做的更改何时真正应用于数据库。由于我们希望CACHE表视图(即模型)中直到用户明确提交,我们选择OnManualSubmit策略。其他选择是OnFieldChangeOnRowChange

最后,我们使用从QSqlQueryModel类继承的setHeaderData()函数设置视图标题中显示的标签。

view = QTableView()
view.setModel(model)
view.resizeColumnsToContents()

然后我们创建一个表格视图。QTableView类提供了一个默认的模型/视图实现,即在模型中显示项的表格视图。它还允许用户编辑项,并将更改存储在模型中。要创建只读视图,请使用从QAbstractItemView类继承的editTriggers属性设置正确的标志。

为了使视图呈现我们的数据,我们使用setModel()函数将我们的模型传送给视图。

submitButton = QPushButton(tr("Submit"))
submitButton.setDefault(True)
revertButton = QPushButton(tr("Revert"))
quitButton = QPushButton(tr("Quit"))
buttonBox = QDialogButtonBox(Qt.Vertical)
buttonBox.addButton(submitButton, QDialogButtonBox.ActionRole)
buttonBox.addButton(revertButton, QDialogButtonBox.ActionRole)
buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole)

TableEditor的按钮是常规QPushButton对象。我们把它们添加到按钮框中以确保按钮以适合当前部件样式的布局呈现。这样做的原因是,对话框和信息框通常按照相应平台的接口指南呈现按钮。不同平台的对话框布局各不相同。QDialogButtonBox允许开发者向它添加按钮,并会自动使用适合用户的桌面环境的布局。

对话框的大多数按钮遵循某些角色。在通过addButton()函数向按钮框添加按钮时,必须使用QDialogButtonBox::ButtonRole枚举指定按钮的角色。或者,QDialogButtonBox提供了一些标准按钮(例如:OK、Cancel、Save),您可以使用它们。它们是作为标志存在的,因此您可以在构造函数中将它们进行按位或运算。

submitButton.clicked.connect(self.submit)
revertButton.clicked.connect(model.revertAll)
quitButton.clicked.connect(self.close)

我们将退出按钮连接到表格编辑器的close()槽,将提交按钮连接到我们的私有submit()槽。后者将负责数据处理。最后,我们将还原按钮连接到我们模型中的revertAll()槽,撤销所有待处理的更改(即恢复原始数据)。

mainLayout = QHBoxLayout()
mainLayout.addWidget(view)
mainLayout.addWidget(buttonBox)
setLayout(mainLayout)
setWindowTitle(tr("Cached Table"))

最终,我们将按钮框和表格视图添加到布局中,将布局安装到表格编辑器小部件上,并设置编辑器的窗口标题。

def submit(self):

    model.database().transaction()
    if model.submitAll():
        model.database().commit()
    else:
        model.database().rollback()
        QMessageBox.warning(self, tr("Cached Table"),
                             tr("The database reported an error: %1")
                             .arg(model.lastError().text()))

当用户点击提交按钮保存更改时,会调用submit()槽。

首先,我们使用transaction()函数在数据库上开始一个事务。数据库事务是数据库管理系统或类似系统交互的单位,独立于其他事务被统一和可靠处理。可以使用database()函数获取所使用数据库的指针。

然后,我们尝试提交所有待处理的更改,即模型中的修改项。如果没有发生错误,使用commit()函数将事务提交到数据库(请注意,在某些数据库上,如果数据库中存在活动QSqlQuery,此函数可能不会工作)。否则,我们将使用rollback()函数回滚事务,并向用户发布警告。

另请参阅

Qt的SQL数据库类的完整列表,以及模型/视图编程文档。

示例项目 @ code.qt.io