Why is this role always sizeHintRole?

183 Views Asked by At

I have been making C++ example code to PySide2.

I review fetchMore example over and over again.

Where is the wrong point in this conversion?

The biggest problems is in data method.

role is always SizeHintRole.

Why?

Here is the code.

# -*- coding: utf-8 -*-
import sys, os, PySide2
from PySide2 import QtCore, QtWidgets, QtGui


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

class Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.model = FileListModel()
        #6.0
#        self.model.setDirPath(QtCore.QLibraryInfo.path(QtCore.QLibraryInfo.PrefixPath))
        self.model.setDirPath(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath))
        self.label = QtWidgets.QLabel("&Directory:")
        self.lineEdit = QtWidgets.QLineEdit()
        self.label.setBuddy(self.lineEdit)
        self.view = QtWidgets.QListView()
        self.view.setModel(self.model)
        self.logViewer = QtWidgets.QTextBrowser(self)
        self.logViewer.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        self.lineEdit.textChanged["QString"].connect(self.model.setDirPath)
        self.lineEdit.textChanged["QString"].connect(self.logViewer.clear)
        self.model.numberPopulated[int].connect(self.updateLog)
        layout = QtWidgets.QGridLayout()
        layout.addWidget(self.label, 0, 0)
        layout.addWidget(self.lineEdit, 0, 1)
        layout.addWidget(self.view, 1, 0, 1, 2)
        layout.addWidget(self.logViewer, 2, 0, 1, 2)
        self.setLayout(layout)
        self.setWindowTitle("Fetch More Example")
    def updateLog(self, number):
        self.logViewer.append("{0} items added.".format(number))
        
class FileListModel(QtCore.QAbstractListModel):
    numberPopulated = QtCore.Signal(int)
    def __init__(self, parent=None):
        super(FileListModel, self).__init__(parent)
        self.fileCount = 0
        self.fileList = []
    def rowCount(self, parent=QtCore.QModelIndex()):
        return 0 if parent.isValid() else self.fileCount
    def data(self, index, role=QtCore.Qt.DisplayRole):     
        if not index.isValid():
            return 0       
        if (index.row() >= len(self.fileList) or index.row() < 0):
            return 0      
        #Why is the role only SizeHintRole?
        if role == QtCore.Qt.DisplayRole:
            return self.fileList[index.row()]
        elif role == QtCore.Qt.BackgroundRole:
            batch = (index.row() / 100) % 2
            if batch == 0:
                return QtWidgets.QApplication.palette().base()
            else:
                return QtWidgets.QApplication.palette().alternateBase()
        return 0
    def canFetchMore(self, parent):
        if parent.isValid():
            return False
        return self.fileCount < len(self.fileList)
    def fetchMore(self, parent):
        if parent.isValid():
            return
        remainder = len(self.fileList) - self.fileCount
        itemsToFetch = min(100, remainder)
  
        if itemsToFetch <= 0:
            return
        self.beginInsertRows(QtCore.QModelIndex(), self.fileCount, self.fileCount + itemsToFetch - 1)
        
        self.fileCount += itemsToFetch
        self.endInsertRows()
        self.emit(QtCore.SIGNAL("numberPopulated(int)"), itemsToFetch)
    def setDirPath(self, path):
        dir_ = QtCore.QDir(path)
        self.beginResetModel()
        self.fileList = dir_.entryList()        
        self.fileCount = 0
        self.endResetModel()        
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv) if QtWidgets.QApplication.instance() is None else QtWidgets.QApplication.instance()
    mainWin = Window()
    mainWin.show()
    sys.exit(app.exec_())
2

There are 2 best solutions below

2
On BEST ANSWER

My answer will expand the explanation of musicamante's answer a bit.

QVariant is a type of Qt/C++ object that allows handling various types of data but in python that object type is no longer necessary given the dynamic typing of the language, so it is not unnecessary to export it to python (although PyQt5 does). A QVariant() builds an invalid QVariant that a python equivalent is None so return QVariant() can be translated to return None or simply return.

The same can be reversed since when returning 0 this is converted into a 0 in C++ and then it is converted to QVariant(0) which has a different meaning than QVariant(), since the first one stores the number 0 and the second does not store anything since it is an invalid QVariant.

0
On

The problem is in the last return of data(), which should not return 0.

When returning 0, the view or its delegate(s) try to convert the value in a suitable type for that role, and since one of the first roles requested by a view is the size hint (and cannot convert your returned "0" to a valid size hint), the result is that it will not request any other data: since the size is invalid, the item is considered hidden, thus there's no need to ask for other roles.

Just remove the return 0 at the end of data(), as the implicit return is enough.

PS: the self.emit syntax you're using is considered obsolete, change that to self.numberPopulated.emit(itemsToFetch); also, explicit overloads are not required for signals that only have a single signature, so you can remove the ["QString"] from textChanged and [int] from numberPopulated.
I also suggest you to always leave at least a blank line between functions, as it makes your code much more readable.