QListView in a QTabWidget tab - External Drop Not Working



  • Hey Gurus,
    I just can't seem to get this working.
    I have a QTabWidget created in QT's form designer (ui->chatTabs). As required I add tabs (ChatTab) to the widget. Any tabs added are based on QWidget class.

    chattab.h

    class ChatTab : public QWidget
    {
    	Q_OBJECT
    public:
    	explicit ChatTab(STClientSettings *settings, QString chatUUID, QWidget *parent = nullptr);
    	~ChatTab();
    
    ...
    };
    
    #endif // CHATTAB_H
    

    chattab.cpp

    ChatTab::ChatTab(struct STClientSettings *settings, QString chatUUID, QWidget *parent) : QWidget(parent)
    {
    	m_settings = settings;
    	m_chatTabUUID = chatUUID;
    
    	m_chatList = new SQListView;
    
    	m_chatList->setResizeMode(QListView::Adjust);
    	m_chatList->setWordWrap(true);
    	m_chatList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
    	m_chatList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    	m_chatList->setModel(&m_mymodel);
    	m_chatList->setMinimumSize(700,400);
    	m_chatList->setSelectionMode(QAbstractItemView::SingleSelection);
    
    	m_chatList->setAcceptDrops(true);
    	m_chatList->setDropIndicatorShown(true);
    
    	connect( m_chatList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(chatListDoubleClicked(const QModelIndex &)));
    
    	// Capture context menu clicks...
    	m_chatList->setContextMenuPolicy(Qt::CustomContextMenu);
    
    	// ...and send those clicks to CustomMenuRequested
    	connect(m_chatList, SIGNAL(customContextMenuRequested(QPoint)), SLOT(ListMenuRequested(QPoint)));
    
    	// NOW tell the view to rely on our custom delegate for drawing
    	m_chatList->setItemDelegate(new ChatBubbleDelegate());
    ...
    	listsLayout->addWidget(m_chatList);
    ...
    	mainLayout->addLayout(listsLayout);
    	mainLayout->addLayout(messageLayout);
    	mainLayout->addLayout(swapLayout);
    	mainLayout->addLayout(quickReplyLayout1);
    	mainLayout->addLayout(quickReplyLayout2);
    
    	setLayout(mainLayout);
    }
    

    SQlistView is my QListView overridden class:

    sqlistview.h

    #ifndef SQLISTVIEW_H
    #define SQLISTVIEW_H
    
    #include <QObject>
    #include <QWidget>
    #include <QListView>
    
    class SQListView : public QListView
    {
    public:
    	SQListView();
    
    protected:
    	 void dropEvent(QDropEvent *ev) override;
    };
    
    #endif // SQLISTVIEW_H
    

    sqlistview.cpp

    #include "sqlistview.h"
    
    #include <QDebug>
    
    SQListView::SQListView()
    {
    
    }
    
    void SQListView::dropEvent(QDropEvent *ev)
    {
    	qDebug() << "file dropped";
    }
    

    A chattab is added to the QTabWidget like this:

    	ChatTab *chatTab = new ChatTab( &m_settings, uuid, this );
    
    	// Add the new tab to the tab widget
    	index = ui->chatTabs->addTab( chatTab, "Chat Room" );
    
    	// Activate the new tab
    	ui->chatTabs->setCurrentIndex( index );
    

    No matter what I do, dropping a file onto the SQListView won't work. The drop indicator always has the circle with the line through it, indicating I can't drop.

    As you can see, my SQListView also has a custom delegate, I thought this may be causing issues, but disabling it changes nothing.

    I just want to be able to drop an external file onto SQListView, I don't need to use the interal drag and drop within the list.

    Could someone please give me some idea of what I may be doing wrong?

    Thanks heaps in advance.

    Steve Q. :-)


  • Lifetime Qt Champion



  • @jsulm I just tested it and acceptDrops resolves to true.
    Still drop does not work?


  • Lifetime Qt Champion

    Hi,

    The Model/View classes have already support for drag and drop. Therefore you have more specific methods to override in order to customise your model/view behaviour to your liking.

    See the dedicated chapter in Qt's documentation especially the mimeTypes, canDropMimeData and dropMimeData methods of your model class.



  • @SGaist Thanks heaps for the heads up.
    I'll have a read of this and see how I go.



  • @SGaist Hi Again,
    I have read this documentation, and I'm still not sure how to proceed. I've modified my code to include this:

    	m_chatList->setAcceptDrops(true);
    	m_chatList->setDragEnabled(false);
    	m_chatList->setDropIndicatorShown(true);
    	m_chatList->viewport()->setAcceptDrops(true);
    	m_chatList->setDragDropMode(QAbstractItemView::DropOnly);
    

    But the SQListViewstill gives me the round circle with the line through it, indicating I can't drop.

    I'm sorry to be a pain, but could you please give me more of an idea of what to do?

    I'm obviously missing something here, but I just don't know what it is!

    Thanks so much,

    Steve Q.



  • @SGaist I have managed to get drag and drop working on the entire widget, which will be fine for now.
    But I still can't get it to work on ONLY the listview in the widget.

    I have change my SQListView code to the following:

    sqlistview.cpp

    #include "sqlistview.h"
    
    #include <QDebug>
    #include <QDragEnterEvent>
    #include <QMimeData>
    
    SQListView::SQListView()
    {
    	viewport()->setAcceptDrops(true);
    }
    
    void SQListView::dropEvent(QDropEvent* e)
    {
    
    	foreach (const QUrl &url, e->mimeData()->urls())
    	{
    		 QString fileName = url.toLocalFile();
    		 qDebug() << "Dropped file:" << fileName;
    	}
    }
    
    void SQListView::dragEnterEvent(QDragEnterEvent *e)
    {
    	if (e->mimeData()->hasUrls())
    	{
    		e->acceptProposedAction();
    	}
    }
    

    sqlistview.h

    #ifndef SQLISTVIEW_H
    #define SQLISTVIEW_H
    
    #include <QObject>
    #include <QWidget>
    #include <QListView>
    
    class SQListView : public QListView
    {
    public:
    	SQListView();
    
    private slots:
    	void dragEnterEvent(QDragEnterEvent *e);
    	void dropEvent(QDropEvent *ev);
    
    protected:
    };
    
    #endif // SQLISTVIEW_H
    

    and in the creation of the listview:

    	m_chatList = new SQListView;
    
    	m_chatList->setResizeMode(QListView::Adjust);
    	m_chatList->setWordWrap(true);
    	m_chatList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
    	m_chatList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    	m_chatList->setModel(&m_mymodel);
    	m_chatList->setMinimumSize(700,400);
    	m_chatList->setSelectionMode(QAbstractItemView::SingleSelection);
    
    	connect( m_chatList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(chatListDoubleClicked(const QModelIndex &)));
    
    	// Capture context menu clicks...
    	m_chatList->setContextMenuPolicy(Qt::CustomContextMenu);
    
    	// ...and send those clicks to CustomMenuRequested
    	connect(m_chatList, SIGNAL(customContextMenuRequested(QPoint)), SLOT(ListMenuRequested(QPoint)));
    
    	// NOW tell the view to rely on our custom delegate for drawing
    	m_chatList->setItemDelegate(new ChatBubbleDelegate());
    

    But still no luck!

    Steve Q. :-)


  • Lifetime Qt Champion

    It's your model that you have to modify to support other mime types etc.



  • @SGaist Ahhh okay. Thanks for the tip. Back to the docs it is for me!



  • Hi all,

    I have now solved this.

    As my drop was not item dependent, that is I didn't care what was under the drop, so long as it was the list, I didn't need to subclass the QStandardItemModel. Instead I simply subclassed the QListView with the following overridden methods:

    	void dragEnterEvent(QDragEnterEvent *e);
    	void dropEvent(QDropEvent *ev);
    	void dragMoveEvent(QDragMoveEvent* event);
    

    I can now drop on my list and I will receive the filename of the file that was dropped. I then "emit" this filename to my parent to act on it accordingly.

    Thanks for all your help.

    Steve Q. :-)



  • hi @steveq

    I have encountered the same difficulties as you. then I follow your way : setAceeptDrop(true) and overridden methods

    	void dragEnterEvent(QDragEnterEvent *e);
    	void dropEvent(QDropEvent *ev);
    	void dragMoveEvent(QDragMoveEvent* event);
    

    and add QDebug() in methods, but I don't see any output of console, and ListView still gives me the round circle with the line through it, indicating I can't drop. I'm obviously missing something here, but I just don't know what it is!


  • Lifetime Qt Champion

    @Luzz-T Please show the code of the class where overriding these methods and show how you're using this class.



  • @jsulm
    I only want get file name by external drop. ListViewDrop class inherits QListView

    ListViewDrop.h

    #ifndef LISTVIEWDROP_H
    #define LISTVIEWDROP_H
    
    #include <QListView>
    
    namespace Ui {
    class ListViewDrop;
    }
    
    class ListViewDrop : public QListView
    {
        Q_OBJECT
    public:
        explicit ListViewDrop(QWidget *parent = nullptr);
        ~ListViewDrop();
    
    protected:
        void dragEnterEvent(QDragEnterEvent* ev) override;
        void dragMoveEvent(QDragMoveEvent *e) override;
        void dropEvent(QDropEvent* ev) override;
    };
    
    #endif // LISTVIEWDROP_H
    

    ListViewDrop.cpp

    #include "listviewdrop.h"
    #include <qdebug.h>
    #include <QDragEnterEvent>
    #include <QDropEvent>
    #include <QMimeData>
    #include <QUrl>
    
    ListViewDrop::ListViewDrop(QWidget* parent):
        QListView(parent)
    {
        this->setAcceptDrops(true);
    
    }
    
    ListViewDrop::~ListViewDrop()
    {
    
    }
    
    void ListViewDrop::dragEnterEvent(QDragEnterEvent *ev)
    {
        qDebug() << "ListViewDrop::dragEnterEvent";
        if(ev->mimeData()->hasUrls())
        {
            ev->accept();
        }
        //ev->ignore();
    }
    
    void ListViewDrop::dropEvent(QDropEvent *ev)
    {
        qDebug() << "ListViewDrop::dropEvent";
        if(ev->mimeData()->hasUrls())
        {
            QList<QUrl> urls = ev->mimeData()->urls();
    
            for(int i=0; i<urls.size(); i++)
            {
                qDebug() << urls.at(i).toLocalFile();
            }
        }
    }
    
    void ListViewDrop::dragMoveEvent(QDragMoveEvent *e) {
        qDebug() << "ListViewDrop::dragMoveEvent";
    }
    
    

    I create a ListViewDrop is in a QWidget

    Thanks heaps in advance.


  • Lifetime Qt Champion

    @Luzz-T said in QListView in a QTabWidget tab - External Drop Not Working:

    I create a ListViewDrop is in a QWidget

    Please show how



  • @Luzz-T
    First you must answer @jsulm's question.

    But I see two possible issues in your current code, which may (or may not) need addressing after you have sorted out why your drag methods are not being hit at all:

    • Your dragMoveEvent(QDragMoveEvent *e) does not accept the event. You need to have that as well as dragEnterEvent() accept the drag, else you are liable to end up with the "no drop" indicator. dropEvent should also accept it, I don't know if that matters in your case.

    • In your dragEnterEvent you use ev->accept();. I'm not sure whether it matters, but here (and the other two methods) you are supposed to use acceptProposedAction().



  • @jsulm

    "create" actually I use qt Designer, Layout following :
    d4a95f13-7ed3-4d57-a8c2-f599b91dfd95-image.png

    In the picture above ,the red box is the “ListViewDrop” of my promotion by QListView,and "SelectView" used in main.cpp

    #include "selectview.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        SelectView w;
        w.show();
        return a.exec();
    }
    
    


  • @JonB

    Thanks for the tip. This is a test code, which I will pay attention to in development



  • @Luzz-T
    In addition to my earlier points, you may need to add the following:

    listView->setDropIndicatorShown(true);
    

    Also if your list view has a model look at what listView->model()->supportedDropActions() is returning. P.S. And also probably QAbstractItemModel::mimeTypes(), dropMimeData(), canDropMimeData() too --- in a word, are you using a model with your QListView?

    I think you should implement your dragMoveEvent() now, as I found without that I got stick with a "no-entry" drop indicator all the time.

    The important is to confirm whether any of your drag/drop overrides report a qDebug() output as their first line?

    Finally, in Designer did you set any of the properties on your listView, there are several related to d&d?



  • @JonB
    Thank you for your suggestions. I found the problem. The dragDropMode attribute is not set, it defaults to
    NoDragDrop . But strangely, the default attribute does not work in Windows, and the default value NoDragDrop is OK in MacOS,
    Also the version of QT may be different or for other reasons

    For all that It's already working. Thank all


Log in to reply