【问题标题】:PyQt5 Model/View - One model, two viewsPyQt5 模型/视图 - 一个模型,两个视图
【发布时间】:2020-04-27 18:26:25
【问题描述】:

我正在使用 PyQt5 设计一个应用程序。我需要在两个表上显示相同的数据,但在每个表上以不同的方式显示。那是: 第一个表是可编辑的。可以编辑几个元素(它们的名称、值等)

然而,第二个表格需要在垂直标题上显示第一个表格的第一列(元素名称),并将第一个表格的其中一列(在本例中为第三个)作为唯一的行:

(我还没有做到这一点,我画了它以便更好地理解)

为了使两个表之间的数据保持一致(并且在内部,因为表中的值用于其他操作),我认为使用模型/架构是最好的方法。我已经开始为第一个表实现模型(继承 QAbstractTableModel),但是这个模型类中的方法(数据、行计数、列计数...)对于每个表来说应该是非常不同的。

我应该如何解决这个问题?我应该为第二个表创建一个自定义 View 类吗?

【问题讨论】:

    标签: python model-view-controller pyqt pyqt5


    【解决方案1】:

    如果您想获得基于另一个模型的新模型,那么您可以使用 QXProxyModel。在您的特定情况下,您可以使用以下过程转换模型:

    • 使用 QTransposeProxyModel 旋转表格。
    • 使用 hideRow() 隐藏行。
    • 通过实现基于 QIdentityProxyModel 的代理来映射标头。
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class CustomProxyModel(QtCore.QIdentityProxyModel):
        def headerData(self, section, orientation, role):
            if role == QtCore.Qt.DisplayRole:
                if orientation == QtCore.Qt.Horizontal:
                    return self.sourceModel().index(0, section).data(role)
                else:
                    return 1
            return super().headerData(section, orientation, role)
    
        def setSourceModel(self, model):
            super().setSourceModel(model)
            model.dataChanged.connect(self._on_headerDataChanged)
    
        def _on_headerDataChanged(self, topLeft, bottomRight, roles):
            if topLeft.row() <= 0 <= bottomRight.row():
                self.headerDataChanged.emit(
                    QtCore.Qt.Horizontal, topLeft.row(), bottomRight.row()
                )
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
    
        model = QtGui.QStandardItemModel(3, 4)
    
        datas = (
            ("Element 1", "2", "3", "4"),
            ("Another element", "6", "7", "8"),
            ("Element nr. 3", "10", "11", "12"),
        )
    
        for i, r in enumerate(datas):
            for j, d in enumerate(r):
                it = QtGui.QStandardItem(d)
                model.setItem(i, j, it)
    
        proxy = QtCore.QTransposeProxyModel()
        proxy.setSourceModel(model)
    
        proxy2 = CustomProxyModel()
        proxy2.setSourceModel(proxy)
    
        view1 = QtWidgets.QTableView()
        view1.setModel(model)
    
        view2 = QtWidgets.QTableView()
        view2.setModel(proxy2)
    
        for r in range(proxy.rowCount()):
            if r != 2:
                view2.hideRow(r)
    
        w = QtWidgets.QWidget()
        lay = QtWidgets.QHBoxLayout(w)
        lay.addWidget(view1)
        lay.addWidget(view2)
        w.resize(640, 480)
        w.show()
    
        sys.exit(app.exec_())
    

    【讨论】:

    • 感谢您的回答。尽管最终的实现会比我预期的更复杂,但您的回答将非常有帮助。我有一个问题:在 CustomProxyModel > _on_headerDataChanged 中,发射信号的参数不应该是 topLeft.column(), bottomRight.column() 吗? (而不是 row(),因为这是来自转置模型...@eyllanesc
    • @kru96 它给您带来了麻烦吗? dataChanged信号是模型的典型,即不考虑是否代理。
    • 首先,很抱歉这么长时间才回复...不,我还没有尝试过。我只是在查看您的代码并试图理解它之后问它。一旦我尝试了这一切我会写一个回复,但目前我能够在这个项目上投入很少的时间。
    【解决方案2】:

    您的设计中缺少的部分是完整的模型视图-视图模型流的观察者设计模式实现。 您可以实现自己的可观察数据容器以绑定到多个视图here 或者您可以使用 pypi 的观察者 3rd 方库。我建议实现您自己的可观察数据容器,以更好地管理未来的更改,例如像您的问题中那样转置表格视图。

    【讨论】:

      猜你喜欢
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 1970-01-01
      • 1970-01-01
      • 2011-08-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多