首页 > 解决方案 > 具有多个 StyledItemDelegateForColumn 的 QAbstractTableModel 和 QTableView 使我的应用程序崩溃

问题描述

我有带有 QAbstractTableModel 和多个 QStyledItemDelegates 的 QTableView。

我通过 setStyledItemForColumn 设置这些代表。

在这种情况下,我的应用程序崩溃了。

当我按下 1 个键或尝试向右扩展 gui 时发生崩溃。

但如果我使用其中之一,我的应用程序运行良好。

我认为这是一种 Qt 错误。

你知道一些吗?

from PySide2 import QtWidgets
from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtSql
import os
import PySide2
import sys
dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path
alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
class IconDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(IconDelegate, self).initStyleOption(option, index)
        if option.features & QtWidgets.QStyleOptionViewItem.HasDecoration:
            s = option.decorationSize
            s.setWidth(option.rect.width())
            option.decorationSize = s
class Delegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent=None):
        super(Delegate, self).__init__(parent=None)
    def initStyleOption(self, option, index):

#        super(IconDelegate, self).initStyleOption(option, index)
        if index.column() == 6:
            if option.features & QtWidgets.QStyleOptionViewItem.HasDecoration:
                s = option.decorationSize
                s.setWidth(option.rect.width())
                option.decorationSize = s
    def createEditor(self, parent, option, index):
        editor = QtWidgets.QComboBox(parent)

        return editor
    def setEditorData(self, editor, index):
        model = index.model()
        items = model.items
        text = items[index.row()][index.column()]
        editor.setCurrentText(text)
    def setModelData(self, editor, model, index):

        items = model.items
#
class TableView(QtWidgets.QTableView):
    def __init__(self, parent=None):
        super(TableView, self).__init__(parent=None)
        delegate = Delegate()
        self.setItemDelegate(delegate)
        #Here is the crash point
#        self.setItemDelegateForColumn(6, delegate)
#        self.setItemDelegateForColumn(11, IconDelegate())
        self.tableModel = TableModel(2, 15)
        self.setModel(self.tableModel)
    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_1:
            self.tableModel.insertRows(0)

class TableItem(object):
    def __init__(self,  parent=None):        
        self.root = False

        self.word = ""
        self.alignment = QtCore.Qt.AlignCenter

        self.rule = ""
        self.foregroundcolor = QtGui.QColor(QtCore.Qt.black)
        self.backgroundcolor = QtGui.QColor(QtCore.Qt.white)
        self.font = QtGui.QFont("Meiryo", 14)
class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, row = 0, column = 0, parent = None):
        super(TableModel, self).__init__(parent = None)        
        self.items = [[TableItem() for c in range(column)] for r in range(row)]
        self.root = QtCore.QModelIndex()        
    def rowCount(self, parent=QtCore.QModelIndex()):        
        return len(self.items)
    def columnCount(self, parent=QtCore.QModelIndex()):       
        return 15
    def data(self, index, role = QtCore.Qt.DisplayRole):    
        if not index.isValid():
            return None
        row = index.row()
        column = index.column()        
        if role == QtCore.Qt.DisplayRole:
            item = self.items[row][column]
            return item
    def headerData(self, section, orientation, role = QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Orientation.Horizontal:
            if role == QtCore.Qt.DisplayRole:
                return alphabet[section]
        return super(TableModel, self).headerData(section, orientation, role)
    def flags(self, index):        
        return QtCore.Qt.ItemFlag.ItemIsEditable|QtCore.Qt.ItemFlag.ItemIsEnabled|QtCore.Qt.ItemFlag.ItemIsSelectable
    def setData(self, index, value, role=QtCore.Qt.EditRole):        
        if role == QtCore.Qt.EditRole:       
            row, column = index.row(), index.column()

            self.items[row][column]  = value
            self.dataChanged.emit(index, index)
            return True
        elif role == QtCore.Qt.DisplayRole:             
            row, column = index.row(), index.column()
            self.items[row][column]  = value
            self.dataChanged.emit(index, index)
            return True
        elif role == QtCore.Qt.FontRole:
            string = value.toString()
            s = string.split(",")
            font = s[0]

            self.dataChanged.emit(index, index)
            return True
    def insertRows(self, position, rows=1, index=QtCore.QModelIndex()):   
        self.beginInsertRows(QtCore.QModelIndex(), position, position+rows-1)
        for row in range(rows):        
            self.items.insert(position+row, [TableItem() for c in range(self.columnCount())])
        self.endInsertRows()
        self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
        self.emit(QtCore.SIGNAL("layoutChanged()"))      
        return True
    def removeRows(self, position, rows=1, index=QtCore.QModelIndex()):
        self.beginRemoveRows(QtCore.QModelIndex(), position, position+rows-1)
        for row in range(rows):
            self.items = self.items[:position] + \
                        self.items[position + rows:]
        self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
        self.emit(QtCore.SIGNAL("layoutChanged()"))      
        return True

def main():
    if QtWidgets.QApplication.instance() is not None:
        app = QtWidgets.QApplication.instance()
    else:
        app = QtWidgets.QApplication([])
    mainwindow = TableView()
    mainwindow.show()
    sys.exit(QtWidgets.QApplication.exec_())
if __name__ == "__main__":
    main()

标签: pythonqtableviewpyside2qabstracttablemodelqstyleditemdelegate

解决方案


解释

这不是 Qt 错误,而是您自己的代码的错误。

首先,建议您从 CMD / 控制台运行代码,以便获得错误信息,如果这样做,您将看到错误消息是:

Traceback (most recent call last):
  File "main.py", line 38, in setEditorData
    editor.setCurrentText(text)
TypeError: 'PySide2.QtWidgets.QComboBox.setCurrentText' called with wrong argument types:
  PySide2.QtWidgets.QComboBox.setCurrentText(TableItem)
Supported signatures:
  PySide2.QtWidgets.QComboBox.setCurrentText(str)

该错误清楚地表明 setCurrentText 方法需要一个字符串,但正在接收一个 TableItem。为什么你会收到一个 TableItem?好吧,您的代码items[index.row()][index.column()] 返回一个 TableItem,假设您想获取文本“word”,那么您必须使用:

def setEditorData(self, editor, index):
    model = index.model()
    items = model.items
    item = items[index.row()][index.column()]
    text = item.word
    editor.setCurrentText(text)

在这两种情况下(setItemDelegate 或 setItemD)都会导致错误。

但是当窗口调整大小时,错误仍然存​​在,因为它是由另一个委托引起的。由于您部分覆盖了委托,因此另一方继续使用通用信息,例如期望index.data(Qt.DisplayRole)返回一个字符串,但在您的情况下返回一个 TableItem:

def data(self, index, role = QtCore.Qt.DisplayRole):    
    if not index.isValid():
        return None
    row = index.row()
    column = index.column()        
    if role == QtCore.Qt.DisplayRole:
        item = self.items[row][column]
        return item

总之,OP 没有正确使用默认角色,导致使用此信息的代表获取不正确的数据。

解决方案

考虑到以上所有,我已经纠正了许多我之前没有提到的错误,因为许多是微不足道的或超出主题的,获得以下代码:

from PySide2 import QtWidgets
from PySide2 import QtCore
from PySide2 import QtGui
from PySide2 import QtSql
import os
import PySide2
import sys

dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, "plugins", "platforms")
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = plugin_path

alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]


class IconDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(IconDelegate, self).initStyleOption(option, index)
        if option.features & QtWidgets.QStyleOptionViewItem.HasDecoration:
            s = option.decorationSize
            s.setWidth(option.rect.width())
            option.decorationSize = s


class Delegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        if option.features & QtWidgets.QStyleOptionViewItem.HasDecoration:
            s = option.decorationSize
            s.setWidth(option.rect.width())
            option.decorationSize = s

    def createEditor(self, parent, option, index):
        editor = QtWidgets.QComboBox(parent)
        return editor


#
class TableView(QtWidgets.QTableView):
    def __init__(self, parent=None):
        super(TableView, self).__init__(parent=None)
        delegate = Delegate(self)
        # self.setItemDelegate(delegate)
        # Here is the crash point
        self.setItemDelegateForColumn(6, delegate)
        icon_delegate = IconDelegate(self)
        self.setItemDelegateForColumn(11, icon_delegate)
        self.tableModel = TableModel(2, 15)
        self.setModel(self.tableModel)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_1:
            self.tableModel.insertRows(0)


class TableItem(object):
    def __init__(self, parent=None):
        self.root = False

        self.word = ""
        self.alignment = QtCore.Qt.AlignCenter

        self.rule = ""
        self.foregroundcolor = QtGui.QColor(QtCore.Qt.black)
        self.backgroundcolor = QtGui.QColor(QtCore.Qt.white)
        self.font = QtGui.QFont("Meiryo", 14)


class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, row=0, column=0, parent=None):
        super(TableModel, self).__init__(parent=None)
        self.items = [[TableItem() for c in range(column)] for r in range(row)]

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

    def columnCount(self, parent=QtCore.QModelIndex()):
        if self.items:
            return len(self.items[0])
        return 0

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        row = index.row()
        column = index.column()
        if 0 <= row < self.rowCount() and 0 <= column < self.columnCount():
            item = self.items[row][column]
        if role == QtCore.Qt.DisplayRole:
            text = item.word
            return text
        elif role == QtCore.Qt.EditRole:
            return item

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Orientation.Horizontal:
            if role == QtCore.Qt.DisplayRole and section < len(alphabet):
                return alphabet[section]
        return super(TableModel, self).headerData(section, orientation, role)

    def flags(self, index):
        return (
            QtCore.Qt.ItemFlag.ItemIsEditable
            | QtCore.Qt.ItemFlag.ItemIsEnabled
            | QtCore.Qt.ItemFlag.ItemIsSelectable
        )

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            row, column = index.row(), index.column()
            self.items[row][column].word = value
            self.dataChanged.emit(index, index)
            return True
        elif role == QtCore.Qt.DisplayRole:
            row, column = index.row(), index.column()
            self.items[row][column].word = value
            self.dataChanged.emit(index, index)
            return True
        return False

    def insertRows(self, position, rows=1, index=QtCore.QModelIndex()):
        self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
        for row in range(rows):
            self.items.insert(
                position + row, [TableItem() for c in range(self.columnCount())]
            )
        self.endInsertRows()
        self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
        self.emit(QtCore.SIGNAL("layoutChanged()"))
        return True

    def removeRows(self, position, rows=1, index=QtCore.QModelIndex()):
        self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
        for row in range(rows):
            self.items = self.items[:position] + self.items[position + rows :]
        self.emit(QtCore.SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
        self.emit(QtCore.SIGNAL("layoutChanged()"))
        return True


def main():
    if QtWidgets.QApplication.instance() is not None:
        app = QtWidgets.QApplication.instance()
    else:
        app = QtWidgets.QApplication([])
    mainwindow = TableView()
    mainwindow.show()
    sys.exit(QtWidgets.QApplication.exec_())


if __name__ == "__main__":
    main()

推荐阅读