QListWidget item editing

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

QListWidget item editing

J Barchan
I have started a thread on the Qt Forum at https://forum.qt.io/topic/97628/qlistwidget-item-editing.  The people there are very helpful about how to do things for Qt in C++, but often there isn't really anyone to help out correctly for PyQt/Python.  I need to extend what I have done in PyQt, and I think I might need some PyQt help to sort it out.  Someone here like the excellent & kind [hidden email] will probably be able to sort me out immediately! :)

I won't repeat the whole stuff.  Basically, I was given to translate:
class SignalItemDelegate : public QStyledItemDelegate{
Q_OBJECT
Q_DISABLE_COPY(SignalItemDelegate)
public:
explicit SignalItemDelegate(QObject* parent = Q_NULLPTR):QStyledItemDelegate(parent){
QObject::connect(this,&SignalItemDelegate::closeEditor,this,&SignalItemDelegate::editFinished);
}
void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{
editStarted();
return QStyledItemDelegate::setEditorData(editor,index);
}
Q_SIGNALS:
void editStarted();
void editFinished();
};
for which I came up with:
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)

My first question is: is that about right (it does seem to work)?


My second question is: if you read my latest https://forum.qt.io/topic/97628/qlistwidget-item-editing/10, you'll see I need to introduce a parameter to my signals from my QListWidget to the outside world for which QListWidgetItem is being edited.  I believe I need to change my QListWidget signals like:
class JEditableListWidget(QtWidgets.QListWidget):
# class variable for "editStarted" signal, including item
editStarted = QtCore.pyqtSignal(QtWidgets.QListWidgetItem, name='editStarted')
Right?  And then how do I emit that signal?  Is it like:
styledItemDelegate.editStarted.connect(lambda: self.editStarted.emit(someItemWidget))
?
  • Is that use of a lambda right (that's a Python/PyQt question)?
  • And how do I get at from within the QListWidget which QListWidgetItem to pass as someItemWidget (that's probably a Qt question)?

Many thanks to anyone who responds!

--
Kindest,
Jonathan

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: QListWidget item editing

Maurizio Berti
[sending again as I forgot to reply to the list]

> is that about right (it does seem to work)?

Seems right.
The "name" argument of signals is not required as long as you use the same name for the attribute.


> Right?  And then how do I emit that signal?  Is it like:
> styledItemDelegate.editStarted.connect(lambda: self.editStarted.emit(someItemWidget))
> Is that use of a lambda right (that's a Python/PyQt question)?
> And how do I get at from within the QListWidget which QListWidgetItem to pass as someItemWidget (that's probably a Qt question)?

Not exactly. If you need to pass an argument to a function within a lambda, it has to be in the lambda arguments too, otherwise python will try to look for the argument name in the local scope and eventually raise an error if it doesn't exist.

Seeing your implementation I'd modify the original editStarted signal of the delegate and add a QModelIndex argument:

editStarted = QtCore.pyqtSignal(QtCore.QModelIndex)

then emit the signal from the setEditorData method with its index argument:

self.editStarted.emit(index)

At this point you can connect it with this lambda in the QListWidget init:

styledItemDelegate.editStarted.connect(lambda index: self.editStarted.emit(self.itemFromIndex(index))

In this way the the lambda will receive the index argument previously emitted, convert it to a list widget item and finally emit its own editStarted signal with the correct list widget item argument.

Regards,
Maurizio

--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: QListWidget item editing

J Barchan


On Fri, 14 Dec 2018 at 21:22, Maurizio Berti <[hidden email]> wrote:
[sending again as I forgot to reply to the list]

> is that about right (it does seem to work)?

Seems right.
The "name" argument of signals is not required as long as you use the same name for the attribute.


> Right?  And then how do I emit that signal?  Is it like:
> styledItemDelegate.editStarted.connect(lambda: self.editStarted.emit(someItemWidget))
> Is that use of a lambda right (that's a Python/PyQt question)?
> And how do I get at from within the QListWidget which QListWidgetItem to pass as someItemWidget (that's probably a Qt question)?

Not exactly. If you need to pass an argument to a function within a lambda, it has to be in the lambda arguments too, otherwise python will try to look for the argument name in the local scope and eventually raise an error if it doesn't exist.

Seeing your implementation I'd modify the original editStarted signal of the delegate and add a QModelIndex argument:

editStarted = QtCore.pyqtSignal(QtCore.QModelIndex)

then emit the signal from the setEditorData method with its index argument:

self.editStarted.emit(index)

At this point you can connect it with this lambda in the QListWidget init:

styledItemDelegate.editStarted.connect(lambda index: self.editStarted.emit(self.itemFromIndex(index))

In this way the the lambda will receive the index argument previously emitted, convert it to a list widget item and finally emit its own editStarted signal with the correct list widget item argument.

Regards,
Maurizio

--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

@Maurizio Berti
Thank you so much for replying.  I get the gist now.  I will try implementing next week!

One thing I didn't like/expect: your Qt reply is that same as at the forum (so I accept that's the way to do it now), you use the QStyledItemDelegate to get the item being edited (from the QIndex parameter), I expected that to be doable in the QListWidget class by (somehow) it knowing which item was being edited instead, but it seems that's not the case.

Grazie/Arrivederci/Ciao :)

--
Kindest,
Jonathan

Virus-free. www.avast.com

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: QListWidget item editing

Maurizio Berti

One thing I didn't like/expect: your Qt reply is that same as at the forum (so I accept that's the way to do it now), you use the QStyledItemDelegate to get the item being edited (from the QIndex parameter), I expected that to be doable in the QListWidget class by (somehow) it knowing which item was being edited instead, but it seems that's not the case.

Well, theoretically you could implement the edit() virtual protected method (not the slot) of QAbstractItemView instead, but then you'll have to check that the trigger matches the current editTriggers and, possibly, the relative QEvent which can sometimes be a bit tricky if you are not really careful. Take a look at the Qt sources of edit() of both QAbstractItemView and QListWidget as a reference.
I read your original question again, and maybe it's better to just reimplement edit() in your case; there also is the virtual protected slot closeEditor() that can help with what you need. If the reimplementations are not too complex, it might help you completely avoid the QStyledItemDelegate at all.
In any case, the index->item conversion is always required, and it's what QListWidget actually does anyway.

Maurizio

PS: as a suggestion, it's better to specify keypoints even when referring to an existing question online: I read that forum message this morning, but when I answered I forgot about the actual purpose of your code, then I just focused on the PyQt questions. While that "was" your question, the original one was focused on a slightly different objective, making this thread more like an XY-Problem, since you could probably avoid the delegate implementation.


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: QListWidget item editing

J Barchan


On Sat, 15 Dec 2018 at 18:09, Maurizio Berti <[hidden email]> wrote:

One thing I didn't like/expect: your Qt reply is that same as at the forum (so I accept that's the way to do it now), you use the QStyledItemDelegate to get the item being edited (from the QIndex parameter), I expected that to be doable in the QListWidget class by (somehow) it knowing which item was being edited instead, but it seems that's not the case.

Well, theoretically you could implement the edit() virtual protected method (not the slot) of QAbstractItemView instead, but then you'll have to check that the trigger matches the current editTriggers and, possibly, the relative QEvent which can sometimes be a bit tricky if you are not really careful. Take a look at the Qt sources of edit() of both QAbstractItemView and QListWidget as a reference.
I read your original question again, and maybe it's better to just reimplement edit() in your case; there also is the virtual protected slot closeEditor() that can help with what you need. If the reimplementations are not too complex, it might help you completely avoid the QStyledItemDelegate at all.
In any case, the index->item conversion is always required, and it's what QListWidget actually does anyway.

Maurizio

PS: as a suggestion, it's better to specify keypoints even when referring to an existing question online: I read that forum message this morning, but when I answered I forgot about the actual purpose of your code, then I just focused on the PyQt questions. While that "was" your question, the original one was focused on a slightly different objective, making this thread more like an XY-Problem, since you could probably avoid the delegate implementation.


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

In my original post here I did feel I had taken the time to pick out the key points of what I had posted in the Qt forum question without repeating the whole thing.  Sorry if I failed to do that sufficiently.

The guy who replied there is a QStyledItemDelegate proselytiser and I respect that he knows what he is talking about, so I don't mind using that.  I just thought it would all be doable from QListWidget without going to the delegate.  I'm not as clever/au fait with PyQt/Qt as you guys, and don't really want to have to go understand the Qt sources for this if I don't have to.  I am looking for the "simplest" answer, which for me probably equates to the least lines of code I have to write!  Not sure I understand enough about what you're suggesting as the alternative for me to implement it.  One thing that "confuses" me: this is probably a dumb thing to ask, but given that I can only write def edit(self) as an override function I don't understand what to do about your:
> Well, theoretically you could implement the edit() virtual protected method (not the slot) of QAbstractItemView instead
as they both look to me like methods named edit()...?

So when I get back to this on Tuesday I think I'll have a look at the QStyledItemDelegate approach, which I think I know how to implement, and see whether I can compare that against your edit() + closeEditor() approach.  If all I have to do is override each of these in QListWidget then that would be just fine, but that's not where they live.  I'm a bit all over the place, I just need to find an approach that simple enough for my poor little brain!
--
Kindest,
Jonathan

Virus-free. www.avast.com

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt