【问题标题】:QTableView model crash when calling endInsertRows()调用 endInsertRows() 时 QTableView 模型崩溃
【发布时间】:2018-08-24 11:59:12
【问题描述】:

在插入代表行的新对象时,我一直在尝试更新 QTableViewModel。我确实遵循了 SO 中几个问题的建议,但我无法获得一个工作示例。

调试后发现调用self.endInsertRows()会导致崩溃。

这是一个最小的例子:

import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWidgets


class Wire:

    def __init__(self, name, x, y, gmr, r):
        self.name = name
        self.x = x
        self.y = y
        self.r = r
        self.gmr = gmr


class WiresCollection(QtCore.QAbstractTableModel):

    def __init__(self, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent)

        self.header = ['Name', 'R (Ohm/km)', 'GMR (m)']

        self.index_prop = {0: 'name', 1: 'r', 2: 'gmr'}

        self.wires = list()

    def add(self, wire: Wire):
        """
        Add wire
        :param wire:
        :return:
        """
        row = len(self.wires)
        self.beginInsertRows(QtCore.QModelIndex(), row, row)
        self.wires.append(wire)
        self.endInsertRows()

    def delete(self, index):
        """
        Delete wire
        :param index:
        :return:
        """
        row = len(self.wires)
        self.beginRemoveRows(QtCore.QModelIndex(), row, row)
        self.wires.pop(index)
        self.endRemoveRows()

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

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

    def parent(self, index=None):
        return QtCore.QModelIndex()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.DisplayRole:
                val = getattr(self.wires[index.row()], self.index_prop(index.column()))
                return str(val)
        return None

    def headerData(self, p_int, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                return self.header[p_int]

    def setData(self, index, value, role=QtCore.Qt.DisplayRole):
        """
        Set data by simple editor (whatever text)
        :param index:
        :param value:
        :param role:
        """
        wire = self.wires[index.row()]
        attr = self.index_prop[index.column()]
        setattr(wire, attr, value)


class TowerBuilderGUI(QtWidgets.QDialog):

    def __init__(self, parent=None):
        """
        Constructor
        Args:
            parent:
        """
        QtWidgets.QDialog.__init__(self, parent)
        self.setWindowTitle('Tower builder')

        # GUI objects
        self.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
        self.layout = QVBoxLayout(self)
        self.wires_tableView = QTableView()
        self.add_wire_pushButton = QPushButton()
        self.add_wire_pushButton.setText('Add')
        self.delete_wire_pushButton = QPushButton()
        self.delete_wire_pushButton.setText('Delete')

        self.layout.addWidget(self.wires_tableView)
        self.layout.addWidget(self.add_wire_pushButton)
        self.layout.addWidget(self.delete_wire_pushButton)

        self.setLayout(self.layout)

        # Model
        self.wire_collection = WiresCollection(self)

        # set models
        self.wires_tableView.setModel(self.wire_collection)

        # button clicks
        self.add_wire_pushButton.clicked.connect(self.add_wire_to_collection)
        self.delete_wire_pushButton.clicked.connect(self.delete_wire_from_collection)

    def msg(self, text, title="Warning"):
        """
        Message box
        :param text: Text to display
        :param title: Name of the window
        """
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText(text)
        # msg.setInformativeText("This is additional information")
        msg.setWindowTitle(title)
        # msg.setDetailedText("The details are as follows:")
        msg.setStandardButtons(QMessageBox.Ok)
        retval = msg.exec_()

    def add_wire_to_collection(self):
        """
        Add new wire to collection
        :return:
        """
        name = 'Wire_' + str(len(self.wire_collection.wires) + 1)
        wire = Wire(name, x=0, y=0, gmr=0, r=0.01)
        self.wire_collection.add(wire)

    def delete_wire_from_collection(self):
        """
        Delete wire from the collection
        :return:
        """
        idx = self.ui.wires_tableView.currentIndex()
        sel_idx = idx.row()

        if sel_idx > -1:
            self.wire_collection.delete(sel_idx)
        else:
            self.msg('Select a wire in the wires collection')


if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    window = TowerBuilderGUI()
    window.show()
    sys.exit(app.exec_())

【问题讨论】:

  • 您的代码产生错误: 1. 当按下return self.createIndex(row, column, self.m_data[row]) 行中的Add 按钮时,我们得到错误AttributeError: 'WiresCollection' object has no attribute 'm_data'; 2.当按下idx = self.ui.wires_tableView.currentIndex()行中的Delete按钮时,我们得到错误AttributeError: 'TowerBuilderGUI' object has no attribute 'ui'
  • 请删除 index 方法,因为它不是必需的。我将从示例中删除它
  • 您的代码产生错误: 1. 当按下行中的添加按钮时返回val = getattr(self.wires[index.row()], self.index_prop(index.column())),我们得到错误TypeError: 'dict' object is not callable; 2. 在 idx = self.ui.wires_tableView.currentIndex() 行中按下 Delete 按钮时,出现错误 AttributeError: 'TowerBuilderGUI' object has no attribute 'ui'

标签: python python-3.x pyqt pyqt5 qabstracttablemodel


【解决方案1】:

如 cmets 所示,您有 2 个错误:

  • 第一个是当你按下添加时,因为当你添加一个新项目时,你必须刷新视图,这就是为什么它被称为 data() 方法,它是错误在 self.index_prop(index.column()) 中显示的地方,index_pro 是一个字典所以你应该使用[] 而不是()

    val = getattr(self.wires[index.row()], self.index_prop[index.column()])
    
  • idx = self.ui.wires_tableView.currentIndex() 行产生了另一个错误,ui 不存在并且没有必要,确定它是以前代码的残余,以访问 wires_tableView,因为这是类不是必须使用中介,必须直接用self访问:idx = self.wires_tableView.currentIndex()

以上是拼写错误,可能会标记它以便问题关闭,还有另一个错误不是,这就是我回答的原因。

self.beginRemoveRows(...) 行中,您必须传递要删除的行,但传递的行不存在:

row = len(self.wires)
self.beginRemoveRows(QtCore.QModelIndex(), row, row)  # <---- row does not exist in the table

解决方法很简单,按索引改一下:

def delete(self, index):
    """
    Delete wire
    :param index:
    :return:
    """
    self.beginRemoveRows(QtCore.QModelIndex(), index, index)
    self.wires.pop(index)
    self.endRemoveRows()

【讨论】:

  • 确实,我之前的代码中有错别字,以制作最小的示例...您是否设法添加一行??
  • 你在windows上吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 2016-02-11
  • 2018-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多