简短版
如何在PySide/PyQt中实现对QListWidgetItems
的撤销功能?
Qt教程中的提示?
下面这篇适用于Qt用户(c++)的教程可能有答案,但我不是c++人,所以有些迷惑:Using Undo/Redo with Item Views
详细版
我正在使用QListWidget
来学习PyQt的Undo Framework(借助一篇文章)。当我自己实现一个命令时(比如从列表中删除一个项目),我可以很好地处理撤销/重做。
我还想让小部件中的QListWidgetItems
可编辑。这很容易:只需为每个项目添加ItemIsEditable
标志即可。问题是,我该如何将这样的编辑推送到撤销栈上,以便我可以撤销/重做它们?
下面是一个简单的工作示例,显示了一个列表,允许您删除项目,并撤销/重做这些删除。应用程序显示列表和撤销栈。如何将编辑放到该栈上?
简单的工作示例
from PySide import QtGui, QtCore
class TodoList(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.initUI()
self.show()
def initUI(self):
self.todoList = self.makeTodoList()
self.undoStack = QtGui.QUndoStack(self)
undoView = QtGui.QUndoView(self.undoStack)
buttonLayout = self.buttonSetup()
mainLayout = QtGui.QHBoxLayout(self)
mainLayout.addWidget(undoView)
mainLayout.addWidget(self.todoList)
mainLayout.addLayout(buttonLayout)
self.setLayout(mainLayout)
self.makeConnections()
def buttonSetup(self):
#Make buttons
self.deleteButton = QtGui.QPushButton("Delete")
self.undoButton = QtGui.QPushButton("Undo")
self.redoButton = QtGui.QPushButton("Redo")
self.quitButton = QtGui.QPushButton("Quit")
#Lay them out
buttonLayout = QtGui.QVBoxLayout()
buttonLayout.addWidget(self.deleteButton)
buttonLayout.addStretch()
buttonLayout.addWidget(self.undoButton)
buttonLayout.addWidget(self.redoButton)
buttonLayout.addStretch()
buttonLayout.addWidget(self.quitButton)
return buttonLayout
def makeConnections(self):
self.deleteButton.clicked.connect(self.deleteItem)
self.quitButton.clicked.connect(self.close)
self.undoButton.clicked.connect(self.undoStack.undo)
self.redoButton.clicked.connect(self.undoStack.redo)
def deleteItem(self):
rowSelected=self.todoList.currentRow()
rowItem = self.todoList.item(rowSelected)
if rowItem is None:
return
command = CommandDelete(self.todoList, rowItem, rowSelected,
"Delete item '{0}'".format(rowItem.text()))
self.undoStack.push(command)
def makeTodoList(self):
todoList = QtGui.QListWidget()
allTasks = ('Fix door', 'Make dinner', 'Read',
'Program in PySide', 'Be nice to everyone')
for task in allTasks:
todoItem=QtGui.QListWidgetItem(task)
todoList.addItem(todoItem)
todoItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
return todoList
class CommandDelete(QtGui.QUndoCommand):
def __init__(self, listWidget, item, row, description):
super(CommandDelete, self).__init__(description)
self.listWidget = listWidget
self.string = item.text()
self.row = row
def redo(self):
self.listWidget.takeItem(self.row)
def undo(self):
addItem = QtGui.QListWidgetItem(self.string)
addItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.listWidget.insertItem(self.row, addItem)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
myList=TodoList()
sys.exit(app.exec_())
请注意,我之前在QtCentre发布了这个问题的早期版本。
(Note: I posted an earlier version of this question on QtCentre.)
currentTextChanged
而不是currentItemChanged
,但坦率地说,我更喜欢前者(你使用的), 因为它对于像复选框状态这样的事物更具可扩展性。一个问题:currentItemChanged
文档说它会发出current
和previous
项目,但它似乎只将新选择的(当前)项目发送到其槽中,即使在 Qt 文档中它说void QListWidget.currentItemChanged(currentItem, previousItem)
(http://qt-project.org/doc/qt-4.8-snapshot/qlistwidget.html#currentItemChanged)。 - ericNone
!这是个愚蠢的错误。一旦我点击另一个项目,我们就会像信号中预期的那样拥有当前和上一个项目。 - eric