0

I'm working on a python application that uses a QTableView to display some information. When the user double-clicks a row of the table, I want to extract the model data for that row and do something with it. The problem is, the data method for QAbstractTableModel returns the display string, not the data that was used to create it. So if I have a cell with an integer, I want to extract the actual integer but the data() method returns a string. In this simple case I could simply convert the string back to an int, but if I do something more complicated that may not be an option.

My solution so far has been to store a copy of the data list used to populate the model and look up the data in that using the index from the double click signal. It works, but I feel like there should be a cleaner way of doing this. Am I just overthinking this? Is there a better way of extracting data from a QT model?

For reference my model class looks like this:

from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from ResourceCard import ResourceCard

class ResourceCardModel( QtCore.QAbstractTableModel):
    '''
    classdocs
    '''
    _headers = ["Level", "Color", "Points", "Cost"]

    def __init__(self, cards: list[ ResourceCard]):
        '''
        Constructor
        '''
        super(ResourceCardModel, self).__init__()
        self._data = cards
    
    def data(self, index, role):
        if role == Qt.DisplayRole or role == Qt.EditRole:
            # Look up the key by header index.
            column = index.column()
            row = index.row()
            item:ResourceCard = self._data[row]
            
            match column: 
                case 0:
                    return str(item.level)
                case 1:
                    return str(item.suit.name)
                case 2:
                    return str(item.points)
                case 3:
                    return str(item.cost)
                case _:
                    return ""

    
    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

    def columnCount(self, index):
        # The length of our headers.
        return len(self._headers)
    
    def headerData(self, section, orientation, role):
        # section is the index of the column/row.
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return str(self._headers[section])

            if orientation == Qt.Vertical:
                return ""

In my main class I have some code like:

def __init__(self, *args, obj=None, **kwargs):
    '''
    Constructor
    '''
    QtWidgets.QMainWindow.__init__(self)
    Ui_Widget.__init__(self)
    
    self.setupUi(self)
    self.players = []
    self.gameActions = GameActions(["p1", "p2", "p3", "p4"])
    self.players = self.gameActions.getPlayersList()
    
    self.cards:dict[int,list] = self.gameActions.game.availableResources
    self.lvOneModel = ResourceCardModel( self.cards.get(1))       
    self.lvOneCardsTable.setModel(self.lvOneModel)       
    self.lvOneCardsTable.doubleClicked.connect(self.availableCardDoubleClicked)

def availableCardDoubleClicked(self, item:QModelIndex):
    msgDialog = QtWidgets.QMessageBox(self)
    itemData = self.cards.get(1)[item.row()]
    msgDialog.setText(str(itemData))
    msgDialog.show()
pbuchheit
  • 1,371
  • 1
  • 20
  • 47
  • 1
    Use a [custom role](https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum) (starting from `Qt.UserRole`). Then get the data using `index.data(MyCustomRole)` and implement that in `data()` by checking if the given `role` argument is `MyCustomRole` and return what you want. See the documentation about [item roles in model view programming](https://doc.qt.io/qt-5/model-view-programming.html#item-roles). – musicamante May 18 '23 at 23:47
  • I hadn't considered using roles, but that should work. Thanks. – pbuchheit May 18 '23 at 23:55
  • That *will* work (if correctly implemented), as it's the only proper way of using Qt models. Note that you could just create a custom function in the model, accepting a QModelIndex (or the row) as argument and directly return the item of `self._data` at that row index; obviously, this only works properly for models with just one or two dimensions. That would be conceptually the same, though: the difference is that using the `data()` method provides a more common interface (following the Qt convention and being more modular) which can be used for other purposes, for example in an item delegate. – musicamante May 19 '23 at 00:06

0 Answers0