无法使用 pyqt5 在 table 视图中 select 已经 select 编辑的项目中的单个项目

Cannot select single item from already selected items in table view with pyqt5

我正在玩 model/view 用 pyqt 编程来尝试理解它。 我的问题是,当我尝试 si select in item from the already selected group of items onSelection changed 事件不会触发,并且 selection 行为变得奇怪。 (不仅不能 select 之前 selected 的项目,而且还会发生连续的 select 离子...)。

如果我评论 def data(self, _index, role=Qt.DisplayRole): 方法我得到了我想要的行为,所以我想我在 table。但如果有评论,我无法在 table 中填充数据(你好:))。

我尝试使用 onMouseClick 事件和 selection 行为来处理它,但没有成功。 我想要的 selection 行为也可以在此示例中找到: https://wiki.python.org/moin/PyQt/Reading%20selections%20from%20a%20selection%20model

在下面找到我的代码,这可能有点乱,因为我只是在做一些试验(很抱歉)。 任何评论将不胜感激,非常感谢。

from PyQt5.QtWidgets import QApplication, QTableView, QAbstractItemView
import sys
from PyQt5.QtCore import QAbstractTableModel, Qt, QModelIndex, QItemSelection, QItemSelectionModel, QAbstractItemModel

class myTableModel(QAbstractTableModel):
    def __init__(self, rows, columns, parent=None, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.rowCount = rows
        self.columnCount = columns
        self.table_data = [[None] * columns for _ in range(rows)]
        self.unselectedItems = []

    def rowCount(self, parent):
        return self.rowCount

    def columnCount(self, parent):
        return self.columnCount

    def flags(self, index):
        return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def data(self, _index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole and _index.isValid():
            row = _index.row()
            column = _index.column()
            item = _index.internalPointer()
            if item is not None:
                print(item)
            value = self.table_data[row][column]
            # print('value returned: ' + str(value) + ' row: ' + str(row) + ' col: ' + str(column))
            return value
        else:
            return None

    def setData(self, _index, value, role=Qt.EditRole):
        if role == Qt.EditRole and _index.isValid():
            # print(_index.row())
            # self.arraydata[index.row()] = [value]
            # print('Return from rowCount: {0}'.format(self.rowCount(index)))
            row = _index.row()
            column = _index.column()
            self.table_data[row][column] = value
            self.dataChanged.emit(_index, _index)
            return True
        return QAbstractTableModel.setData(self, index, value, role)

    def updateSelection(self, selected, deselected):
        selectedItems = selected.indexes()
        for _index in selectedItems:
            _text = f"({_index.row()}, {_index.column()})"
            self.setData(_index, _text)
        del selectedItems[:]
        self.unselectedItems = deselected.indexes()
        for _index in self.unselectedItems:
            _text = "previous selection"
            self.setData(_index, _text)
            print('unselected item: ' + str(_index))


class myTableView(QTableView):
    def __init__(self, rowCount, columnCount, model):
        super().__init__()
        self.rowCount = rowCount
        self.columnCount = columnCount
        self.model = model
        self.setModel(model)
        self.selectionModel().selectionChanged.connect(tblModel.updateSelection)
        self.setSelectionMode(QAbstractItemView.ContiguousSelection)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            selectedItems = self.selectedIndexes()
            allIndexes = []
            for i in range(self.rowCount):
                for j in range(self.columnCount):
                    allIndexes.append(self.model.index(i, j))
            # print('all indexes appended')
            indexesToClear = [_index for _index in allIndexes if
                              _index not in selectedItems and _index not in self.model.unselectedItems]
            for _index in indexesToClear:
                valueFromIndex = str(self.model.data(_index, Qt.DisplayRole))
                if valueFromIndex == "previous selection":
                    self.model.setData(_index, "")

    # def mousePressEvent(self, event):
    #     if event.button() == Qt.LeftButton:
    #         self.selectionModel().reset()


app = QApplication(sys.argv)
tblModel = myTableModel(8, 4, app)  # create table model
tblView = myTableView(8, 4, tblModel)
topLeft = tblModel.index(0, 0, QModelIndex())
bottomRight = tblModel.index(5, 2, QModelIndex())

selectionMode = tblView.selectionModel()
selection = QItemSelection(topLeft, bottomRight)
selectionMode.select(selection, QItemSelectionModel.Select)

# set selected indexes text to selection
indexes = selectionMode.selectedIndexes()

for index in indexes:
    text = str(index.row()) + str(index.column())
    tblModel.setData(index, text, role=Qt.EditRole)

tblView.show()
app.exec()

行为不稳定还因为您没有调用 mouseReleaseEvent 的基础 class 实现,它会执行一些正确更新选择所需的操作,包括取消选择之前选择的项目,除了current/new 一个(但行为可以根据视图的 selectionMode 改变)。
另外,请考虑选择模型的 selectionChanged 信号仅发出 changes:如果在选择更改时已经选择了一个项目,它将 not被列在信号参数的 selected 列表中。

为了访问所选项目的完整列表,您需要调用视图的 selectedIndexes() 或其选择 model

class myTableView(QTableView):
    def __init__(self, model):
        super().__init__()
        # no need for these
        # self.rowCount = rowCount
        # self.columnCount = columnCount

        # NEVER overwrite existing class property names!
        # self.model = model

        self.setModel(model)
        self.selectionModel().selectionChanged.connect(self.updateSelection)
        self.setSelectionMode(QAbstractItemView.ContiguousSelection)

    def updateSelection(self, selected, deselected):
        selectedIndexes = self.selectedIndexes()
        for row in range(model.rowCount()):
            for column in range(model.columnCount()):
                _index = model.index(row, column)
                if _index in selectedIndexes:
                    _text = f"({_index.row()}, {_index.column()})"
                elif _index in deselected:
                    _text = "previous selection"
                else:
                    _text = ""
                model.setData(_index, _text)

我还删除了 table init 的 rowCount 和 columnCount 参数,因为它是多余的(如果更改模型大小则容易出错):它们的值仅取决于模型自身的大小,而您应该只能通过它访问它们。

最后,您应该永不覆盖现有的class属性;除了我在上面注释掉的 self.model 之外,这也适用于您在模型中使用的 self.rowCountself.columnCount (这也没有多大意义,因为 public方法会 return 方法本身,导致递归)。