PyQt.QTableView 内容的单元测试

Posted

技术标签:

【中文标题】PyQt.QTableView 内容的单元测试【英文标题】:Unit Test for content of PyQt.QTableView 【发布时间】:2018-08-14 13:13:39 【问题描述】:

我的 GUI(PyQt5、Python3.6)显示来自 SQLite 数据库的数据,主要使用 QTableViews。

我正在使用定义的输入文件对应用程序进行单元测试,并且我想检查是否所有内容都显示在 GUI 中的正确位置。现在,所有数据都在附加到 QTableView 的模型中,而不是 QTableView 本身。我有时会使用QTableView.hideRow() 来隐藏原始数据中存在但不希望在此特定视图中显示的某些列。

如何测试所有内容都显示在 QTableView 中的预期位置

这是一个我想为其编写单元测试的迷你示例类:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from PyQt5 import QtSql
from PyQt5.QtWidgets import (QWidget, QTableView, QApplication, QHBoxLayout)
import sys

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(400, 150)

        self.createConnection()
        self.fillTable()
        self.createModel()
        self.initUI()

    def createConnection(self):
        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName("test.db")
        if not self.db.open():
            print("Cannot establish a database connection")
            return False

    def fillTable(self):
        self.db.transaction()
        q = QtSql.QSqlQuery()

        q.exec_("DROP TABLE IF EXISTS Cars;")
        q.exec_("CREATE TABLE Cars (Company TEXT, Model TEXT, Year NUMBER);")
        q.exec_("INSERT INTO Cars VALUES ('Honda', 'Civic', 2009);")
        q.exec_("INSERT INTO Cars VALUES ('VW', 'Golf', 2013);")
        q.exec_("INSERT INTO Cars VALUES ('VW', 'Polo', 1999);")

        self.db.commit()

    def createModel(self):
        self.model = QtSql.QSqlTableModel()
        self.model.setTable("Cars")
        self.model.select()

    def initUI(self):
        layout = QHBoxLayout()
        self.setLayout(layout)

        view = QTableView()
        layout.addWidget(view)

        view.setModel(self.model)
        view.hideRow(1)

    def closeEvent(self, e):
        if (self.db.open()):
            self.db.close()


def main():
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

我当然可以测试模型中的数据:

self.assertEqual(model.headerData(0, Qt.Horizontal, Qt.DisplayRole), "Company")
self.assertEqual(model.data(model.index(0, 0), Qt.DisplayRole), "Honda")

等等

但是 model 包含 3 行,而我的 QTableView 只有 2 行(因为隐藏了 VW Golf 行)。

那么,有没有办法测试 QTableView 中显示的数据而不是模型中的数据?(在示例类中,我希望测试断言视图仅包含 2行,而 Polo 行是第二行。)

【问题讨论】:

【参考方案1】:

如果您想验证是否有任何索引可见,这不依赖于模型,因为模型是数据的表示,例如,模型可以由多个视图共享,因此每个视图选择要显示的项目,以知道哪些索引在 QTableView 中可见,您必须使用 isIndexHidden() 方法:

from PyQt5 import QtCore, QtWidgets, QtSql
import sys

class Example(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        ...

    def initUI(self):
        layout = QtWidgets.QHBoxLayout()
        self.setLayout(layout)

        self.view = QtWidgets.QTableView()
        layout.addWidget(self.view)

        self.view.setModel(self.model)
        self.view.hideRow(1)
        self.items_visible()

    def items_visible(self):
        for i in range(self.model.rowCount()):
            for j in range(self.model.columnCount()):
                ix = self.model.index(i, j)
                if not self.view.isIndexHidden(ix):
                    print("-: ".format(ix.row(), ix.column(), ix.data()))


def main():
    app = QtWidgets.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

【讨论】:

想要检查 Polo 行是否显示为第二行?由于视图中的行号和模型不一样,我觉得这很难。 由于一个模型可以被多个视图共享,所以在某些情况下可以过滤某些列,在其他情况下隐藏某些行等,因此可视化索引不一定与模型的索引匹配。 [cont] visible 有点模糊,比如说你有 100 行,并且你没有使用 hideRow(),所以可以说所有行都是可见的,但是很多行,滚动变量会使一些行隐藏到视图中,所以为了避免混淆和浪费时间,你如何定义一行是可见的,你如何给它们编号 这就是为什么我希望以某种方式测试视图本身的数据,因为每个视图都显示一组定义的行,所以应该可以从那里开始计数。但如果这不可能,我将不得不通过测试所有(并且只有)非隐藏行没有隐藏,并且非隐藏行的内容是可以的。看起来丑陋,但可行。感谢指向 isIndexHidden 的指针。 是不是显示的索引的性能没有被性能保存,你不认为保存visualIndex会导致不必要的内存消耗吗?我已经使用Qt和我从来不需要获取visualIndex,所以我认为没有必要,这只是内存开销。

以上是关于PyQt.QTableView 内容的单元测试的主要内容,如果未能解决你的问题,请参考以下文章

将 pandas DataFrame 与 PyQt5 QTableView 同步

PyQt4 - QTableView - 如何循环 QTableView

PyQt4 qTableView 对齐

基于 PyQt4 QTableView 模型

PyQt QTableView 更新到 PyQt 4.5.1 后不显示图标

用于测试 rxjava 的书面单元测试,但不确定我的单元测试是不是正在测试所有内容