Search code examples
pythonpyqt5pyside2

QVariant alternative when migrating from PyQt5 to PySide2


I am having some trouble switching from PyQt5 to PySide2 because of the following piece of code:

class EnumModel(QtCore.QAbstractListModel):

    def __init__(self, list_of_enums):
        """
        Enumeration model
        :param list_of_enums: list of enumeration values to show
        """
        QtCore.QAbstractListModel.__init__(self)
        self.items = list_of_enums

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

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid() is True:
            if role == QtCore.Qt.DisplayRole:
                return QtCore.QVariant(self.items[index.row()].value[0])
            elif role == QtCore.Qt.ItemDataRole:
                return QtCore.QVariant(self.items[index.row()].value[0])
        return QtCore.QVariant()

The code woks fine under PyQt5.

In my efforts on migrating I found that the official website says:

PySide only supports PyQt's API 2 (see PSEP 101) for details. Therefore Qt classes such as QStrings, QStringLists, and QVariants are not available on PySide. Instead, you should simply use native Python datatypes.

So the solution would be (I guess) to simply change QVariant by str. When I do that the class does not throw any error, but it does not display the model either.

In fact the function data is receiving role=13 instead of role=QtCore.Qt.DisplayRole.

I don't know if this is a bug with PySide2 (which under Linux is a bit buggy) or it is because of something else.

An minimum viable example is this:

from PySide2.QtWidgets import *
from PySide2 import QtCore
from enum import Enum


class SomeEnum(Enum):
    A = 'A'
    B = 'B'
    C = 'C'


class EnumModel(QtCore.QAbstractListModel):

    def __init__(self, list_of_enums):
        """
        Enumeration model
        :param list_of_enums: list of enumeration values to show
        """
        QtCore.QAbstractListModel.__init__(self)
        self.items = list_of_enums

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

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid() is True:
            if role == QtCore.Qt.DisplayRole:
                return self.items[index.row()].value[0]
            elif role == QtCore.Qt.ItemDataRole:
                return self.items[index.row()].value[0]
            else:
                print('not recognised')
        return ""


if __name__ == '__main__':
    import sys

    model = EnumModel([SomeEnum.A, SomeEnum.A, SomeEnum.B, SomeEnum.C])

    app = QApplication(sys.argv)
    lst = QListView()
    lst.setModel(model)
    lst.show()
    sys.exit(app.exec_())

Solution

  • The problem is that when the view requires information related to the role Qt::SizeHintRole (13) you are passing it an empty string, instead you must return None or simply do not return anything since it interferes with the other roles:

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if index.isValid():
            print(role)
            if role == QtCore.Qt.DisplayRole:
                return self.items[index.row()].value[0]
            elif role == QtCore.Qt.ItemDataRole:
                return self.items[index.row()].value[0]
            else:
                print('not recognised')