
我目前使用的是 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 做成 ES 式,把操作抽象成 args=>state Pre=>state Next 然后每个操作内容都存到队列,undo 了从头执行一遍。也可以每隔几个设置快照,从最近的快照开始执行历史操作。 还可以限制队列的长度,到达最大长度则每次也把起始状态更新,这样内存消耗固定,但只能 undo 几次。 你这个代码就相当于每次操作都有快照,内存消耗会比较大。 QUndoStack 的话,看起来是每个操作都要实现 undo 和 redo,redo 是正向的 state Pre=>tate Next,还要对每种操作额外实现 undo 的 state Next => state Pre,然后 push 执行 redo,pop 执行 undo |