1

I have QTableView with QAbstractTableModel and multiple QStyledItemDelegates.

I set these delegates by setStyledItemForColumn.

In this case, my app crashes.

Crash happens when I push 1 key, or try to expand the gui to right.

But if I use one of them, my app goes well.

I think this is a kind of Qt bug.

Do you know somewhat?

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()
Haru
  • 1,884
  • 2
  • 12
  • 30
  • I know the cause of the error but even so I cannot provide you with a solution since I do not understand what the logic of your model is, you could explain in detail what each class is for, also it seems that you do not handle the logic of the models so my question is why do you need to implement a custom model? – eyllanesc May 23 '20 at 02:10
  • @eyllanesc Oh, you know the cause... but you must know what my app is for? I'm sorry, I couldn't reach the point. I have thought this error happens Independent of my purpose of model. – Haru May 23 '20 at 02:16
  • Now I use setDelegate not for column or row...-.-; this causes no error. – Haru May 23 '20 at 02:19

1 Answers1

2

Explanation

It is not a Qt bug but it is a bug of your own code.

First of all it is recommended that you run your code from the CMD / console so that you get error information, if you do you will see that the error message is:

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)

The error clearly indicates that the setCurrentText method expects a string but is receiving a TableItem. Why do you receive a TableItem? Well with your code items[index.row()][index.column()] returns a TableItem, assuming you want to get the text "word" then you must use:

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

In both cases (setItemDelegate or setItemD) it causes the error.

But the error still persists when the window is resized since it is caused by the other delegate. Since you are partially overriding a delegate then the other party continues to use the generic information, for example expect index.data(Qt.DisplayRole) to return a string but in your case return a 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

In conclusion, the OP have not correctly used the default roles, causing delegates who use this information to obtain the incorrect data.

Solution

Considering all of the above, I have corrected many errors that I have not mentioned previously because many are trivial or are out of topic, obtaining the following code:

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()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Oh,,, I don't want to do the same failure like this case... In my case, my app suddenly crashes and didn't emit error messages. But in only this case, it was better I put the Question , I could the way for finding out the implicit Errors.Thanks for your sharply appropriate explanation. – Haru May 23 '20 at 02:34
  • thanks. It is very great. You know, If I don't see any error with CMD trial and my app crashes, can I assume it is high possibility of Qt bug?As you said, I have tried to do it with CMD, and I could have found my app crashes with other problem without error messages. – Haru May 24 '20 at 07:51