Missing QAbstractItemModel.multiData binding

Phil Thompson phil at riverbankcomputing.com
Wed Aug 30 09:37:55 BST 2023


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