Шаг 124.
Библиотека Qt.
Создание собственных типов перетаскивания

    На этом шаге рассмотрим создание собственных типов перетаскивания.

    Возможности Qt не ограничены перетаскиванием данных определенных типов, таких как, например, текст или растровые изображения. Перетаскиваться может информация любого типа. Для этого необходимо определиться с идентификацией типа для перетаскиваемых данных, для того, чтобы принимающая сторона могла принять решение — допускает она их или нет.

    Идентификация достигается использованием строки mimeType, которая представляет перетаскиваемые данные, эта же строка используется принимающей стороной для получения доступа к данным. Таким образом, основной метод для создания объекта перетаскивания мог бы выглядеть примерно так, как показано ниже.

MyDragClass::startDrag()
{
//Ссоздается объект растрового изображения, данные которого будут
//подвергнуты перетаскиванию
    QImage img("mira.jpg");
//Для того чтобы поместить данные в бинарном виде в объект класса QMimeData,
//мы создаем объекты классов QByteArray и QBuffer
    QByteArray data;
    QBuffer buf(&data);
    QMimeData* pMimeData = new QMimeData;
    buf.open(QIODevice::WriteOnly);
//При помощи метода QImage::save() мы записываем данные в объект класса QBuffer,
//который управляет бинарным массивом (data)
    img.save(&buf, "JPG");
//После чего мы передаем этот массив в метод QMimeData::setData().
//Первый параметр этого метода — строка image/jpg -  является идентификатором
//для перетаскиваемого типа данных
    pMimeData->setData("image/jpg", data);
    QDrag* pDrag = new QDrag(this);
    pDrag->setMimeData(pMimeData);
    pDrag->exec(Qt::MoveAction);
} 

    Если перетаскивание должно работать только в пределах вашего приложения, то можно поступить еще проще и повысить эффективность: избежать промежуточного копирования данных в QByteArray и использовать их напрямую. Этот способ заключается в следующем. Ввиду того что приложение работает в одном адресном пространстве, можно напрямую передавать адреса и указатели на объекты, записывая их в MIME-объектах. Разумеется, сами данные не должны быть локальными.

    Продемонстрируем этот подход на примере, где в качестве данных передаются указатели на виджеты, с помощью которых принимающая сторона может получить доступ к сброшенному виджету. В окне программы на рис. 1, показаны два фрейма, которые могут обмениваться данными.


Рис.1. Пример обмена данными

    Ниже представлено определение класса DragWidget:

class DragWidget : public QFrame
{
public:
    DragWidget(QWidget *parent=0);
protected:
    void dragEnterEvent(QDragEnterEvent *event);
    void dragMoveEvent(QDragMoveEvent *event);
    void dropEvent(QDropEvent *event);
    void mousePressEvent(QMouseEvent *event);
};

    Рассмотрим метод dragEnterEvent:

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
//Проверяем на совместимость тип данных перетаскиваемых объектов
    if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
//Если источник операции перетаскивания виджет этого приложения,
//функция source() возвращает указатель на виджет, который инициирует перенос
        if (event->source() == this) {
//устанавливаем действие по перемещению данных при отпускании
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else {
//действия по перетаскиванию будут игнорироваться,
//если переносимые данные покидают виджет
        event->ignore();
    }
}

    Рассмотрим метод dropEvent:

void DragWidget::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
//создаем бинарные массив и 
        QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
//устройство открыто только для чтения данных
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
//растровое изображение
        QPixmap pixmap;
//позиция точки
        QPoint offset;
//помещаем соответствующие значения
        dataStream >> pixmap >> offset;
//создаем надпись, загружаем растровое изображение, позицианируем
//и отображаем на экране, удаляем после закрытия
        QLabel *newIcon = new QLabel(this);
        newIcon->setPixmap(pixmap);
        newIcon->move(event->pos() - offset);
        newIcon->show();
        newIcon->setAttribute(Qt::WA_DeleteOnClose);
        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else {
        event->ignore();
    }
} 

    Рассмотрим метод mousePressEvent:

void DragWidget::mousePressEvent(QMouseEvent *event)
{
//создаем дочерний виджет надписи в позиции курсора
    QLabel *child = static_cast<QLabel*>(childAt(event->pos()));
    if (!child)
        return;
//сохраняем растровое изображение надписи
    QPixmap pixmap = *child->pixmap();
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
//помещаем в массив растровое изображение и координаты надписи
    dataStream << pixmap << QPoint(event->pos() - child->pos());
//создаем объект класса QMimeData, в который, вызовом метода setData(),
//передаются перетаскиваемые данные 
    QMimeData *mimeData = new QMimeData;
    mimeData->setData("application/x-dnditemdata>, itemData);
//создаем объект перетаскивания класса QDrag, в конструктор которого
//передается указатель на виджет, из которого осуществляется перетаскивание
    QDrag *drag = new QDrag(this);
    drag->setMimeData(mimeData);
    drag->setPixmap(pixmap);
    drag->setHotSpot(event->pos() - child-*gt;pos());
//создаем темный прямоугольник на растровом изображении,
//которое собираемся перемещать
    QPixmap tempPixmap = pixmap;
    QPainter painter;
    painter.begin(&tempPixmap);
    painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127));
    painter.end();
    child->setPixmap(tempPixmap);
    if (drag->exec(Qt::MoveAction))
        child->close();
    else {
        child->show();
        child->setPixmap(pixmap);
    }
}

    Файлы приложения можно взять здесь.

    На следующем шаге рассмотрим работу с буфером обмена.




Предыдущий шаг Содержание Следующий шаг