I have a QDockWidget in a QMainWindow, the QDockWidget contains a stack of QUndoViews containing commands that I want to change the text of in the QUndoView. You accomplish this by doing command.setText(). However, the QUndoView is only showing the updated text when I hover over it with the mouse. Not even will calling the below right after setText work:
self.editor().undoView().setFocus()
self.editor().undoView().update()
self.editor().undoView().hide()
self.editor().undoView().show()
Here is the minimal code to demonstrate the problem:
from PyQt5.QtWidgets import (QMainWindow, QDockWidget, QPushButton, QLabel, \
QStackedWidget, QUndoStack, QUndoCommand, QApplication, \
QUndoView)
import sys
from PyQt5.QtCore import QObject, pyqtSignal, Qt
class Command(QUndoCommand):
def __init__(self):
super().__init__()
def undo(self):
print("Command Undone")
def redo(self):
print("Command Redone")
class CommandTimeline(QDockWidget):
def __init__(self):
super().__init__()
self.stackedWidget = QStackedWidget()
self.setWidget(self.stackedWidget)
def addUndoView(self, stack):
view = QUndoView(stack)
self.stackedWidget.addWidget(view)
return view
class Obj(QObject):
nameChanged = pyqtSignal(str)
def __init__(self, name):
super().__init__()
self._name = name
def setName(self, name):
self._name = name
self.nameChanged.emit(name)
def __str__(self):
return self._name
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.undoStack = QUndoStack()
self.commandTimeline = CommandTimeline()
self.commandTimeline.addUndoView(self.undoStack)
self.addDockWidget(Qt.LeftDockWidgetArea, self.commandTimeline)
button = QPushButton("Click")
self.setCentralWidget(button)
button.clicked.connect(self.changeObjName)
self.obj = Obj("A")
self.addCommand()
def addCommand(self):
def getText(obj):
return "The command text: " + str(obj)
command = Command()
command.setText(getText(self.obj))
self.obj.nameChanged.connect(lambda name: command.setText(getText(self.obj)))
self.undoStack.push(command)
def changeObjName(self):
self.obj.setName("B")
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec_())
Run this code and notice that after clicking the button, the text in the undoview doesn't change, but after hovering over the dockwidget, the text updates immediately.
How can I trigger the update upon command.setText()
?
QUndoCommand
does not notify if the text is changed, it is designed to have a static text. So we must do that task, in this case use setFocus()
, you signal that it does not work, but for me it works so you probably have not implemented it correctly or your MCVE is not verifiable.
import sys
from functools import partial
from PyQt5.QtWidgets import (QMainWindow, QDockWidget, QPushButton, QLabel, \
QStackedWidget, QUndoStack, QUndoCommand, QApplication, \
QUndoView)
from PyQt5.QtCore import QObject, pyqtSignal, Qt
class Command(QUndoCommand):
def undo(self):
print("Command Undone")
def redo(self):
print("Command Redone")
class CommandTimeline(QDockWidget):
def __init__(self):
super().__init__()
self.stackedWidget = QStackedWidget()
self.setWidget(self.stackedWidget)
def addUndoView(self, stack):
view = QUndoView(stack)
self.stackedWidget.addWidget(view)
return view
class Obj(QObject):
nameChanged = pyqtSignal(str)
def __init__(self, name):
super().__init__()
self._name = name
def setName(self, name):
self._name = name
self.nameChanged.emit(name)
def __str__(self):
return self._name
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.undoStack = QUndoStack()
self.commandTimeline = CommandTimeline()
self.view = self.commandTimeline.addUndoView(self.undoStack)
self.addDockWidget(Qt.LeftDockWidgetArea, self.commandTimeline)
button = QPushButton("Click")
self.setCentralWidget(button)
button.clicked.connect(self.changeObjName)
self.obj = Obj("A")
self.addCommand()
def addCommand(self):
command = Command()
command.setText("The command text: {}".format(self.obj))
self.obj.nameChanged.connect(partial(self.onNameChanged, command))
self.undoStack.push(command)
def changeObjName(self):
self.obj.setName("B")
def onNameChanged(self, command, name):
fw = QApplication.focusWidget()
command.setText("The command text: {}".format(name))
self.view.setFocus()
fw.setFocus()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())