QListWidget item editing
-
@dheerendra
Yep, I looked through all those. None of them pick up, e.g., terminating editing, at least, so no good.... -
terminatingEditing has to be custom. You have no choice other the your own delegate :)
-
@dheerendra
Yep, that's fine, as I wrote to @VRonin above, I am presently having a go at his code (converted to Python/PyQt!) to get me going.... -
@VRonin
OK, I have come up with the basics which I believe emulates/corresponds to your C++ as best I can. I now have a further question about something which you don't do in your example which I would like.First, I'll paste the guts of my Python/PyQt code. You'll see it's rather different in signals/slots/emits. This may help or hinder, I don't know, feel free to ignore if that's best.
class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate): # class variable for "editStarted" signal editStarted = QtCore.pyqtSignal(name='editStarted') # class variable for "editFinished" signal editFinished = QtCore.pyqtSignal(name='editFinished') def __init__(self, parent: QtCore.QObject=None): super().__init__(parent) self.closeEditor.connect(self.editFinished) def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex): self.editStarted.emit() return super().setEditorData(editor, index) class JEditableListWidget(QtWidgets.QListWidget): # class variable for "editStarted" signal editStarted = QtCore.pyqtSignal(name='editStarted') # class variable for "editFinished" signal editFinished = QtCore.pyqtSignal(name='editFinished') def __init__(self, parent: QtWidgets.QWidget=None): super().__init__(parent) styledItemDelegate = JEditableListStyledItemDelegate(self) styledItemDelegate.editStarted.connect(self.editStarted) styledItemDelegate.editFinished.connect(self.editFinished) self.setItemDelegate(styledItemDelegate)
The above "works". The
JEditableListStyledItemDelegate
emits its owneditStarted
/Finished
signals. TheJEditableListWidget
"redirects" those signals to its owneditStarted
/Finished
signals, for the outside world to slot onto. So far so good?Now, if you look at a
QListWidget
signal for an item like http://doc.qt.io/qt-5/qlistwidget.html#itemChanged, its signature isQListWidget::itemChanged(QListWidgetItem *item)
. Hence it sends to the outside world a parameter of theQListWidgetItem
which has been changed. MyeditStarted
/Finished
signals should do the same.Now I get stuck as to how I'm supposed to do that.
I start by changing my
JEditableListWidget
signals to:class JEditableListWidget(QtWidgets.QListWidget): # class variable for "editStarted" signal, including item editStarted = QtCore.pyqtSignal(QtWidgets.QListWidgetItem, name='editStarted') # class variable for "editFinished" signal, including item editFinished = QtCore.pyqtSignal(QtWidgets.QListWidgetItem, name='editFinished')
The extra first parameter of
QtWidgets.QListWidgetItem
declares the signal as forwarding the item as a parameter.Now how do I pass that? I believe that (a) I will need a lambda for the signal and (b) I need to know what item is being edited. I think where I had:
styledItemDelegate.editStarted.connect(self.editStarted)
I now need something like:
styledItemDelegate.editStarted.connect(lambda: self.editStarted(someItemWidget))
[Actually I suspect it might be more like:
styledItemDelegate.editStarted.connect(lambda: self.editStarted.emit(someItemWidget))
]
Could you tell me:-
Is this how you would do it in C++ (e.g. with a lambda)? (If not, I may be doing it wrong.) Could you show me your C++ for how you would do this and I will attempt to figure it to PyQt? (Also, I believe your code omitted an
emit
because I know that's a NO-OP in C++; but could you include it wherever it should be, because in PyQt you can see we have to useemit
as a function.) -
How do I calculate what the
someItemWidget
will be, from inside myJEditableListWidget
?
-
-
It's actually super easy.
In the delegate:- add an argument of type
QtCore.QModelIndex
to the signals. - remove
self.closeEditor.connect(self.editFinished)
- change
self.editStarted.emit()
toself.editStarted.emit(index)
- add
def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex): self.editFinished.emit(index) return super().destroyEditor(editor, index)
Now in
JEditableListWidget
you can connect a slot that accepts aQtCore.QModelIndex
. To find the correspondingQListWidgetItem
you can just callitem(index.row())
- add an argument of type
-
@VRonin
TVM. I'll implement next week. You're passing theQtCore.QModelIndex
available in theJEditableListStyledItemDelegate
class up the signal chain to indicate whichQListWidgetItem
was acted upon. I thought theJEditableListWidget
class would know which item in the list was being edited, but seemingly not? -
@VRonin
Well, I followed your principle of having theQStyledItemDelegate
emit signals with theQModelIndex
as a parameter.I diverged from yours slightly. Given that I'm now overriding
destroyEditor()
so as to access the index, it seems to me that I might as well be consistent and overridecreateEditor()
for beginning editing. So the following two overrides:class JEditableListStyledItemDelegate(QtWidgets.QStyledItemDelegate): # class variable for "editStarted" signal, with QModelIndex parameter editStarted = QtCore.pyqtSignal(QtCore.QModelIndex, name='editStarted') # class variable for "editFinished" signal, with QModelIndex parameter editFinished = QtCore.pyqtSignal(QtCore.QModelIndex, name='editFinished') def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex): editor = super().createEditor(parent, option, index) if editor is not None: self.editStarted.emit(index) return editor def destroyEditor(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex): self.editFinished.emit(index) return super().destroyEditor(editor, index)
The above seems to give me fairly reliable signals for what I want, e.g. disable other buttons for doing things to
QListWidget
(add row, delete row) while the user is editing a row.Seem reasonable to you? Thanks for all your input.
-
@JonB said in QListWidget item editing:
Given that I'm now overriding destroyEditor() so as to access the index, it seems to me that I might as well be consistent and override createEditor() for beginning editing.
Only doubt I have is if the delegate recycles the editor. Try going from editing 1 cell to editing another one directly and see if both create and destroy are called or it just calls
setEditorData
-
@VRonin
OK, thanks! You should have said that concern was why you had overridden different/"inconsistent" methods, I had no idea why! Tried with direct click to edit another item, all is well, does call destroy and then create.So I shall mark what I did as the solution to my original question. TVM.
P.S.
Your Xmas hat looks good. But a lot of others are also wearing them...! :) -
@JonB said in QListWidget item editing:
Your Xmas hat looks good. But a lot of others are also wearing them...! :)
Well that is is on purpose :)