从 QtSQL 创建离线/断开数据的便捷方式?

Posted

技术标签:

【中文标题】从 QtSQL 创建离线/断开数据的便捷方式?【英文标题】:Convenient Way to Create Offline/Disconnected data from QtSQL? 【发布时间】:2020-06-09 16:56:36 【问题描述】:

我试图弄清楚如何从数据库中获取 2D 数据并将其填充到小部件中。这个数据是准静态的——一旦我抓住它,就不需要保持与数据库的连接。此外,如果我让连接保持打开状态,但它超时,它可能会使我的应用程序崩溃。我想知道 QtSql 中是否有某种我不知道的离线数据容器或功能。

据我所知,Qt 仅提供 QsqlTableModel 和 QAbstractTableModel 作为数据容器。如果连接断开,我还没有找到任何将数据保存在 QsqlTableModel 中的方法。并且 QAbstractTableModel 甚至不能单独使用;你必须继承它。如果我找不到更简单或优雅的解决方案,我很可能最终会走子类化路线。有一个subclassing example here。

通过代码示例,下面的代码填充了 SQL Server 数据库中的两个组合框。单击第二个按钮并填充第二个组合框时,第一个组合框会中断,应用程序也会中断。我希望有一种简单的方法来获取数据并将其保存在与数据库断开连接的本地容器中。

from PyQt5.QtWidgets import (QApplication, QMainWindow, QComboBox, QPushButton,
                             QTableView, QTableView)
from PyQt5.QtSql import (QSqlQuery, QSqlQueryModel, QSqlDatabase)
import sys


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

        self.setGeometry(300, 300, 600, 350)

        self.db = QSqlDatabase.addDatabase("QODBC", 'MyDB')
        self.db.setDatabaseName('Driver=SQL Server;Server=MyServer;Database=MyDB;Trusted_Connection=Yes;')

        self.cb1 = QComboBox(parent=self)
        self.cb1.setGeometry(25,25, 250, 50)

        self.cb2 = QComboBox(parent=self)
        self.cb2.setGeometry(300,25, 250, 50)

        self.button1 = QPushButton('^^^ Fill Table 1 ^^^', parent=self)
        self.button1.setGeometry(55,290, 200, 30)
        self.button1.clicked.connect(self.fillTable1)

        self.button2 = QPushButton('^^^ Fill Table 2 ^^^', parent=self)
        self.button2.setGeometry(320, 290, 200, 30)
        self.button2.clicked.connect(self.fillTable2)

    def fillTable1(self):
        print('self.db.open() ', self.db.open())
        sql = 'select * from aaa.car limit 10'
        query = QSqlQuery(self.db)
        print("query.exec_(sql) ", query.exec_(sql))

        self.t1model = QSqlQueryModel(parent = self)
        self.t1model.setQuery(query)
        self.cb1.setModel(self.t1model)
        self.cb1.setModelColumn(0)
        self.cb1.setView(QTableView(self.cb1))

    def fillTable2(self):
        print('self.db.open() ', self.db.open())
        sql = 'select * from aaa.car limit 10'
        query = QSqlQuery(self.db)
        print("query.exec_(sql) ", query.exec_(sql))
        self.t2model = QSqlQueryModel(parent = self)
        self.t2model.setQuery(query)
        self.cb2.setModel(self.t2model)
        self.cb2.setModelColumn(0)
        self.cb2.setView(QTableView(self.cb2))


app = QApplication(sys.argv)
main = MainWindow(None)
main.show()
sys.exit(app.exec_())

【问题讨论】:

为什么不在 sqlite 中创建 SQL server 表的副本,然后使用 sqlite? 呃。这是一种解决方案,但它有很多开销。我必须为要运行的每个查询创建新表。可能是子类化 QAbstractTableModel 是最少的工作/开销。 我的逻辑是:创建表的镜像副本,并在必要时更新(例如,如果原始表发生更改) 【参考方案1】:

这是一个准系统的子类 QAbstractTableModel。你在 fillFromQuery 方法中给它一个 QSqlQuery,它从查询结果中提取数据。 正如所写,它不能很好地与 QCompleter 配合使用已编辑。现在可以与 QCompleter 一起使用

from PyQt5.QtCore import Qt, QAbstractTableModel
from PyQt5.QtSql import QSqlQueryModel

class OfflineTableModel(QAbstractTableModel):
def __init__(self, parent, inputQuery=None):
    QAbstractTableModel.__init__(self, parent)
    self.mylist = []
    self.header = []
    
    if inputQuery is not None:
        self.fillFromQuery(inputQuery)

def fillFromQuery(self, inputQuery):
    # don't know how to get row/column count except from a QSqlQueryModel
    bogusModel = QSqlQueryModel()
    bogusModel.setQuery(inputQuery)
    rowCount = bogusModel.rowCount()
    colCount = bogusModel.columnCount()

    inputQuery.first()
    
    self.header = []
    for col in range(colCount):
        self.header.append(inputQuery.record().fieldName(col))

    self.mylist = []
    for row in range(rowCount):
        innerList = []
        for col in range(colCount):
            innerList.append(inputQuery.value(col))
        self.mylist.append(tuple(innerList))
        inputQuery.next()

def rowCount(self, parent=None):
    return len(self.mylist)
def columnCount(self, parent=None):
    return len(self.mylist[0])
def data(self, index, role):
    if not index.isValid():
        return None
    elif role not in (Qt.EditRole, Qt.DisplayRole):
        return None
    return self.mylist[index.row()][index.column()]
def dataRowCol(self, row, col):
    return self.mylist[row][col]
def headerData(self, col, orientation, role):
    if orientation == Qt.Horizontal and role not in (Qt.EditRole, Qt.DisplayRole):
        return self.header[col]
    return None

与创建本地 Sqlite 数据库相比,此技术的优势在于,此 OfflineTableModel 将通过任何 SQL SELECT 查询向视图小部件提供离线数据。

当这个 OfflineTable 模型应用到 OP 时,原来的 QSqlDatabase (db) 可以多次打开和关闭,而不会破坏已经加载了离线数据的小部件。 41 行代码。似乎应该有一种更简单的方法来完成同样的事情。 . .

【讨论】:

以上是关于从 QtSQL 创建离线/断开数据的便捷方式?的主要内容,如果未能解决你的问题,请参考以下文章

Android数据离线缓存

在离线数据库中存储图像、文本注释和音频文件的方式。安卓、Java

在 iPhone 应用程序中处理离线存储的最佳方式?

怎么使用离线地图?

Pcap.Net 使用 OffLinePacketDevice 处理离线跟踪文件

android中怎么使用html5离线功能