You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从PyQt QTableView中获取行的原始数据?

问题描述

我正在开发一个用QTableView展示信息的Python应用,当用户双击表格行时,想提取该行的原始模型数据做后续操作。但QAbstractTableModel的data()方法返回的是显示用的字符串,不是原始数据——比如单元格里的整数,我要的是实际的int类型,而不是转成字符串后再转回来(复杂场景下这种转换不可行)。

目前我是通过单独存储一份数据列表,双击时用索引去查找,虽然能用,但总觉得应该有更简洁的实现方式。有没有更好的从Qt模型中提取原始数据的方法?

现有代码

模型类代码

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 ""

主类相关代码

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()
解决方案

方法1:自定义数据角色(推荐)

Qt允许自定义角色(Role)来存储和获取原始数据,这种方式完全符合Qt的MVC设计模式,能保证数据一致性:

  1. 在模型类中定义自定义角色,用于返回原始数据
  2. 修改data()方法,针对自定义角色返回对应原始值(可以是单个字段的原始类型,也可以是整个ResourceCard对象)

修改后的模型类示例:

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

class ResourceCardModel(QtCore.QAbstractTableModel):
    _headers = ["Level", "Color", "Points", "Cost"]
    # 自定义角色,用于返回原始数据(可定义多个角色对应不同需求)
    RawCardRole = Qt.UserRole + 1
    RawFieldRole = Qt.UserRole + 2

    def __init__(self, cards: list[ResourceCard]):
        super().__init__()
        self._data = cards
    
    def data(self, index, role):
        if not index.isValid():
            return None
        
        row = index.row()
        column = index.column()
        item: ResourceCard = self._data[row]

        if role == Qt.DisplayRole or role == Qt.EditRole:
            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 ""
        # 返回整行的原始ResourceCard对象
        elif role == self.RawCardRole:
            return item
        # 返回对应列的原始字段值
        elif role == self.RawFieldRole:
            match column:
                case 0:
                    return item.level
                case 1:
                    return item.suit
                case 2:
                    return item.points
                case 3:
                    return item.cost
                case _:
                    return None
        return None

    # rowCount、columnCount、headerData方法保持不变

主类中的双击事件可改为:

def availableCardDoubleClicked(self, index: QtCore.QModelIndex):
    msgDialog = QtWidgets.QMessageBox(self)
    # 获取整行的原始ResourceCard对象
    rawCard = index.model().data(index, ResourceCardModel.RawCardRole)
    msgDialog.setText(str(rawCard))
    msgDialog.show()

    # 如果需要单个字段的原始值(比如Level的整数类型):
    # rawLevel = index.model().data(index, ResourceCardModel.RawFieldRole)
    # print(type(rawLevel))  # 输出 <class 'int'>

方法2:给模型添加公共方法获取原始数据

如果不想自定义角色,可以直接在模型类中添加一个公共方法,用来返回指定行的原始数据:

class ResourceCardModel(QtCore.QAbstractTableModel):
    # ... 其他代码 ...
    def getRowRawData(self, row: int) -> ResourceCard:
        """返回指定行的原始ResourceCard对象"""
        if 0 <= row < len(self._data):
            return self._data[row]
        return None

然后在主类的双击事件中调用:

def availableCardDoubleClicked(self, index: QtCore.QModelIndex):
    msgDialog = QtWidgets.QMessageBox(self)
    rawCard = self.lvOneModel.getRowRawData(index.row())
    msgDialog.setText(str(rawCard))
    msgDialog.show()

为什么不推荐当前的方案?

你现在单独在主类中维护一份数据列表,一旦模型中的数据发生更新(比如添加、删除行),主类中的列表必须同步修改,否则会出现数据不一致的问题。而上面两种方法都是直接从模型获取数据,完全遵循Qt的MVC设计理念,能避免这类问题。

内容的提问来源于stack exchange,提问作者pbuchheit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:13:13