Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Dragging out to non-Qt desktop



  • Hello,
    I’m working on dragging and dropping capability in my desktop app, which consists of multiple QWidget.
    D&D works OK if it’s internal movement where the drop receiver is another QWidget.
    My question is how can I achieve the same functionality between the desktop app and non Qt app such as Window desktop where no qt app exists.
    How and where can I handle the dropEvent outside of Qt?

    Many thanks in advance for your help.
    Sat


  • Moderators

    @ShinSat
    your question is very unclear.

    How and where can I handle the dropEvent outside of Qt?

    You shouldn't and you wont handle the drop outside your application. Similar to a copy and aste via clipboard you simply provide data to the operating system and let it do the multi-window drag-handling. The final target application which receives the drop handles the data on it's own.
    The only result you get is when your QDrag::exec() returnes and check the returned drop result.


  • Lifetime Qt Champion

    Hi
    To drop to desktop, you must follow windows rules for the mimedata
    else it wont accept.

    This sample drags file from Qt to desktop / explorer
    It uses a native windows header. (#include <Shlobj.h>)

    #include "mainwindow.h"
    #include <QApplication>
    #include <QtGui>
    #include <Shlobj.h>
    
    class Widget : public QWidget {
      void mousePressEvent(QMouseEvent* e) {
        QPoint hotSpot = e->pos();
        QList<QUrl> strList;
        FILEGROUPDESCRIPTOR fgd;
        FILEDESCRIPTOR fd[ 1 ];
    
        strList.append( QUrl( "http://www.qt.io/" ) );
        fgd.cItems = 1;
    
        fgd.fgd[ 0 ].dwFlags = FD_PROGRESSUI;
        wcscpy( fgd.fgd[ 0 ].cFileName, L"qtrules.url" );
        QMimeData* mimeData = new QMimeData;
        mimeData->setText("www.gaspar.net");
        mimeData->setUrls( strList );
        mimeData->setData("text/plain", QByteArray( "http://www.gaspar.net" ) );
        mimeData->setData("text/html", QByteArray( "<!--StartFragment--><a href=\"http://www.gaspar.net\">The Gaspar Website</a><!--EndFragment-->" ) );
        mimeData->setData("FileName", QByteArray( "Gaspar Website.url" ) );
        mimeData->setData("FileGroupDescriptorW", QByteArray::fromRawData( ( const char* )&fgd, sizeof ( FILEGROUPDESCRIPTOR ) ) );
        mimeData->setData("FileContents", QByteArray( "[InternetShortcut]\r\nURL=http://www.gaspar.net" ) );
    
        QPixmap pixmap(":/images/imagelists/16x16/webpage.png");
        render(&pixmap);
    
        QDrag* drag = new QDrag(this);
        drag->setMimeData(mimeData);
        drag->setPixmap(pixmap);
        drag->setHotSpot(hotSpot);
    
        Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
    
        if (dropAction == Qt::MoveAction) {
          update();
        }
      }
    };
    
    int main(int argc, char* argv[]) {
      QApplication app(argc, argv);
      Widget window;
      window.show();
      return app.exec();
    }
    

    Credits goes to http://www.gaspar.net/oteqmimewindows.html

    alt text



  • @mrjj
    There should be a "Code Snippets" forum here, and this kind of post could be put there with a decent title.


  • Lifetime Qt Champion

    @JNBarchan
    Yes, that would be great. I think we might be able to use the wiki for it since it already has samples.


  • Moderators

    @mrjj
    Just a note: File drags to desktop can also be achieved using Qt classes only:

    1. start a drag containing all the needed mime-types
    2. create an empty QTemporaryFile with the target file-name and set it's file path to the mime-data (file-urls type)
    3. fill the temporary file once it is requested in the QMimeData::retrieveData() method (see the delayed encoding example)
    4. the OS copies the (temp-)file to the drop-position

  • Lifetime Qt Champion

    @raven-worx

    Did you try delayed encoding sample ?

    The sample will not work for me on win 7 or win 10.
    Tried 2 pc. Desktop/explorer wont accept.

    So i wonder if issue with my pcs or just dont work like that in newer Windows.


  • Moderators

    @mrjj
    yes, but it's a while ago. I will give it a try.


  • Lifetime Qt Champion

    @raven-worx

    Super. Maybe i just do it wrong.
    I drag the export Button out from window. It show red stop sign.


  • Moderators

    @mrjj
    the following (unoptimized code) works (Qt 5.9, Windows 7):

    MimeData::MimeData()
        : QMimeData()
    {
        QString filePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) % QDir::separator() % "Gaspar Website.url";
        m_File = new QFile( filePath, this );
    }
    
    MimeData::~MimeData()
    {
        if( m_File && m_File->isOpen() )
            m_File->close();
    }
    
    QStringList MimeData::formats() const
    {
        return QMimeData::formats() << QLatin1String("text/uri-list");
    }
    
    QVariant MimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
    {
        if( mimeType == QStringLiteral("text/uri-list") )
        {
            // since this method may be called multiple during a single drag it's not necessary to also write to the file multiple times -> needs optimization
            if( m_File->open(QFile::WriteOnly) )
            {
                m_File->write( QByteArray("[InternetShortcut]\r\nURL=http://www.gaspar.net") );
                m_File->close();
            }
    
            return QVariant::fromValue<QUrl>( QUrl::fromLocalFile(QFileInfo(*m_File).absoluteFilePath()) );
        }
        else
            return QMimeData::retrieveData(mimeType, type);
    }
    

    Although it seems there are some caveats:

    1. it stopped working when i set the path of the temp file into a subfolder in the temp-dir ... o.O
    2. the retrieveData() always requests the data with the type QVariant::QVariantList. But still it only works when i return a QUrl (not a QList) packed into the variant data.

    Very strange.


  • Lifetime Qt Champion

    @raven-worx

    Good work.
    Ehh so it likes only root of tmp dir ?? odd
    2: that is a bit odd but i guess it was only ment for 1 file then.

    Would it be possible to get the test code ?
    Want to see if win 10 likes it also :)

    This is nicer than the platform bound i found :) \o/


  • Moderators

    @mrjj said in Dragging out to non-Qt desktop:

    Would it be possible to get the test code ?

    thats actually all the code (despite the obvious header file).

    All i did additionally for testing was this:

    void MainWindow::mousePressEvent(QMouseEvent *event)
    {
        event->accept();
    
        MimeData* mimeData = new MimeData;
    
        QDrag drag( this );
            drag.setMimeData(mimeData);
        drag.exec(Qt::CopyAction);
    }
    

    the mime type is text/uri-list, so a list does make sense. It also makes sense to copy multiple files at once to a folder in the filesystem explorer.
    I have no idea why it's not working. Would need to debug into Qt source code to make sure.


Log in to reply