QAbstractTableModel & QTableView有多个StyledItemDelegateForColumn会使我的应用程序崩溃。

问题描述 投票:0回答:1

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

我通过setStyledItemForColumn来设置这些委托人。

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

当我按下1个键,或者试图将gui向右展开时,就会发生崩溃。

但如果我使用其中一个键,我的应用程序就会正常运行。

我想这是一个Qt bug。

你知道吗?

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()
python qtableview pyside2 qabstracttablemodel qstyleditemdelegate
1个回答
2
投票

解释

这不是Qt的bug,而是你自己代码的bug。

首先建议你从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()
© www.soinside.com 2019 - 2024. All rights reserved.