Перетаскивание в QML

Последнее обновление: 07.01.2024

Любой элемент в QML может быть источником событий перетаскивания (Drag and Drop). У каждого элемента типа Item (который является базовым типом для всех стандартных элементов интерфейса) имеется прикрепленное свойство Drag, которое представляет одноименный тип Drag и с помощью которого можно установить настройки перетаскивания. Для этого данный тип предоставляет ряд свойств, из которых отмечу следующие:

  • active: определяет, активна ли в данный момент последовательность событий перетаскивания. Установка для свойства значения true сгенерирует событие QDragEnter, то есть индикатор начало перемещения элемента. Установка значения false приведет к отправке события QDragLeave. Пока это свойство равно true, любое изменение положения элемента будет генерировать событие QDragMove - индикатор перемещения элемента с новой позицией элемента.

  • hotSpot: хранит позицию перетаскивания относительно верхнего левого угла элемента. По умолчанию это (0, 0). Изменения в hotSpot вызывают новое перетаскивание с обновленной позицией.

  • imageSource: хранит адрес изображения (объект QUrl), которое отображается во время перетаскивания

  • mimeData: тип перетаскиваемых данных

  • source: содержит объект-источник событий перетаскивания. По умолчанию это элемент, к которому прикреплено свойство Drag и который перетаскивается.

  • target: объект, на который выполняется перетаскивание.

Рассмотрим простейший пример:

import QtQuick

Window {
    width: 250
    height: 150
    visible: true
    title: "METANIT.COM"

    Rectangle {
        x: 10; y: 10        // начальная позиция
        width: 50; height: 50
        color: "red"

        Drag.active: dragArea.drag.active
        Drag.hotSpot.x: 10
        Drag.hotSpot.y: 10

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent
        }
    }
}

Для теста перетаскивания определен элемент Rectangle, а чтобы можно было взаимодействовать мышью, внутри Rectangle определена область MouseArea, которая занимает все пространство прямоугольника. Привязка свойства active к свойству MouseArea::drag.active приведет к вызову startDrag, когда пользователь начнет перетаскивание. У объекта MouseArea, в свою очередь, устанавливается, свойство drag.target, которому передается идентифкатор перетаскиваемого элемента - в данном случае - идентифкатор родительского объекта Rectangle.

И при захвате мышью прямоугольника мы сможем переместить его в любое место окна.

Drag and Drop и перетаскивание элементов в QML и QT

DropArea

Для создания области перетаскивания применяется тип DropArea, который представляет невидимый элемент и который может получать события перетаскивания элемента. Когда перетаскивание элемента активно (свойство drag.active равно true), любое изменение позиции элемента будет генерировать событие перетаскивания, которое будет получено объектом DropArea.

Например, немного усложним предыдущий пример. Допустим, нам надо, чтобы прямоугольник можно было перетащить не в любое место окна, а только в какую-то определенную область. В этом случае нам надо отселживать перемещение перетаскиваемого элемента:

import QtQuick

Window {
    width: 250
    height: 150
    visible: true
    title: "METANIT.COM"

    Rectangle {
        id: dragItem
        x: 10; y: 10
        property point beginDrag    // для фиксации начальной позиции
        width: 50; height: 50
        color: "red"

        Drag.active: dragArea.drag.active

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent

            // по нажатию устанавливаем начальную позицию прямоугольника
            onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y)

            onReleased: {
                // возвращаемся на начальную позицию
                dragItem.x = dragItem.beginDrag.x
                dragItem.y = dragItem.beginDrag.y
            }
        }
    }
}

Здесь в элементе Rectangle добавлено новое свойство для хранения начальной позиции элемента:

property point beginDrag

Это свойство представляет тип point - точку, у которой есть свойства x и y для хранения координат точки. И по нажатию на область MouseArea в обработчике onPressed устанавливаем эту точку с помощью функции Qt.point:

onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y)

то есть beginDrag представляет начальные координаты прямоугольника (x=10, y=10).

В обработчике onReleased выполняет код при отпускании мыши, когда перетаскивание завершено:

onReleased: {
    dragItem.x = dragItem.beginDrag.x
    dragItem.y = dragItem.beginDrag.y
}

То есть свойствам x и y прямоугольника присваиваются начальные значения, сохраненные в beginDrag.

Таким образом, куда бы мы не перетащили прямоугольник, он всегда будет возвращаться на предыдущую позицию. Теперь пойдем дальше и добавим в окне область для перетаскивания:

import QtQuick

Window {
    width: 250
    height: 150
    visible: true
    title: "METANIT.COM"

    Rectangle {
        id: dragItem
        x: 10; y: 10
        width: 50; height: 50
        color: "red"
        Drag.active: dragArea.drag.active

        property point beginDrag    // для фиксации начальной позиции
        property bool caught: false // элемент в DropArea
        z: dragArea.drag.active || dragArea.pressed ? 2 : 1

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent

            // по нажатию устанавливаем начальную позицию прямоугольника
            onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y)

            onReleased: {
                // возвращаемся на начальную позицию
                if(!dragItem.caught){
                    dragItem.x = dragItem.beginDrag.x
                    dragItem.y = dragItem.beginDrag.y
                }
            }
        }
    }

    Rectangle {
        // обработчик вхождения элемента в DropArea
        function onEntered(drag){ drag.source.caught = true }

        x: parent.width/2
        width: parent.width/2 ; height:parent.height
        color: "silver"

        DropArea {
            id: dropArea
            anchors.fill: parent
            // обработчик ухода элемента из DropArea
            onExited: drag.source.caught = false
        }

        Component.onCompleted: {
            // связь сигнала entered с onEntered
            dropArea.entered.connect(onEntered)
        }
    }
}

Прежде всего в перетаскиваемый элемент Rectangle добавлено новое свойство

property bool caught: false // элемент в DropArea

Если это свойство равно true, то элемент находится в пределах DropArea.

И теперь обработчик опускания мыши в MouseArea возвращает прямоугольник на начальную позицию, если это свойство равно false:

onReleased: {
    // возвращаемся на начальную позицию
    if(!dragItem.caught){
        dragItem.x = dragItem.beginDrag.x
        dragItem.y = dragItem.beginDrag.y
    }
}

В конце кода окна добавлен второй прямоугольник Rectangle, который будет представлять область перетаскивания. Этот прямоугольник занимает правую половину окна. А все пространство этого прямоугольника занимает DropArea.

Для DropArea определены два обработчика сигналов. Прежде всего, когда перетаскиваемый элемент входит в границы DropArea, то генерируется сигнал entered, который обрабатывается функцией onEntered:

function onEntered(drag){ drag.source.caught = true }

В качестве параметра в функцию передается информация о событии в виде объекта DragEvent. Его свойство source указывает на перетаскиваемый элемент. В данном случае просто переключаем у прямоугольника свойство caught в true, тем самым указывая, что перетаскиваемый элемент надо DropArea.

Другой сигнал - exited срабатывает, когда элемент покидает границы DropArea. Он обрабатывается функцией onExited, которая устанавливает для свойства caught значение false:

onExited: drag.source.caught = false

И чтобы при перетаскивании перемещаемый прямоугольник отображался поверх DropArea, у него устанавливаем свойство z:

z: dragArea.drag.active || dragArea.pressed ? 2 : 1

Таким образом, прямоугольник можно будет перетащить только на DropArea:

Drag and Drop и перетаскивание элементов на DropArea в QML и QT
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850