Fail to drop in a QListWidget
-
Hi,
I have the following widget structure: a root QWidget contains a QFrame. The QFrame contains a QListWidget and a QTreeView. And, I want to drag-and-drop from the QTreeWidget to the QListWidget.But, as I do not want to subclass the QTreeView just for this, what I did is to connect the signal QTreeView::pressed to a slot that is on the root QWidget. This slott contains the following code:
//------------------------------------------------------------------------------- void LoadView::slotInitiateDragAndDrop(const QModelIndex& p_index) //------------------------------------------------------------------------------- { // Initiate the drag operation QDrag* drag = new QDrag(this); QMimeData* mimeData = new QMimeData; mimeData->setText(static_cast<QFileSystemModel*>(m_ui.m_dirExplorer->model())->filePath(p_index)); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->exec(Qt::CopyAction); }// slotInitiateDragAndDrop(const QModelIndex &p_index)
On the other side, the QListWidget has the following configuration on its constructor:
// Accept drop operation this->setAcceptDrops(true); this->setMouseTracking(true); this->setDropIndicatorShown(true);
And I have reimplemented the dragEnterEvent, dragMoveEvent and dropEvent functions.
Dragging operation is initiated (I see the indicator). But drop is not performed. In fact the QListWidget never enters in the reimplemented functions. I have checked that it is configured to accept copy action.
So what I did wrong?
Thanks for your help.
-
did you
setDragDropMode(QAbstractItemView::DropOnly)
on the listview?P.S.
as I do not want to subclass the QTreeView just for this
Hacking away a problem for "laziness" is bad. If you work in a team, this kind of things is what gets you a ton of 💩 dumped on. Particularly in this case where the subclassing solution is so much easier than your hack
-
@VRonin said in Fail to drop in a QListWidget:
setDragDropMode(QAbstractItemView::DropOnly)
Yes. But it does nothing. I also tried to apply the setAcceptDrops on the QLIstWidget::viewport(), as seen in a Wiki concerning the QLIstWidget. But it does nothing.
The drag-and-drop process works well to drop in another application (I tried to drop in Notepad++) but not between these two stupid widgets. I also tried to create the drag object from the QTreeView. but this changes nothing.
-
Hi
I tested your slotInitiateDragAndDrop way and it does work for me.
for the custom listwidget i havevoid mylistwidget::dragEnterEvent(QDragEnterEvent* event) { event->acceptProposedAction(); } void mylistwidget::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } void mylistwidget::dropEvent(QDropEvent* event) { qDebug() << event->mimeData()->formats(); QByteArray itemData = event->mimeData()->data("text/plain"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); addItem( itemData ); event->acceptProposedAction(); }
and
-
Hi,
So, this morning I did the following test:
1- I have simplified the problem by managing a drag-and-drop internally to my widget. This is the code of the widget:#include "FavoriteListView.h" #include <QtGui/QDragEnterEvent> #include <QtGui/QDragMoveEvent> #include <QtGui/QDropEvent> #include <QtCore/QSettings> #include <QtCore/QStandardPaths> #include "VerboseTreeView.h" //---------------------------------------------------------------------------------------------------------------- FavoriteListView::FavoriteListView(QWidget* p_parent /* = nullptr */) : QListWidget(p_parent) //---------------------------------------------------------------------------------------------------------------- { m_favoriteFile = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "favorites.ini"; QSettings favoriteFile(m_favoriteFile, QSettings::IniFormat); // Populate the list with default values QListWidgetItem* newItem; newItem = new QListWidgetItem(tr("Desktop"), this); newItem->setData(Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); this->addItem(newItem); newItem = new QListWidgetItem(tr("Documents"), this); newItem->setData(Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); this->addItem(newItem); newItem = new QListWidgetItem(tr("User"), this); newItem->setData(Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); this->addItem(newItem); for (auto currentKey : favoriteFile.allKeys()) { // Add additional items QString currentData = favoriteFile.value(currentKey).toString(); newItem = new QListWidgetItem(currentKey, this); // Last name of the directory newItem->setData(Qt::UserRole, currentData); // Full path } // Accept drop operation this->setMouseTracking(true); this->setAcceptDrops(true); }// FavoriteListView(QWidget* p_parent) //---------------------------------------------------------------------------------------------------------------- FavoriteListView::~FavoriteListView() //---------------------------------------------------------------------------------------------------------------- { QSettings favoriteFile(m_favoriteFile, QSettings::IniFormat); // remove previous favorites favoriteFile.clear(); for (int i = 3; i < this->count(); ++i) { // Save the current item QListWidgetItem* currentItem = this->item(i); QString dataToSave = currentItem->data(Qt::UserRole).toString(); favoriteFile.setValue(currentItem->text(), dataToSave); } favoriteFile.sync(); }// ~FavoriteListView() //--------------------------------------------------------------------------------------- void FavoriteListView::dragEnterEvent(QDragEnterEvent* p_event) //--------------------------------------------------------------------------------------- { std::cout << "Mime Data: " << p_event->mimeData()->hasFormat("text/plain") << "VerboseTreeView: " << dynamic_cast<VerboseTreeView*>(p_event->source()) << "Already present: " << this->isInFavorites(p_event->mimeData()->text()) << std::endl; p_event->acceptProposedAction(); if ((p_event->mimeData()->hasFormat("text/plain")) && // Content is text (dynamic_cast<VerboseTreeView*>(p_event->source()) != NULL) && // Source is a FileExplorerView (!this->isInFavorites(p_event->mimeData()->text()))) // Path is not in the favorites { // Accept the event p_event->acceptProposedAction(); }// Accept the event }// dragEnterEvent ( QDragEnterEvent * event ) //--------------------------------------------------------------------------------------- void FavoriteListView::dragMoveEvent(QDragMoveEvent* p_event) //--------------------------------------------------------------------------------------- { std::cout << "Mime Data: " << p_event->mimeData()->hasFormat("text/plain") << "VerboseTreeView: " << dynamic_cast<VerboseTreeView*>(p_event->source()) << "Already present: " << this->isInFavorites(p_event->mimeData()->text()) << std::endl; p_event->acceptProposedAction(); if ((p_event->mimeData()->hasFormat("text/plain")) && // Content is text (dynamic_cast<VerboseTreeView*>(p_event->source()) != NULL) && // Source is a FileExplorerView (!this->isInFavorites(p_event->mimeData()->text()))) // Path is not in the favorites { // Accept the event p_event->acceptProposedAction(); }// Accept the event }// dragMoveEvent ( QDragMoveEvent * event ) //--------------------------------------------------------------------------------------- void FavoriteListView::dropEvent(QDropEvent* p_event) //--------------------------------------------------------------------------------------- { QString selectedString = p_event->mimeData()->text(); if (selectedString != "") { QListWidgetItem* curItem = new QListWidgetItem(this); curItem->setText(QFileInfo(selectedString).fileName()); curItem->setData(Qt::UserRole, QFileInfo(selectedString).absoluteFilePath()); this->addItem(curItem); }// Something selected p_event->acceptProposedAction(); }// dropEvent(QDropEvent *event) //--------------------------------------------------------------------------------------- bool FavoriteListView::isInFavorites(const QString& p_path) //--------------------------------------------------------------------------------------- { bool result = false; QString pathWithEnd = p_path + "/"; // to identify that d: and d:/ are the same dir for (int i = 0; i < this->count(); ++i) { // Search on each item QListWidgetItem* currentItem = this->item(i); QString fullPath = currentItem->data(Qt::UserRole).toString(); result = result || (fullPath == p_path) || (fullPath == pathWithEnd); } return result; }// isInFavorites(const std::string &p_path) //------------------------------------------------------------------------------- void FavoriteListView::mousePressEvent(QMouseEvent* event) //------------------------------------------------------------------------------- { // Initiate the drag operation QDrag* drag = new QDrag(this); QMimeData* mimeData = new QMimeData; mimeData->setText("fait chier"); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->exec(Qt::CopyAction); }// slotInitiateDragAndDrop(const QModelIndex &p_index)
2- If I put this widget in a QDialog (that run in modal mode) everything works well.
3- But if the widget is a part of my QMainWindow (in my widget hierachy) this does not work. None of the drag event function is called. My widget hierarchy is the following (parenting)
LoadView (QWidget) ==> QFrame ==> FavoriteListView (QListWidget)
The LoadView has no parent. And it is in a QStackedWidget. The hierarchy of the stacked widget is:
QMainWindow ==> QWidget ==> QStackedWidget
So, there is someone, in this structure, that blocks the drag events.
-
OK,
I have reproduced the whole hierarchy structure in my QDialog. And in this case everything works well. This helps me to find were the problem is. It is somewhere in the DoDragDrop() function. This one, for an unkonwn reason never call QWindowsOleDropTarget::HandleDrag when the widget is in my QMainWindow.Unfortunately, as DoGrapAndDrop is a Windows function I have no access to its code.
@Addendum: I'm on Windows10 with Qt 5.6.3.