QListWidget drag/drop documentation/behaviour
-
I can't really point out on how to translate it into designer but this minimal example works for me. Items can be dragged to the "DropZone" but not internally in the list
#include <QApplication> #include <QListWidget> #include <QHBoxLayout> #include <QDragEnterEvent> #include <QMimeData> #include <QDropEvent> class DropZone : public QWidget{ public: explicit DropZone(QWidget* parent = nullptr) : QWidget(parent) { setAcceptDrops(true); setMinimumSize(100,100); setStyleSheet("background-color:red;"); } protected: void dragEnterEvent(QDragEnterEvent *event) override { event->acceptProposedAction(); } void dropEvent(QDropEvent *event) override { event->acceptProposedAction(); } }; class Interceptor : public QWidget{ public: explicit Interceptor(QWidget* parent = nullptr) : QWidget(parent) , listWid(new QListWidget(this)) { listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly); listWid->addItem(QStringLiteral("Test")); listWid->addItem(QStringLiteral("Item")); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(listWid); DropZone* dropZone = new DropZone(this); layout->addWidget(dropZone); } private: QListWidget *listWid; }; int main(int argc, char *argv[]) { QApplication app(argc,argv); Interceptor wid; wid.show(); return app.exec(); }
-
I can't really point out on how to translate it into designer but this minimal example works for me. Items can be dragged to the "DropZone" but not internally in the list
#include <QApplication> #include <QListWidget> #include <QHBoxLayout> #include <QDragEnterEvent> #include <QMimeData> #include <QDropEvent> class DropZone : public QWidget{ public: explicit DropZone(QWidget* parent = nullptr) : QWidget(parent) { setAcceptDrops(true); setMinimumSize(100,100); setStyleSheet("background-color:red;"); } protected: void dragEnterEvent(QDragEnterEvent *event) override { event->acceptProposedAction(); } void dropEvent(QDropEvent *event) override { event->acceptProposedAction(); } }; class Interceptor : public QWidget{ public: explicit Interceptor(QWidget* parent = nullptr) : QWidget(parent) , listWid(new QListWidget(this)) { listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly); listWid->addItem(QStringLiteral("Test")); listWid->addItem(QStringLiteral("Item")); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(listWid); DropZone* dropZone = new DropZone(this); layout->addWidget(dropZone); } private: QListWidget *listWid; }; int main(int argc, char *argv[]) { QApplication app(argc,argv); Interceptor wid; wid.show(); return app.exec(); }
@VRonin said in QListWidget drag/drop documentation/behaviour:
listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly);
So far as I can understand, this is all you do in the list widget source (your
DropZone
is just the target). And I do those two lines from the UI/in theui_....h
file, because I thought like you thatQAbstractItemView::DragOnly
is what would stop re-ordering. But as I said I can still re-order.I did say earlier I have
(If it makes any difference, note that my viewMode is set to IconMode.)
Maybe this is an issue? When i put it in I remember the docs saying something about this set some "Free Movement", or similar, flag/option, maybe that's to do with what we are seeing.... [EDIT: I switched over to the normal view but that did not alter the behaviour, so it doesn't seem this is relevant,]
-
@VRonin
Thanks. So to be clear: the mouse and other events go to myQListWidget
first/directly, and (without sub classing) I cannot "intercept" them up at the containingQDialog
first, even if I ask nicely?I kinda thought this was the point of an
installEventFilter()
on, say, aQDialog
holding widgets, so that I could intercept the events up at the dialog instead of letting them into the sub-widgets? It's a lot more limited as it seems to stand.@JonB said in QListWidget drag/drop documentation/behaviour:
I kinda thought this was the point of an installEventFilter() on, say, a QDialog holding widgets, so that I could intercept the events up at the dialog instead of letting them into the sub-widgets? It's a lot more limited as it seems to stand.
Aren't events propagated upwards the parent-child-tree starting from the child which is at the given coords?!
So it's like:
Top-most widget (in your case:QListWidget
) recieves drag/mouse/whatever event -> Accept? Yes/no? If yes, it starts to process the event, if not accepted, the event is propagated to the direct/next parent. Then it starts all over again.Old, but still nice to read:
I kinda thought this was the point of an installEventFilter() on, say, a QDialog holding widgets, so that I could intercept the events up at the dialog instead of letting them into the sub-widgets?
That should work. If you install the filter on your dialog and set a target to get "inspected" you can pre-handle the events before they get to the target.
-
@JonB said in QListWidget drag/drop documentation/behaviour:
I kinda thought this was the point of an installEventFilter() on, say, a QDialog holding widgets, so that I could intercept the events up at the dialog instead of letting them into the sub-widgets? It's a lot more limited as it seems to stand.
Aren't events propagated upwards the parent-child-tree starting from the child which is at the given coords?!
So it's like:
Top-most widget (in your case:QListWidget
) recieves drag/mouse/whatever event -> Accept? Yes/no? If yes, it starts to process the event, if not accepted, the event is propagated to the direct/next parent. Then it starts all over again.Old, but still nice to read:
I kinda thought this was the point of an installEventFilter() on, say, a QDialog holding widgets, so that I could intercept the events up at the dialog instead of letting them into the sub-widgets?
That should work. If you install the filter on your dialog and set a target to get "inspected" you can pre-handle the events before they get to the target.
@Pl45m4 said in QListWidget drag/drop documentation/behaviour:
That should work. If you install the filter on your dialog and set a target to get "inspected" you can pre-handle the events before they get to the target.
Is that not exactly what I show I have done in my code??
ui->listWidget->installEventFilter(this);
?Aren't events propagated upwards the parent-child-tree starting from the child which is at the given coords?!
How would an event filter on a parent to filter out mouse events to a child work, if the child gets the event first? What's the damn point of an event filter?! ;-)
Is this a rodent-only issue? Because I can't see how event filters are working with rodents..... And if the docs know they won't work right with rodents but will with other types of event, I wish they would say so.
-
@Pl45m4 said in QListWidget drag/drop documentation/behaviour:
That should work. If you install the filter on your dialog and set a target to get "inspected" you can pre-handle the events before they get to the target.
Is that not exactly what I show I have done in my code??
ui->listWidget->installEventFilter(this);
?Aren't events propagated upwards the parent-child-tree starting from the child which is at the given coords?!
How would an event filter on a parent to filter out mouse events to a child work, if the child gets the event first? What's the damn point of an event filter?! ;-)
Is this a rodent-only issue? Because I can't see how event filters are working with rodents..... And if the docs know they won't work right with rodents but will with other types of event, I wish they would say so.
For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver's parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).
(https://doc.qt.io/qt-5/qcoreapplication.html#notify)
So from my understanding
QCoreApp..
notifies "receiver" widget (at least when it comes to mouse- and keyEvent) and propagates upwards to the top-most. Don't know how d'n'd events work, but a dragEvent starts with a mouseClick and mouseMove :)
Have you found out, which widget actually your "receiver" of your drags onto yourQDialog
is? The dialog itself? The listWidget? The listWidgets's viewport? Something else?I would have suggested to install the eventFilter on another object (like the viewPort of listWidget), if @VRonin hadn't done that...
Have you tried "simple" mousePress event first? Maybe dropEvents are treated specially
-
For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver's parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).
(https://doc.qt.io/qt-5/qcoreapplication.html#notify)
So from my understanding
QCoreApp..
notifies "receiver" widget (at least when it comes to mouse- and keyEvent) and propagates upwards to the top-most. Don't know how d'n'd events work, but a dragEvent starts with a mouseClick and mouseMove :)
Have you found out, which widget actually your "receiver" of your drags onto yourQDialog
is? The dialog itself? The listWidget? The listWidgets's viewport? Something else?I would have suggested to install the eventFilter on another object (like the viewPort of listWidget), if @VRonin hadn't done that...
Have you tried "simple" mousePress event first? Maybe dropEvents are treated specially
@Pl45m4
I will look into this tomorrow now.The only "sensible" explanation would indeed be if "mouse events go upward". I can see that makes sense. It would also mean that "install an event filter to intercept mouse events" isn't going to work. As per what my code seems to be showing. Which is fine, if
installEventFilter()
or similar mentioned this.Funnily enough, the example there says it does what I want for key presses:
Here's a KeyPressEater class that eats the key presses of its monitored objects:
So that would mean it does work for keys, but not for mouse events.
Which, if true, must be out there on the web. I'm too tired to go search now.
Or the dragging of items on a
QListWidget
miraculously by-passes mouse-event-handling, which would be surprising. -
@Pl45m4
I will look into this tomorrow now.The only "sensible" explanation would indeed be if "mouse events go upward". I can see that makes sense. It would also mean that "install an event filter to intercept mouse events" isn't going to work. As per what my code seems to be showing. Which is fine, if
installEventFilter()
or similar mentioned this.Funnily enough, the example there says it does what I want for key presses:
Here's a KeyPressEater class that eats the key presses of its monitored objects:
So that would mean it does work for keys, but not for mouse events.
Which, if true, must be out there on the web. I'm too tired to go search now.
Or the dragging of items on a
QListWidget
miraculously by-passes mouse-event-handling, which would be surprising.Just found out, that
QListWidget
has a different handler when it's inIconMode
StartDrag
DropFilter
Have you tried the same without using
IconMode
? Does it work?Edit:
This seems to be similar to your issue. -
Just found out, that
QListWidget
has a different handler when it's inIconMode
StartDrag
DropFilter
Have you tried the same without using
IconMode
? Does it work?Edit:
This seems to be similar to your issue.@Pl45m4
Thank you for this reply, which for some reason I did not see yesterday.I got excited from your message, and particularly the https://forum.qt.io/topic/27375/solved-prevent-users-from-moving-icons-in-qlistwidget-iconmode which seemed to be exactly what i was looking for. However, sadly:
-
I have now verified that I see exactly same behaviour in ListMode not just IconMode, so that is not the issue.
-
It is true that per that thread
setMovement(QListView::Static)
prevents the drag-drop rearrangement within theQListWidget
, which is all that person was posting to request. But... -
Static
setting also prevents any kind of drag on the items, so I can no longer drag them to copy to another window.
So it's an all-or-nothing:
-
Static
movement prevents re-arrangement, as I want, but only by disabling any dragging of items out of theQListView
as well. -
The alternatives
Free
orSnap
allow dragging of items out of theQListView
, so I need one of those, but then also allow dropping within theQListView
to re-arrange, which I do not want.
So far I have tried every combination and nothing works to allow outward dragging but not internal dropping. And meanwhile any attempt to get at this via an
eventFilter()
does nothing, as very mysteriously the drag-drop seems to function without generating any events on theQListView
(or itsviewport()
which can be intercepted by the filter. Sigh....P.S.
Found https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qlistview.cpp.html#236 to confirm my findings aboutsetMovement(QListView::Static)
being an all-or-nothing for any dragging operation:#if QT_CONFIG(draganddrop) bool movable = (movement != Static); setDragEnabled(movable); d->viewport->setAcceptDrops(movable); #endif
P.P.S.
Eureka! So looking at what it's doing inspired me to try in my code (afterui->setupUi(this)
):ui->listWidget->viewport()->setAcceptDrops(false); // or works equally ui->listWidget->setAcceptDrops(false);
So... even though I already had acceptDrops checkbox unchecked in Designer on the
QListWidget
, the fact thatQListWidget::movement()
needs to be set to something other thanQListView::Static
to permit any kind of dragging means this internal code makes it so if you havesetDragEnabled(true)
(which is required) you automatically getsetAcceptDrops(true)
, which is not wanted! Hence my extra line fixes this :)Final word: Doesn't have to be
setMovement()
non-QListView::Static
. Also results fromsetViewMode(QListView::IconMode)
. See my next post below for further information. Putting in a manualsetAcceptDrops(false);
after this still fixes for what I want. -
-
I can't really point out on how to translate it into designer but this minimal example works for me. Items can be dragged to the "DropZone" but not internally in the list
#include <QApplication> #include <QListWidget> #include <QHBoxLayout> #include <QDragEnterEvent> #include <QMimeData> #include <QDropEvent> class DropZone : public QWidget{ public: explicit DropZone(QWidget* parent = nullptr) : QWidget(parent) { setAcceptDrops(true); setMinimumSize(100,100); setStyleSheet("background-color:red;"); } protected: void dragEnterEvent(QDragEnterEvent *event) override { event->acceptProposedAction(); } void dropEvent(QDropEvent *event) override { event->acceptProposedAction(); } }; class Interceptor : public QWidget{ public: explicit Interceptor(QWidget* parent = nullptr) : QWidget(parent) , listWid(new QListWidget(this)) { listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly); listWid->addItem(QStringLiteral("Test")); listWid->addItem(QStringLiteral("Item")); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(listWid); DropZone* dropZone = new DropZone(this); layout->addWidget(dropZone); } private: QListWidget *listWid; }; int main(int argc, char *argv[]) { QApplication app(argc,argv); Interceptor wid; wid.show(); return app.exec(); }
@VRonin said in QListWidget drag/drop documentation/behaviour:
listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly);
Dear @VRonin,
I hope you might take a couple of minutes to look at this --- just OOI and to feel my pain --- since I have spent so much time trying to figure why your sample code does work but that generated from Designer does not.
I invite you to place the following line above your two lines:
listWid->setViewMode(QListView::IconMode);
(The only thing I care about is that you can still start a drag, and you cannot drop the drag back in the list widget to cause changed layout.) Everything still works, right?
Now please move that line to after the original two lines. This is where it ends up from the
.ui
file/uic
generator toui_....h
from setting all these properties. Now try.... You can now drag & drop the items around the list widget to re-arrange them, can't you? Which is what I was wanting to prevent.Now that I have figured this I am of course OK going forward. But I should like sympathy for how long it has taken to me to determine what has been going on... ;-)
-
@VRonin said in QListWidget drag/drop documentation/behaviour:
listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly);
Dear @VRonin,
I hope you might take a couple of minutes to look at this --- just OOI and to feel my pain --- since I have spent so much time trying to figure why your sample code does work but that generated from Designer does not.
I invite you to place the following line above your two lines:
listWid->setViewMode(QListView::IconMode);
(The only thing I care about is that you can still start a drag, and you cannot drop the drag back in the list widget to cause changed layout.) Everything still works, right?
Now please move that line to after the original two lines. This is where it ends up from the
.ui
file/uic
generator toui_....h
from setting all these properties. Now try.... You can now drag & drop the items around the list widget to re-arrange them, can't you? Which is what I was wanting to prevent.Now that I have figured this I am of course OK going forward. But I should like sympathy for how long it has taken to me to determine what has been going on... ;-)
@JonB said in QListWidget drag/drop documentation/behaviour:
I invite you to place the following line above your two lines:
The only thing how I could explain it, is that as I've assumed before, the different "modes" are handled independently.
Default mode is notIconMode
. So maybe all the settings you make, only apply for the current mode (intentionally or not?!) and when you switch theviewMode
everything resets / defaults, so you can drop the stuff again... oof... :)QtDesigner at it's best :P
-
@JonB said in QListWidget drag/drop documentation/behaviour:
I invite you to place the following line above your two lines:
The only thing how I could explain it, is that as I've assumed before, the different "modes" are handled independently.
Default mode is notIconMode
. So maybe all the settings you make, only apply for the current mode (intentionally or not?!) and when you switch theviewMode
everything resets / defaults, so you can drop the stuff again... oof... :)QtDesigner at it's best :P
@Pl45m4
As I showed in the code from your woboq file, it's kind of like that, but not completely. SettingIconMode
causes a couple of changes to be made. The trouble is, where they put that line in theui....h
file from Designer properties "undoes" some of the other proprieties you have also set there. Without you realising.Anyway, I'm good to go now that I know why & what, just "bruised" from the length of the tussle... ;-)
-
@Pl45m4
As I showed in the code from your woboq file, it's kind of like that, but not completely. SettingIconMode
causes a couple of changes to be made. The trouble is, where they put that line in theui....h
file from Designer properties "undoes" some of the other proprieties you have also set there. Without you realising.Anyway, I'm good to go now that I know why & what, just "bruised" from the length of the tussle... ;-)
@JonB said in QListWidget drag/drop documentation/behaviour:
The trouble is, where they put that line in the ui....h file from Designer properties "undoes" some of the other proprieties you have also set there.
Yes... so it's impossible to get that behavior you asked for, when using QtDesigner even tho it is theoretically possible because these are just 3 properties. Just a matter of order. If
uic
will always put it this way... it doesn't work and you don't know why :)Another reason to use QtD for placing widgets and other stuff only and do the rest with code :)
-
@VRonin said in QListWidget drag/drop documentation/behaviour:
listWid->setDragEnabled(true); listWid->setDragDropMode(QAbstractItemView::DragOnly);
Dear @VRonin,
I hope you might take a couple of minutes to look at this --- just OOI and to feel my pain --- since I have spent so much time trying to figure why your sample code does work but that generated from Designer does not.
I invite you to place the following line above your two lines:
listWid->setViewMode(QListView::IconMode);
(The only thing I care about is that you can still start a drag, and you cannot drop the drag back in the list widget to cause changed layout.) Everything still works, right?
Now please move that line to after the original two lines. This is where it ends up from the
.ui
file/uic
generator toui_....h
from setting all these properties. Now try.... You can now drag & drop the items around the list widget to re-arrange them, can't you? Which is what I was wanting to prevent.Now that I have figured this I am of course OK going forward. But I should like sympathy for how long it has taken to me to determine what has been going on... ;-)
@JonB said in QListWidget drag/drop documentation/behaviour:
Now please move that line to after the original two lines. This is where it ends up from the .ui file/uic generator to ui_....h from setting all these properties. Now try.... You can now drag & drop the items around the list widget to re-arrange them, can't you? Which is what I was wanting to prevent.
Indeed, this is due to
QListView::setViewMode
overwriting theacceptDrops
property (see https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qlistview.cpp.html#_ZN9QListView11setViewModeENS_8ViewModeE). It's been like this since before the Nokia days so I can't easily dig out why this is the case.For people ending up here from a search engine, the solution is to put
ui->listWidget->setDragDropMode(QAbstractItemView::DragOnly);
in your widget constructor after the call tosetupUi()
-
@JonB said in QListWidget drag/drop documentation/behaviour:
Now please move that line to after the original two lines. This is where it ends up from the .ui file/uic generator to ui_....h from setting all these properties. Now try.... You can now drag & drop the items around the list widget to re-arrange them, can't you? Which is what I was wanting to prevent.
Indeed, this is due to
QListView::setViewMode
overwriting theacceptDrops
property (see https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qlistview.cpp.html#_ZN9QListView11setViewModeENS_8ViewModeE). It's been like this since before the Nokia days so I can't easily dig out why this is the case.For people ending up here from a search engine, the solution is to put
ui->listWidget->setDragDropMode(QAbstractItemView::DragOnly);
in your widget constructor after the call tosetupUi()
@VRonin
Thank you for this. You may be able to see why I was so confused!I would also say that as per my comment earlier I actually put in the following (after
setupUi()
) to address the issue:ui->listWidget->setAcceptDrops(false);
I don't know how that compares against your
ui->listWidget->setDragDropMode(QAbstractItemView::DragOnly)
, I can only say it is what is working for me in the situation I described. -
@VRonin
Thank you for this. You may be able to see why I was so confused!I would also say that as per my comment earlier I actually put in the following (after
setupUi()
) to address the issue:ui->listWidget->setAcceptDrops(false);
I don't know how that compares against your
ui->listWidget->setDragDropMode(QAbstractItemView::DragOnly)
, I can only say it is what is working for me in the situation I described.