我目前使用的是 subclass 的QTableView
和QAbstractTableModel
.
在 Stackoverflow 上搜索了一阵子,看起来正统的做法之一是在 Model 中包含一个QUndoStack
,然后另外写一个 subclass QUndoCommand
的类。但是我没有搞明白,到底在哪里调用最后这个类,所以也就没写出来。
我目前用的是笨办法。因为表格中的数据很小,大概就 500KB 左右。每次进行修改,就把上一次的数据直接深拷贝。同时在 View 里头截获 keypress,如果是Ctrl+Z
就调用 Model 中的undo()
,直接把深拷贝的原样直接覆盖到现有的数据上。简化后的代码如下:
# Model
class SlotConfigModel(QAbstractTableModel):
def __init__(self, data):
super(SlotConfigModel, self).__init__()
self._data = data
self._olddata: list = []
self._lable = ("id", "Probability", "Reel 1", "Reel 2", "Reel 3")
def setData(self, index, value, role=None):
if role == Qt.EditRole:
# 每次编辑都把未修改前的 data 存档(需要深复制因为 data 是 list),undo 的时候覆盖。
self._olddata.append(copy.deepcopy(self._data))
print("Old data saved to list")
row = index.row()
col = index.column()
self._data[index.row()][index.column()] = value
return True
return False
def undo(self):
if len(self._olddata) > 0:
self._data = copy.deepcopy(self._olddata[-1])
print("Undo successful!")
self._olddata.pop()
else:
print("Nothing to Undo!")
# View
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.tableview = QTableView()
self.horizontal_header = self.tableview.horizontalHeader()
self.horizontal_header.setStretchLastSection(True)
data = [
[15, 0.13, "[101]", "[101]", "[101]"],
[15, 0.03, "[101]", "[101]", "[102, 104, 5, 2, 1]"],
[15, 0.04, "[101]", "[102;104;5;2;1]", "[101;102;104;5;2;1]"],
[16, 0.20, "[5]", "[5]", "[5]"],
[16, 0.50, "[101]", "[5]", "[102, 104, 5, 2, 1]"],
[16, 0.10, "[5]", "[102;104;5;2;1]", "[101;102]"]
]
self.model = SlotConfigModel(data)
self.tableview.setModel(self.model)
self.setCentralWidget(self.tableview)
def keyPressEvent(self, event):
if event.key() == (Qt.Key_Control and Qt.Key_Z):
# 如果 Ctrl-Z 则调用 Model 中的 undo()
self.model.undo()
# 手动更新 View,否则直到下一次交互才更新
self.tableview.viewport().update()
QTableView.keyPressEvent(self.tableview, event)
我觉得这个不是长久之计,比如说我还要写Insertrow()
等方法,这样弄起来还是颇为费劲,最好是能用QUndoCommand
来,求问有没有什么具体的范例?多谢!
1
islxyqwe 2020-05-19 09:15:10 +08:00 1
做成 ES 式,把操作抽象成 args=>state Pre=>state Next
然后每个操作内容都存到队列,undo 了从头执行一遍。也可以每隔几个设置快照,从最近的快照开始执行历史操作。 还可以限制队列的长度,到达最大长度则每次也把起始状态更新,这样内存消耗固定,但只能 undo 几次。 你这个代码就相当于每次操作都有快照,内存消耗会比较大。 QUndoStack 的话,看起来是每个操作都要实现 undo 和 redo,redo 是正向的 state Pre=>state Next,还要对每种操作额外实现 undo 的 state Next => state Pre,然后 push 执行 redo,pop 执行 undo |