【问题标题】:Cannot select single item from already selected items in table view with pyqt5无法使用pyqt5从表格视图中已选择的项目中选择单个项目
【发布时间】:2019-12-27 12:49:22
【问题描述】:

我正在使用 pyqt 进行模型/视图编程以尝试理解它。 我的问题是,当我从已选择的项目组中尝试 si select in item 时,不会触发 onSelection changed 事件,并且选择行为变得很奇怪。 (不仅不能从先前选择的项目中选择项目,而且还会发生连续的选择......)。

如果我评论 def data(self, _index, role=Qt.DisplayRole): 方法,我会得到我想要的行为,所以我想我在表格中填充数据的方式上遗漏了一些东西。但是,如果已对此进行注释,我将无法在表中填充数据(您好 :))。

我尝试使用 onMouseClick 事件和选择行为来处理它,但没有成功。 我想要的选择行为也可以在这个例子中找到: 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()

【问题讨论】:

    标签: python pyqt pyqt5 tableview


    【解决方案1】:

    该行为也不稳定,因为您没有调用mouseReleaseEvent 的基类实现,它执行正确更新选择所需的一些操作,包括取消选择除当前/新项目之外的先前选择的项目(但行为可以根据视图的selectionMode进行更改。
    另外,考虑到选择模型的 selectionChanged 信号只发出 更改:如果在选择更改时已经选择了一个项目,它将不会在@987654324 中列出@信号参数列表。

    为了访问所选项目的完整列表,您需要调用视图的 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 参数,因为它是多余的(如果您更改模型大小,很容易出错):它们的值仅取决于模型自身的大小,您只能通过它访问它们.

    最后,你应该永远覆盖现有的类属性;除了我在上面评论的self.model 之外,这也适用于您在模型中使用的self.rowCountself.columnCount(这也没有多大意义,因为公共方法本身会返回方法,从而导致递归) .

    【讨论】:

    • 感谢您的回答。将 updateSelection 移至 myTableView 类并使用模型作为参数似乎很正确。但是这样做我无法在 updateSelection 方法中访问模型。我得到:对于范围内的行(self.model.rowCount()):AttributeError:'builtin_function_or_method'对象没有属性'rowCount'
    • @RustyBucketBay 与 PyQt 对象的所有属性和几乎所有变量一样(特殊情况除外,例如 QStyleOption 变量),model 不是“python 属性”,但必须作为功能;只需致电self.model()
    • 我尝试添加这个model = self.model(),在model() 和rowCount() 中包含所有括号选项,同时删除和包含self。我无法访问模型参数。对不起,我的无能,您能否在答案中提供完整的工作脚本?提前致谢
    • 已解决。我的错。感谢您的回答和所有周围的解释。那非常有用。我借机问,我怎么知道不应该被覆盖的默认类属性? (我在文档中查找了多重继承,我并不清楚)。我的意思是,关于你所说的关于 tableView 的模型,它不应该被覆盖,因为它是一个类属性
    • 没问题!关于命名问题,不幸的是你只能从经验中得到它。从学习基类函数和属性开始(这总是有用的),最重要的是 QObject 和 QWidget,然后记住你总是可以阅读出现在每个 Qt 类描述顶部的“所有成员列表,包括继承的成员”链接.幸运的是,Qt 有一个非常具有描述性和一致的命名约定,所以,在大多数情况下,如果你最终命名的东西看起来“如此明显以至于它应该已经存在”,它可能是 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-10
    相关资源
    最近更新 更多