【问题标题】:Using large record set with QTableView and QAbstractTableModel -- retrieve visible rows only使用带有 QTableView 和 QAbstractTableModel 的大型记录集——仅检索可见行
【发布时间】:2015-01-19 21:18:48
【问题描述】:

我正在使用 PyQt 并在 QTableView 中显示数据表。我只是在寻找一种方法来避免在屏幕上出现不超过 100 行时将整个数据集调用到内存中。从理论上讲,如果我只是检索可见数据,我应该能够几乎立即浏览包含数十万条记录的表。

据我了解,当 QTableView 想要显示一个数据单元格时,它会调用我的表模型(它是 QAbstractTableModel 的子类)的“data()”函数。该函数接收应该显示的单元格的行和列索引。如果我已经加载了我需要的所有数据,这对我来说非常有用。

但是,如果我还没有所有数据,我是否必须对每个单元格进行查询?还有另一种方法可以做到这一点吗?也许有一种方法可以逐行而不是逐个单元格地获取数据。也许有一个针对 MySql 的特定于数据库的解决方案,这就是我们正在使用的。

【问题讨论】:

  • 为什么不使用QSqlTableModel
  • 是否只加载可见的内容?很高兴知道,我会对此进行研究,但我非常想了解如何使用 QAbstractTableModel (其中 QSqlTableModel 是一个子类)来实现这一点。我没有在我的问题中提到这一点,但我可能需要对大型 CSV 文件执行相同的操作,因为我不想将整个文件加载到内存中。
  • 我没有任何使用它的个人经验,但它肯定比在 PyQt 中实现的自定义模型要快。如果您想详细了解它是如何完成的,您可能必须阅读相关的 Qt 源代码。或者要获得更一般的指导,请尝试Model subclassing reference
  • 好吧,我拒绝 QSqlTableModel 做任何不寻常的事情。并不是渲染行对我来说很慢。我只是想找出一种方法来从视图中进行适当的数据库调用。这是一个足够通用的问题,我希望有某种通用的解决方案。
  • 我的意思是子类化QSqlTableModel 可能比基于QAbstractTableModel 从头开始​​编写整个东西更高效。但就像我说的,我并没有真正使用过它,所以也许没有那么多收获。

标签: python mysql qt pyqt


【解决方案1】:

这是绝对可行的。我使用这种技术可以顺利滚动具有数万甚至数十万行的 pandas 表。就像您说的那样,您将需要使用QAbstractTableModel 将您的数据子类化。

代码可能看起来很大,但这里的关键是要记住,代码的主要部分是为每个可见数据单元调用的“数据”。

类似于:

class MyTableModel(QAbstractTableModel):
    def __init__(self, datain, headerdata, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.arraydata = datain
        self.headerdata = headerdata

    def rowCount(self, parent):
        return len(self.arraydata)

    def columnCount(self, parent):
        return len(self.arraydata[0])

    def data(self, index, role):
        if not index.isValid():
            return QVariant()
        elif role == Qt.TextAlignmentRole:
            return QVariant(Qt.AlignRight | Qt.AlignVCenter)
        elif role == Qt.DisplayRole:
            return QVariant(str(self.arraydata[index.row()][index.column()]))
        return QVariant()

活动小部件将是 QWidget 的子类,带有 __init__ 类似:

class DbsVarWindow(QWidget):
    """Display a Qt window of a Zacks Dbs Var. inputs:
        * fname file name (full path) of the dbs file
        * vname var name (openvest munged name' to display"""
    def __init__(self, fname, vname,  *args):
        QWidget.__init__(self, *args)
        dbs = ZacksDbs(fname)
        vals = dbs.get(vname)#[:,:10]
        tickers = dbs.get('ticker')

        # set the title
        self.setWindowTitle(vname)
        # set the minimum size
        self.resize(800, 600)

        # set the model and table view settings
        tablemodel = MyTableModel(vals,tickers, self)
        tableview = QTableView()
        # try some sorting
        tableview.setSortingEnabled(True)
        # allow drag to rearrange columns
        tableview.horizontalHeader().setMovable(True)
        tableview.setModel(tablemodel)
        # set the font
        font = QFont("Courier", 12, 1)
        tableview.setFont(font)

        layout = QVBoxLayout(self)
        layout.addWidget(tableview)
        self.setLayout(layout)
        self.show()

带有tableview.setModel(tablemodel) 的那一行是将您的数据(表格模型)与小部件挂钩。

如果代码过多,请见谅。这与我所能收集到的一个典型的例子一样接近。

【讨论】:

  • 这与我已经在做的非常接近。并不是说显示记录很慢。这是data() 函数调用期望整个记录集已经加载到self.arraydata。这很慢。现在我正在研究 QSqlTableModel 如何只提取它需要在屏幕上显示的数据。
  • @TheGern 取决于您的确切数据大小,通常 1 个查询来获取所有索引(按您的排序顺序)就足够了。然后,当您收到 data() 调用时,检查“完整”记录的本地缓存并运行 50 条记录的 sql 查询以刷新本地缓存。抱歉,这不是更具体,但这实际上取决于适用于您的数据方案的内容。
  • 这实际上是我正在调查的路径之一!但是检查我对我的 OP 的最后评论——QSqlQueryModel 根据可见的内容一次获取一行来做到这一点。我只需要弄清楚如何在我的 QAbstractTableModel 子类中实现一个简单的版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
  • 1970-01-01
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多