Missing QAbstractItemModel.multiData binding

Phil Thompson phil at riverbankcomputing.com
Wed Aug 30 13:32:16 BST 2023


If you rebuild the current PyQt6 snapshot with the next SIP snapshot 
then you will get the proper iterator behaviour.

Phil

On 30/08/2023 09:37, Phil Thompson wrote:
> That's the correct code at the moment. However I've realised that it's
> very easy to add support for Python iteration.
> 
> Phil
> 
> On 29/08/2023 19:41, Charles wrote:
>> Apparently the problem is with using python iteration over the
>> QModelRoleDataSpan object (does it supports python iteration)?
>> 
>> This code instead works fine
>> 
>>     def multiData(self, index, roleDataSpan):
>>         item = self._items[index.row()]
>>         itemData = self._itemData(item, index.column())
>>         for i in range(roleDataSpan.length()):
>>             roleData = roleDataSpan[i]
>>             if roleValue := itemData.get(roleData.role()):
>>                 roleData.setData(roleValue)
>>             else:
>>                 roleData.clearData()
>> 
>> On Tue, Aug 29, 2023 at 8:17 PM Charles <peacech at gmail.com> wrote:
>> 
>>> Hi Phil, here is an example standalone code
>>> 
>>> import sys
>>> from dataclasses import dataclass
>>> 
>>> from PyQt6.QtCore import QAbstractTableModel, Qt
>>> from PyQt6.QtWidgets import QApplication, QMainWindow, QTableView
>>> 
>>> 
>>> @dataclass
>>> class Item:
>>>     name: str
>>>     weight: int
>>> 
>>> 
>>> class Model(QAbstractTableModel):
>>>     def __init__(self, parent=None):
>>>         super().__init__(parent)
>>>         self._items = [Item('A', 10), ('B', 11)]
>>> 
>>>     def rowCount(self, index):
>>>         return len(self._items)
>>> 
>>>     def columnCount(self, index):
>>>         return 2
>>> 
>>>     def _itemData(self, item, column):
>>>         match column:
>>>             case 0:
>>>                 return {Qt.ItemDataRole.DisplayRole: item.name}
>>>             case 1:
>>>                 return {Qt.ItemDataRole.DisplayRole: 
>>> str(item.weight)}
>>> 
>>>     # crash
>>>     def multiData(self, index, roleDataSpan):
>>>         item = self._items[index.row()]
>>>         itemData = self._itemData(item, index.column())
>>>         for roleData in roleDataSpan:
>>>             if roleValue := itemData.get(roleData.role()):
>>>                 if existingData := roleData.data():
>>>                     existingData.setValue(roleValue)
>>>                 else:
>>>                     roleData.setData(roleValue)
>>>             else:
>>>                 roleData.clearData()
>>> 
>>>     # works
>>>     # def multiData(self, index, roleDataSpan):
>>>     #     pass
>>> 
>>> 
>>> class Widget(QTableView):
>>>     def __init__(self, parent=None):
>>>         super().__init__(parent)
>>>         self.setModel(Model(self))
>>> 
>>> 
>>> class Window(QMainWindow):
>>>     def __init__(self):
>>>         super().__init__()
>>>         self.setCentralWidget(Widget(self))
>>> 
>>> 
>>> app = QApplication(sys.argv)
>>> win = Window()
>>> win.show()
>>> app.exec()
>>> 
>>> On Tue, Aug 29, 2023 at 7:59 PM Phil Thompson 
>>> <phil at riverbankcomputing.com>
>>> wrote:
>>> 
>>>> Can you create a short complete example I can use?
>>>> 
>>>> Thanks,
>>>> Phil
>>>> 
>>>> On 29/08/2023 13:40, Charles wrote:
>>>> > I tried using multiData with this code snippet but it seems to crash
>>>> > when
>>>> > setting roleData.setData(...). Also I notice that the role values might
>>>> > be
>>>> > negative. (Btw, ic is icecream print).
>>>> >
>>>> >     def _itemData(self, item, col):
>>>> >         match col.name:
>>>> >             case 'code':
>>>> >                 return {
>>>> >                     Qt.DisplayRole: item.code,
>>>> >                 }
>>>> >             case 'earnings_chg':
>>>> >                 return {
>>>> >                     Qt.DisplayRole: f'{item.earnings_chg}%',
>>>> >                 }
>>>> >             case 'time':
>>>> >                 return {
>>>> >                     Qt.DisplayRole: item.time.strftime('%Y-%m-%d'),
>>>> >                 }
>>>> >             case 'earnings':
>>>> >                 return {
>>>> >                     Qt.DisplayRole: formatMoney(item.earnings),
>>>> >                 }
>>>> >
>>>> >     def multiData(self, index, roleDataSpan):
>>>> >         ic(index, roleDataSpan)
>>>> >         item = self._items[index.row()]
>>>> >         col = self.COLUMNS[index.column()]
>>>> >         itemData = self._itemData(item, col)
>>>> >         for roleData in roleDataSpan:
>>>> >             role = roleData.role()
>>>> >             ic(role)
>>>> >             if value := itemData.get(role):
>>>> >                 roleData.setData(value)
>>>> >             else:
>>>> >                 match role:
>>>> >                     case Qt.FontRole:
>>>> >                         roleData.setData(style.TABLE_FONT)
>>>> >                     case Qt.TextAlignmentRole:
>>>> >                         align = col.align
>>>> >                         if align == 'C':
>>>> >                             roleData.setData(Qt.AlignCenter)
>>>> >                         elif align == 'R':
>>>> >                             roleData.setData(Qt.AlignVCenter |
>>>> > Qt.AlignRight)
>>>> >
>>>> > ic| earnings.py:66 in multiData()
>>>> >     index: <PyQt6.QtCore.QModelIndex object at 0x000001D17B77D000>
>>>> >     roleDataSpan: <PyQt6.QtCore.QModelRoleDataSpan object at
>>>> > 0x000001D17B77D4D0>
>>>> > ic| earnings.py:72 in multiData()- role: 6
>>>> > ic| earnings.py:72 in multiData()- role: 7
>>>> > ic| earnings.py:72 in multiData()- role: 9
>>>> > ic| earnings.py:72 in multiData()- role: 10
>>>> > ic| earnings.py:72 in multiData()- role: 1
>>>> > ic| earnings.py:72 in multiData()- role: 0
>>>> > ic| earnings.py:72 in multiData()- role: 8
>>>> > ic| earnings.py:72 in multiData()- role: -948493808
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 2033014364
>>>> > ic| earnings.py:72 in multiData()- role: 2
>>>> > ic| earnings.py:72 in multiData()- role: 16777216
>>>> > ic| earnings.py:72 in multiData()- role: 1893028080
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 2033014458
>>>> > ic| earnings.py:72 in multiData()- role: 2
>>>> > ic| earnings.py:72 in multiData()- role: 2033021932
>>>> > ic| earnings.py:72 in multiData()- role: 3
>>>> > ic| earnings.py:72 in multiData()- role: 524288
>>>> > ic| earnings.py:72 in multiData()- role: 1893028080
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 1897532384
>>>> > ic| earnings.py:72 in multiData()- role: 2033022038
>>>> > ic| earnings.py:72 in multiData()- role: 1
>>>> > ic| earnings.py:72 in multiData()- role: 22528
>>>> > ic| earnings.py:72 in multiData()- role: 1893028080
>>>> >
>>>> > On Sun, Aug 27, 2023 at 10:45 PM Phil Thompson
>>>> > <phil at riverbankcomputing.com>
>>>> > wrote:
>>>> >
>>>> >> On 14/08/2023 16:21, Jakub Fránek wrote:
>>>> >> > Hello
>>>> >> >
>>>> >> > I am working on a PyQt app that features a large QTableView driven
>>>> by a
>>>> >> > custom QAbstractTableModel implementation. Profiling shows that even
>>>> >> > after
>>>> >> > heavy optimization, the program spends a lot of time calling
>>>> >> > QAbstractItemModel.data method.
>>>> >> >
>>>> >> > I think that QAbstractItemModel.multiData method could improve the
>>>> >> > performance considerably, however it seems that PyQt does not support
>>>> >> > this
>>>> >> > binding (since it does not even support QModelRoleDataSpan).
>>>> >> >
>>>> >> > My question is: is there any plan of supporting
>>>> >> > QAbstractItemModel.multiData method in the future? If there is no
>>>> such
>>>> >> > plan
>>>> >> > yet, I would like to cast my vote and humbly request this binding in
>>>> >> > some
>>>> >> > future version of PyQt.
>>>> >>
>>>> >> multiData() and QModelRoleSpan are implemented in the next snapshot -
>>>> >> please test.
>>>> >>
>>>> >> Phil
>>>> >>
>>>> 
>>> 


More information about the PyQt mailing list