【发布时间】:2021-06-20 02:57:25
【问题描述】:
在 QTableWidget 中有不同的方式来改变行顺序:
- 通过拖放进行内部移动
- 通过单独的按钮将所选行向上或向下移动一个位置
事实证明,这两种方法对于较长的列表和我的特殊目的不是很实用。 因此,我尝试通过更改单元格值来分配新位置来实现以下方法:
- 第一列保存当前位置编号
- 通过编辑这些数字,我想将新位置分配给这一行
- 我希望只允许编辑第一列
- 如果输入了无效的位置编号(在行数范围内),则不应更改任何内容
- 如果输入了有效的位置编号,则第一列中的其他位置编号会相应修改。
- 然后我可以通过单击列标题以新顺序获取重新排列的行以按第一列排序。
示例:职位编号1,2,3,4,5。
如果我将 row3,column1 中的值从 3 更改为 1,则第一列中的位置编号应更改如下:
1 --> 2
2 --> 3
3 --> 1
4 --> 4
5 --> 5
但是,setEditTriggers(QAbstractItemView.NoEditTriggers) 和 setEditTriggers(QAbstractItemView.DoubleClicked) 似乎有问题。
根据我尝试的一些不同的代码变体,看起来我仍然得到一个EditTrigger,尽管我认为我已经通过self.setEditTriggers(QAbstractItemView.NoEditTriggers) 禁用了 EditTriggers。
或者我得到RecursionError: maximum recursion depth exceeded while calling a Python object。
或TypeError: '>' not supported between instances of 'NoneType' and 'int'。
我希望我能把问题说清楚。我在这里做错了什么?
代码:(最小化的非工作示例。应该是复制 & 粘贴 & 运行)
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QAction, QTableWidget, QTableWidgetItem, QVBoxLayout, QPushButton, QAbstractItemView
from PyQt5.QtCore import pyqtSlot, Qt
import random
class MyTableWidget(QTableWidget):
def __init__(self):
super().__init__()
self.setColumnCount(3)
self.setRowCount(7)
self.setSortingEnabled(False)
header = self.horizontalHeader()
header.setSortIndicatorShown(True)
header.sortIndicatorChanged.connect(self.sortItems)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.col_pos = 0
self.oldPosValue = None
self.manualChange = False
self.cellDoubleClicked.connect(self.cell_doubleClicked)
self.cellChanged.connect(self.cell_changed)
def cell_doubleClicked(self):
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
if self.currentColumn() != self.col_pos: # editing allowed only for this column
return
self.setEditTriggers(QAbstractItemView.DoubleClicked)
try:
self.oldPosValue = int(self.currentItem().text())
except:
pass
self.manualChange = True
def cell_changed(self):
if not self.manualChange:
return
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
try:
newPosValue = int(self.currentItem().text())
except:
newPosValue = None
rowChanged = self.currentRow()
print("Value: {} --> {}".format(self.oldPosValue, newPosValue))
if newPosValue>0 and newPosValue<=self.rowCount():
for row in range(self.rowCount()):
if row != rowChanged:
try:
value = int(self.item(row,self.col_pos).text())
if value<newPosValue:
self.item(row,self.col_pos).setData(Qt.EditRole,value+1)
except:
print("Error")
pass
else:
self.item(rowChanged,self.col_pos).setData(Qt.EditRole,self.oldPosValue)
print("New value outside range")
self.manualChange = True
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 table'
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(0,0,400,300)
self.layout = QVBoxLayout()
self.tw = MyTableWidget()
self.layout.addWidget(self.tw)
self.pb_refill = QPushButton("Refill")
self.pb_refill.clicked.connect(self.on_click_pb_refill)
self.layout.addWidget(self.pb_refill)
self.setLayout(self.layout)
self.show()
@pyqtSlot()
def on_click_pb_refill(self):
self.tw.setEditTriggers(QAbstractItemView.NoEditTriggers)
for row in range(self.tw.rowCount()):
for col in range(self.tw.columnCount()):
if col==0:
number = row+1
else:
number = random.randint(1000,9999)
twi = QTableWidgetItem()
self.tw.setItem(row, col, twi)
self.tw.item(row, col).setData(Qt.EditRole,number)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
结果:
【问题讨论】:
-
如果你通过连接标题的 sortIndicatorChanged 信号来禁用排序,为什么要禁用排序?
-
@musicamante 抱歉,这是早期版本的剩余部分。可以删除。
-
好的。另一个问题:重新排序是否只能通过单击标题来完成,或者也可以在编辑更改时完成?我的意思是,如果我将值从 3 更改为 1,模型应该自动排序还是应该仅在从标题手动排序时发生?
-
@musicamante 手动排序可能会更好。我之前在填充表格期间使用自动排序遇到了这个问题。这就是我遵循建议关闭排序或进行手动排序的原因:请参阅 stackoverflow.com/q/62284220/7295599 和 stackoverflow.com/q/66506588/7295599
标签: python pyqt pyqt5 qtablewidget