Return to Linux Life Edit

  ホーム · 全てのクラス · メインのクラス · 注釈つき · グループ別 · 関数一覧

ドラッグ&ドロップ

Drag and drop provides a simple visual mechanism which users can use to transfer information between and within applications. (In the literature this is referred to as a "direct manipulation model".) Drag and drop is similar in function to the clipboard's cut and paste mechanism.

設定

  QApplication オブジェクトは ドラッグ&ドロップ に関する いくつかのプロパティを提供します。

These quantities provide sensible default values for you to use if you provide drag and drop support in your widgets.

ドラッグ

ドラッグを始めるには、 QDrag オブジェクトを作り、そのstart()関数を呼び出してください。widgetからのドラッグを有効にする最も簡単な方法はwidgetの mousePressEvent() を再実装し、ドラッグ操作を開始することです。

    void MainWindow::mousePressEvent(QMouseEvent *event)
    {
        if (event->button() == Qt::LeftButton
            && iconLabel->geometry().contains(event->pos())) {
            QDrag *drag = new QDrag(this);
            QMimeData *mimeData = new QMimeData;
            mimeData->setText(commentEdit->toPlainText());
            drag->setMimeData(mimeData);
            drag->setPixmap(iconPixmap);
            Qt::DropAction dropAction = drag->start();
        ...
        }
    }

ユーザーはドラッグ操作を完成させるのに多少時間がかかるかもしれませんが、アプリケーションに関する限りstart()関数は直ちに いくらかの値のうちひとつを返します。これらの値と、値が示す操作の終了状況は以下で詳しく説明されています。

クリックとドラッグでドラッグ操作を見分ける必要のあるwidgetでは、widgetの mousePressEvent() 関数をドラッグの開始地点を記録するよう再実装するのが便利です

    void DragWidget::mousePressEvent(QMouseEvent *event)
    {
        if (event->button() == Qt::LeftButton)
            dragStartPosition = event->pos();
    }

その後、 mouseMoveEvent()の中で私たちはドラッグを始めるべきか、そして操作を扱うためにドラッグオブジェクトを作るか決めることができます。

    void DragWidget::mouseMoveEvent(QMouseEvent *event)
    {
        if (!(event->buttons() & Qt::LeftButton))
            return;
        if ((event->pos() - dragStartPosition).manhattanLength()
             < QApplication::startDragDistance())
            return;
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
        mimeData->setData(mimeType, data);
        drag->setMimeData(mimeData);
        Qt::DropAction dropAction = drag->start(Qt::CopyAction | Qt::MoveAction);
        ...
    }

ドロップ

Widgetにドロップされたメディアを受け取るには、 setAcceptDrops(true) をそのwidgetで呼び出し、イベントハンドラのメソッドをオーバーライドしてください。例えば、次のコードはドロップイベントを QWidget のサブクラスのコンストラクタ内で有効にしています。

    Window::Window(QWidget *parent)
        : QWidget(parent)
    {
        ...
        setAcceptDrops(true);
    }

全てのwidgetで dragEnterEvent() dropEvent()を実装するだけでドロップを有効にすることができます。dragEnterEvent()関数は典型的にQtにwidgetが受け取ったデータの型を知らせるのに使われます。dropEvent()はドロップされたデータを開いたり、あなたのアプリケーションに適したように扱うのに使われます。

次のコードは、どのようにdragEnterEvent()にプレーンテキストを扱うだけのドラッグ&ドロップシステムを実装できるのかを示しています。

    void Window::dragEnterEvent(QDragEnterEvent *event)
    {
        if (event->mimeData()->hasFormat("text/plain"))
            event->acceptProposedAction();
    }

dropEvent()は QTextBrowser インスタンスへのイベントからのテキストを書き、 QComboBox インスタンスを、データを説明するのに使われるMIMEタイプのリストで満たします。

    void Window::dropEvent(QDropEvent *event)
    {
        textBrowser->setPlainText(event->mimeData()->text());
        mimeTypeCombo->clear();
        mimeTypeCombo->addItems(event->mimeData()->formats());
        event->acceptProposedAction();
    }

この例では、私たちは中身を確かめずに提案されたアクションを受け止めています。実際のアプリケーションでは、もしアクションが無関係なら、提案されたアクションを受け取ったりデータを扱ったりせずに、dropEvent()関数からreturnする必要があるでしょう。例えば、私たちがアプリケーションで外部のソースへのリンクをサポートしないなら Qt::LinkAction アクションを無視するようにするでしょう。私たちはまた、提案されたアクションを無視したり、データに含まれているその他のアクションを行うこともあるでしょう。 そうすると、私たちはイベントオブジェクトの setDropAction() 関数を、好きなアクションを Qt::DropActionの中から選び、代入して呼び出すでしょう。

For more sophisticated applications, reimplementing dragMoveEvent() and dragLeaveEvent() will let you make certain parts of your widgets sensitive to drop events, and give you more control over drag and drop in your application.

ドラッグ&ドロップアクション

In the simplest case, the target of a drag and drop action receives a copy of the data being dragged, and the source decides whether to delete the original. This is described by the CopyAction action. The target may also choose to handle other actions, specifically the MoveAction and LinkAction actions. If the source calls QDrag::start(), and it returns MoveAction, the source is responsible for deleting any original data if it chooses to do so. The QMimeData and QDrag objects created by the source widget should not be deleted - they will be destroyed by Qt. The target is responsible for taking ownership of the data sent in the drag and drop operation; this is usually done by keeping references to the data.

If the target understands the LinkAction action, it should store its own reference to the original information; the source does not need to perform any further processing on the data. The most common use of drag and drop actions is when performing a Move within the same widget; see the section on Drop Actions for more information about this feature.

ほかの一般的なドラッグアクションの利用は、text/uri-listのような参照型を使う場合、つまりドラッグされたデータが実際にファイルやオブジェクトを参照している場合です。

Adding New Drag and Drop Types

Drag and drop is not limited to text and images. Any type of information can be transferred in a drag and drop operation. To drag information between applications, the applications must be able to indicate to each other which data formats they can accept and which they can produce. This is achieved using MIME types. The QDrag object constructed by the source contains a list of MIME types that it uses to represent the data (ordered from most appropriate to least appropriate), and the drop target uses one of these to access the data. For common data types, the convenience functions handle the MIME types used transparently but, for custom data types, it is necessary to state them explicitly.

To implement drag and drop actions for a type of information that is not covered by the QDrag convenience functions, the first and most important step is to look for existing formats that are appropriate: The Internet Assigned Numbers Authority (IANA) provides a hierarchical list of MIME media types at the Information Sciences Institute (ISI). Using standard MIME types maximizes the interoperability of your application with other software now and in the future.

To support an additional media type, simply set the data in the QMimeData object with the setData() function, supplying the full MIME type and a QByteArray containing the data in the appropriate format. The following code takes a pixmap from a label and stores it as a Portable Network Graphics (PNG) file in a QMimeData object:

        QByteArray output;
        QBuffer outputBuffer(&output);
        outputBuffer.open(QIODevice::WriteOnly);
        imageLabel->pixmap()->toImage().save(&outputBuffer, "PNG");
        mimeData->setData("image/png", output);

Of course, for this case we could have simply used setImageData() instead, but the above code provides more control over the image's data format.

Drop Actions

In the clipboard model, the user can cut or copy the source information, then later paste it. Similarly in the drag and drop model, the user can drag a copy of the information or they can drag the information itself to a new place (moving it). The drag and drop model has an additional complication for the programmer: The program doesn't know whether the user wants to cut or copy the information until the operation is complete. This often makes no difference when dragging information between applications, but within an application it is important to check which drop action was used.

We can reimplement the mouseMoveEvent() for a widget, and start a drag and drop operation with a combination of possible drop actions. For example, we may want to ensure that dragging always moves objects in the widget:

    void DragWidget::mouseMoveEvent(QMouseEvent *event)
    {
        if (!(event->buttons() & Qt::LeftButton))
            return;
        if ((event->pos() - dragStartPosition).manhattanLength()
             < QApplication::startDragDistance())
            return;
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
        mimeData->setData(mimeType, data);
        drag->setMimeData(mimeData);
        Qt::DropAction dropAction = drag->start(Qt::CopyAction | Qt::MoveAction);
        ...
    }

The action returned by the start() function may default to a CopyAction if the information is dropped into another application but, if it is dropped in another widget in the same application, we may obtain a different drop action.

The proposed drop actions can be filtered in a widget's dragMoveEvent() function. However, it is possible to accept all proposed actions and let the user decide which they want to accept later:

    void DragWidget::dragMoveEvent(QDragMoveEvent *event)
    {
        event->acceptProposedAction();
    }

When a drop occurs in the widget, the dropEvent() handler function is called, and we can deal with each possible action in turn. First, we deal with drag and drop operations within the same widget:

    void DragWidget::dropEvent(QDropEvent *event)
    {
        if (event->source() == this && event->possibleActions() & Qt::MoveAction)
            return;

In this case, we refuse to deal with move operations. Each type of drop action that we accept is checked and dealt with accordingly:

        if (event->possibleActions() == Qt::MoveAction) {
            event->acceptProposedAction();
            // Process the data from the event.
        } else if (event->possibleActions() == Qt::CopyAction) {
            event->acceptProposedAction();
            // Process the data from the event.
        } else {
            // Ignore the drop.
            return;
        }
        ...
    }

Note that we checked for individual drop actions in the above code. It is often more helpful to check for the presence of each action in the value supplied by possibleActions() and set the accepted action with the event's setDropAction() function.

Drop Rectangles

The widget's dragMoveEvent() can be used to restrict drops to certain parts of the widget by only accepting the proposed drop actions when the cursor is within those areas. For example, the following code accepts any proposed drop actions when the cursor is over a child widget (dropFrame):

    void Window::dragMoveEvent(QDragMoveEvent *event)
    {
        if (event->mimeData()->hasFormat("text/plain")
            && event->answerRect().intersects(dropFrame->geometry()))
            event->acceptProposedAction();
    }

The dragMoveEvent() can also be used if you need to give visual feedback during a drag and drop operation, to scroll the window, or whatever is appropriate.

クリップボード

Applications can also communicate with each other by putting data on the clipboard. To access this, you need to obtain a QClipboard object from the QApplication object:

        clipboard = QApplication::clipboard();

The QMimeData class is used to represent data that is transferred to and from the clipboard. To put data on the clipboard, you can use the setText(), setImage(), and setPixmap() convenience functions for common data types. These functions are similar to those found in the QMimeData class, except that they also take an additional argument that controls where the data is stored: If Clipboard is specified, the data is placed on the clipboard; if Selection is specified, the data is placed in the mouse selection (on X11 only). By default, data is put on the clipboard.

For example, we can copy the contents of a QLineEdit to the clipboard with the following code:

        clipboard->setText(lineEdit->text(), QClipboard::Clipboard);

Data with different MIME types can also be put on the clipboard. Construct a QMimeData object and set data with setData() function in the way described in the previous section; this object can then be put on the clipboard with the setMimeData() function.

The QClipboard class can notify the application about changes to the data it contains via its dataChanged() signal. For example, we can monitor the clipboard by connecting this signal to a slot in a widget:

        connect(clipboard, SIGNAL(dataChanged()), this, SLOT(updateClipboard()));

The slot connected to this signal can read the data on the clipboard using one of the MIME types that can be used to represent it:

    void ClipWindow::updateClipboard()
    {
        QStringList formats = clipboard->mimeData()->formats();
        QByteArray data = clipboard->mimeData()->data(format);
        ...
    }

The selectionChanged() signal can be used on X11 to monitor the mouse selection.

Interoperating with Other Applications

On X11, the public XDND protocol is used, while on Windows Qt uses the OLE standard, and Qt/Mac uses the Carbon Drag Manager. On X11, XDND uses MIME, so no translation is necessary. The Qt API is the same regardless of the platform. On Windows, MIME-aware applications can communicate by using clipboard format names that are MIME types. Already some Windows applications use MIME naming conventions for their clipboard formats. Internally, Qt has facilities for translating proprietary clipboard formats to and from MIME types. This interface will be made public at some time, but if you need to do such translations now, contact your Qt Technical Support service.

On X11, Qt also supports drops via the Motif Drag & Drop Protocol. The implementation incorporates some code that was originally written by Daniel Dardailler, and adapted for Qt by Matt Koss <koss@napri.sk> and Trolltech. Here is the original copyright notice:

    Copyright 1996 Daniel Dardailler.
    Permission to use, copy, modify, distribute, and sell this software
    for any purpose is hereby granted without fee, provided that the above
    copyright notice appear in all copies and that both that copyright
    notice and this permission notice appear in supporting documentation,
    and that the name of Daniel Dardailler not be used in advertising or
    publicity pertaining to distribution of the software without specific,
    written prior permission. Daniel Dardailler makes no representations
    about the suitability of this software for any purpose. It is
    provided "as is" without express or implied warranty.
    Modifications Copyright 1999 Matt Koss, under the same license as
    above.


Copyright © 2005 Trolltech Trademarks
Qt 4.0.0