Search code examples
qtdrag-and-dropqmlqtquick2

QML: How to drag file from explorer to my Qt Quick application?


I'm running a QML app on desktop Linux (KDE). When a file is dropped onto the main window of the QML app, I need to get the file path and filename as text (with or without prefix "file:///").

If I drag a file from Dolphin file manager to Firefox's address bar, the text URI (e.g., "file:///home/user/Downloads/file.txt") is dropped into the address bar and the file opens in Firefox. This shows that the drag information for a file is available from the OS.

Another test I can do using the QML app I built (code provided below) is select some text (say on a webpage) and drag that selection onto my app's main window. This works and the app obtains and stores whatever the text selection contained.

However, I am not able to drag and drop files onto my QML application. I don't understand why I can drag a text selection but not a file. I'm providing a working example of my code.

Testing on KDE, when I drop a file onto the QML app window, the onDropped method is called. In there I do a console.log and drop.text is empty (and drag.source is null).

How is dragging and dropping of files (e.g., from a file manager) to the QML application supposed to be implemented?

and the QML:

import QtQuick.Window 2.2
import QtQuick 2.2
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.0

ApplicationWindow {
    id: rootId
    visible: true
    title: "Drag Drop MRE"
    property int margin: 16
    minimumWidth: 600
    minimumHeight: 480

FileDialog {
        id: fileDialog
        title: "Choose File Attachment"
        folder: shortcuts.home
        onAccepted: {
            attachments.text += fileDialog.fileUrls + '\n'
            console.log("added attachment: " + fileDialog.fileUrls)
        }
    }
    DropArea{
    id: drop
    enabled: true
    anchors.fill: parent
    anchors.margins: margin
    Rectangle {
        anchors.fill: parent
        color: "green"
        visible: parent.containsDrag
    }
    onEntered: console.log("entered DropArea")
    onDropped:{
        attachments.text += drop.text + '\n'
        console.log("added attachment: " + drop.text)
        console.log("drag.source: " + drag.source)
    }
    Drag.dragType: Drag.Automatic
    GridLayout {
        id: mainLayout
        anchors.fill: parent
        anchors.margins: margin

        Label {
            text: "Drag Drop MRE"
            font.pixelSize: 16
            Layout.row: 1
            Layout.column: 1
        }

        GroupBox {
            Layout.row: 2
            Layout.column: 1
            id: gridBox
            Layout.fillWidth: true

            GridLayout {
                id: gridLayout
                rows: 1
                flow: GridLayout.TopToBottom
                anchors.fill: parent

                Label { text: "Attached files"
                }

                TextArea {
                    id: attachments
                    Layout.fillWidth: true
                    implicitHeight: 300
                }
            }
        }

        GroupBox {
            Layout.row: 3
            Layout.column: 1
            id: rowBox
            Layout.fillWidth: true
            Row {
                anchors.right: parent.right
                id: rowLayout
                Button {
                    id: attach_button
                    text: "Attach"
                    onClicked: fileDialog.open();

                }
                Button {
                    id: save_button
                    text: "Save"
                    onClicked: console.log(attachments.text)
                }
                Button {
                    id: exit_button
                    text: "Exit"
                    onClicked: Qt.quit()
                }
            }
        }

    }} // Grid Layout & Drop Area
}

I found a related question, but it is the other way around: Drag file from application to explorer. Can my application do the copying?

I have also built and run the Qt examples. Unfortunately, the examples suffer from the same issue, so they aren't helpful in resolving the problem. However, I have read that what I'm trying to do can be done. I just haven't found a working solution.


Solution

  • This should work (tested on Windows):

    Window {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("Drop Area")
    
        DropArea {
            id: dropArea;
            anchors.fill: parent
            onEntered: {
                root.color = "gray";
                drag.accept (Qt.LinkAction);
            }
            onDropped: {
                console.log(drop.urls)
                root.color = "white"
            }
            onExited: {
                root.color = "white";
            }
        }
    }