无法使用 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.rowCount
和 self.columnCount
(这也没有多大意义,因为 public方法会 return 方法本身,导致递归)。
我正在玩 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.rowCount
和 self.columnCount
(这也没有多大意义,因为 public方法会 return 方法本身,导致递归)。