How to customize item editing with QTreeView / QStandardItemModel?
-
I want to customize editing in two ways:
- I want to be able to activate editing by single-clicking a current item (that is not selected).
- I want the edit field to contain certain text that I will specify, regardless of which column I've clicked. Also, after editing is completed I should receive some sort of signal, and ideally editing should not result in the item actually being changed.
How to do this?
-
-
"QAbstractItemView::setEditTriggers":http://qt-project.org/doc/qt-5/qabstractitemview.html#editTriggers-prop
-
"QAbstractItemDelegate::setEditorData":http://qt-project.org/doc/qt-5/qstyleditemdelegate.html#setEditorData
and
"QAbstractItemDelegate::setModelData":http://qt-project.org/doc/qt-5/qabstractitemdelegate.html#setModelData
The first one should be a single line of code, the second problem is a bit more daunting, depending on how much experience you have. You would have to subclass "QStyledItemDelegate":http://qt-project.org/doc/qt-5/qstyleditemdelegate.html (that's probably the one your view uses) and reimplement its "setEditorData":http://qt-project.org/doc/qt-5/qstyleditemdelegate.html#setEditorData -function. Take a look at the Qt sources ("QStyledItemDelegate.cpp":https://qt.gitorious.org/qt/webkit/source/435bbd4be73768f617e4a4083a345d1d8d62daa3:src/gui/itemviews/qstyleditemdelegate.cpp), the function you would be reimplementing is quite simple. Just copy and slightly modify it - if you do so, you wouldn't need to call any base-class implementation.
When reimplementing "QStyledItemDelegate::setModelData":http://qt-project.org/doc/qt-5/qstyleditemdelegate.html#setModelData you can easily intercept the changes, emit whatever signals you need and, of course, manipulate data before it is written to the model.
Oh, of course you have to call "QAbstractItemView::setItemDelegate":http://qt-project.org/doc/qt-5/qabstractitemview.html#setItemDelegate at some point to actually use your subclass.
I hope this helps! :)
-
-
Thanks!
I've seen the edit triggers, there's no trigger for my case (single click on a current item; there's only a click on selected item available).
-
Maybe this one is the right one for you:
"QAbstractItemView::CurrentChanged -
Editing start whenever current item changes."Either that or you have to create a custom signal (reimplement mousePressEvent and mouseReleaseEvent within your tree view) that you connect to "QAbstractItemView::edit":http://qt-project.org/doc/qt-5/qabstractitemview.html#edit .
Now everything solved? ;P
-
Gotta be careful to not mask double click...
-
I don't think you can double-click when every click is supposed to initiate editing (and hence the second click is eaten by the editor). No matter if you try to implement the desired behavior yourself or not.
-
Obviously, I don't enable single click edit trigger.
For a moment I thought I've had a great idea. I put this code in my widget inherited from QTreeView:
@QObject::connect(this, &CFileListView::clicked, [this](const QModelIndex& index){
if (currentIndex() == index)
edit(index, EditKeyPressed, nullptr);
});@But apparently, the current item is set before clicked() signal is emitted. So no matter what I click, it first becomes current and then the signal handler is called, which leads to editor opening up.
I suppose there's no way to avoid handling mousePressEvent()...
Q: Is there any way to easily detect a single click without masking a double click? As in, is there any Qt built-in way?
-
Measure the time between first and second click - while also checking that the mouse hasn't been moved too much between press and release.
"This":http://qt-project.org/doc/qt-5/qapplication.html#doubleClickInterval-prop tells you about the maximal time between clicks, "this":http://qt-project.org/doc/qt-5/qinputevent.html#timestamp lets you check the times of the events (can also be done with a QTimer).
I would highly recommend choosing something different than the single click as the edit trigger. If you want to be able to use double clicks, you will have to wait after a single click until you are sure that no double click was delivered. Then you can open your editor - and that might make for a strange user experience.
-
I use a commercial program that has a list view and uses single click to open editor. It works perfectly.
-
As for QInputEvent::timestamp(): it is unclear what resolution it has, or how to calculate the difference between stamps in ms. Any hints?
-
Take a look at the sources (I don't have them available) or just try it out by inserting a "qDebug("%lli", event->timestamp());" into e.g. the mousePressEvent and then clicking a button twice within a rough estimate of e.g. two seconds. I think it's going to be microseconds, maybe milliseconds - should be easy to figure out.
EDIT: I didn't find any hints in the sources, so either figure it out as described or use your own timer.
-
Thanks for checking.
I'm almost done with my editing questions. One last thing: I want to prevent the editor from committing changes if it was closed in any other way than with Enter button press. Right now, it seems, only Esc will cancel the commit, any other way to close the editor will result in the data being commited.
-
For the data to be commited the setModelData-function has to be called. I mentioned that before - reimplement it and filter out what you need to.
-
But how do I know if the editor was closed with Enter, or in another way?
-
It shouldn't matter. Maybe take a look at the closeEditor()-function - but if you really want to know about specific button presses, you might have to subclass QLineEdit (or whatever comes close to what you need) and write an editor on your own.
Also: Read a bit in the documentation. As far as I know, the setModelData-function is always (!) called when data has to be committed (so pressing Esc wouldn't trigger it). In there the data is written to the model.
-
My question was: I want to prevent the editor from committing changes if it was closed in any other way than with Enter button press.
-
Ok, then. Have a detailed look at the description of the "QStyledItemDelegate":http://qt-project.org/doc/qt-5/qstyleditemdelegate.html#details and then try to make use of the "Star Delegate Example":http://qt-project.org/doc/qt-5/qtwidgets-itemviews-stardelegate-example.html . As far as I know the only way to actively choose how the editor can be closed is by creating your own editor (it sends out a signal when things have to be commited, so you decide when that signal actually is sent). It is not too difficult, especially since you probably only need basic QLineEdit-functionality.
There is also "QAbstractItemDelegate::editorEvent":http://qt-project.org/doc/qt-5/qabstractitemdelegate.html#editorEvent , but it supposedly only is called when the editing starts, not when it finishes. In that case it wouldn't be of much use.
Good luck. :)
-
Thanks for the explanation!