在映射器索引保持不变的情况下更新Qt QDataWidgetMapper上的数据

时间:2018-10-02 13:21:01

标签: python pyqt

我正在尝试使用QAbstractTableModel将数据从QDataWidgetMapper映射到自定义视图。映射器工作正常,但是映射的数据不会根据模型中的更改自动更新。我需要手动更改 currentMapperIndex 以触发自定义视图上的更改。

我试图最大程度地简化我的问题。

TableModel:

class TableModel(QAbstractTableModel):
    def __init__(self, parent=None):
        super(TableModel, self).__init__(parent)
        self.columns = ['Col1', 'Col2']
        self.datatable = []

    def update(self, dataIn):
        self.layoutAboutToBeChanged.emit()
        self.datatable = dataIn
        self.layoutChanged.emit()

    def rowCount(self, parent=QModelIndex()):
        return len(self.datatable)

    def columnCount(self, parent=QModelIndex()):
        return len(self.columns)

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.columns[section].title()

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole or role == Qt.EditRole:
            row = self.datatable[index.row()]
            column_key = self.columns[index.column()]
            return row[column_key]
        else:
            return None

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid() or role != Qt.EditRole:
            return False
        if self.datatable:
            column_key = self.columns[index.column()]

            self.datatable[index.row()][column_key] = value
            self.dataChanged.emit(index, index, [])
            return True
        return True

自定义视图:

class CustomView(QWidget):
    def __init__(self, parent=None):
        super(CustomView, self).__init__(parent)

        self.setupLayout()
        self.spinboxMapperIndex.valueChanged.connect(self.changeMapperIndex)

    def setModel(self, model):
        self.mapper = QDataWidgetMapper()
        self.mapper.setModel(model)
        self.mapper.addMapping(self.lineEdit1, 0)
        self.mapper.addMapping(self.lineEdit2, 1)
        self.mapper.toFirst()

    def changeMapperIndex(self, index):
        self.mapper.setCurrentIndex(index)

    def setupLayout(self):

        self.spinboxMapperIndex = QSpinBox()

        self.lineEdit1 = QLineEdit()
        self.lineEdit2 = QLineEdit()

        layout = QVBoxLayout()
        layout.addWidget(self.spinboxMapperIndex)
        layout.addWidget(self.lineEdit1)
        layout.addWidget(self.lineEdit2)
        self.setLayout(layout)

主要:

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)

    model = TableModel()

    table = QTableView()
    table.setModel(model)
    table.show()

    data = [ {'Col1': 11, 'Col2': 12},
             {'Col1': 21, 'Col2': 22},
             {'Col1': 31, 'Col2': 32}]
    model.update(data)

    view = CustomView()
    view.setModel(model)
    view.show()

    newData = [ {'Col1': 111, 'Col2': 112},
                {'Col1': 121, 'Col2': 122},
                {'Col1': 131, 'Col2': 132}]
    model.update(newData)

    sys.exit(app.exec_())

通过运行以上代码,可以看到TableView用新数据很好地更新了,而CustomView映射的数据没有更新,除非我手动更改用于更改映射器索引的Spinbox。

QDataWidgetMapper文档中,我们可以阅读:

  

请注意,QDataWidgetMapper会跟踪外部修改。如果模型的内容在应用程序的另一个模块中更新,则小部件也将更新。

似乎并非如此...

即使以Qt C ++形式编写,任何帮助也将不胜感激。

编辑:

根据update的答案对Kuba Ober方法进行的更改:

def update(self, dataIn):
    self.beginResetModel()
    self.datatable = dataIn
    self.endResetModel()

1 个答案:

答案 0 :(得分:0)

您的模型没有履行每个Qt模型必须履行的合同。问题的一部分在于模型,而不是映射器。即,update应该使用beginResetModelendResetModel方法。您不应该自己发出布局更改信号。实际上,您应该从模型中发出的唯一预定义信号是dataChanged-QAbstractItemModel会发出其他信号,以响应对beginActionendAction的调用。

重置模型后,视图应重置当前索引-毕竟,在重置过程中,模型实际上没有行-并且当前索引变为无效。因此,将以下内容添加到CustomView.setModel

self.model.modelReset.connect(self.mapper.toFirst)

可以说,也许映射器应该自己建立这种连接,但事实并非如此,因此您必须这样做。