• Aucun résultat trouvé

Drag and Drop

Dans le document Qt 4 THE BOOK of THE BOOK of (Page 196-200)

Events, Drag and Drop, and the Clipboard

7.4 Drag and Drop

QApplication app(argc, argv);

ChatWindow win;

win.show();

return app.exec();

}

7.4 Drag and Drop

Thedrag and drop functionality, that is, the capability to transfer information with the mouse between two widgets within the same program, or between two applications, is also regulated in Qt via events (Figure 7.2 on page 196). Each event has its own event handler in QWidget-based classes.

7.4.1 MIME Types

The first question to arise here is how the information should be encoded so that it can be transferred at all between two widgets via drag and drop. This is solved by the QMimeData class: It serves as a container for data, whose type is specified as a MIME type.3 A PNG image, for example, has the MIME type image/png, and a normal ASCII text file has the type text/plain.

It is also possible to use your own MIME types that are understood only by your own application. The names of these are defined according to the pattern application/x-vendor.content designator(page 242 shows an example).

In the following example we pack an image so that it can be “sent away” with a drag. To do this we write a QLabel-based widget that expects the path to a PNG image, displays it, and allows it to be included in other applications via drag and drop.

The following help function, called prepareImageDrag(), packs the image into a QMimeData object:

QMimeData* prepareImageDrag(const QString& path) {

QFile file(path);

3 MIME stands forMultipurpose Internet Mail Extensionsand is described in RFCs 2045, 2046, and 2047.

if (!file.open()) return;

QByteArray image = file.readAll();

QMimeData *mimeData = new QMimeData;

mimeData->setData("image/png", image);

return mimeData;

}

Fortunately QMimeData already includes its own encoding methods for the most important data types, such as for colors, HTML, reformatted text, and URLs. In practice, the following code is therefore sufficient to encode an image:

QMimeData* prepareImageDrag(const QString& path) {

QImage image(path);

QMimeData *mimeData = new QMimeData;

mimeData->setImageData(image);

return mimeData;

}

Qt even makes the image available in different formats with setImageData(). QMime-Data can save several MIME types together with their data in one object. When dragging, Qt offers all supported image formats, but it has a preference for PNG here, since this displays the best quality. The program that receives the drop then iterates through the list of MIME types and selects the data for the first MIME type that it can handle.

We make use of this property to include the path specification for the image: We pack it into a QUrl object, which converts it into an RFC-compliant URL, and we also include the normalized path specification as a text:

// draglabel/draglabel.cpp

#include <QtGui>

QMimeData* prepareImageDrag( const QString& path ) {

QImage pic( path );

QMimeData *mimeData = new QMimeData;

mimeData->setImageData( pic );

We intentionally do not use the path variable here directly: If we are passed a rel-ative path, this could become a problem with drag and drop between applications with different working directories. QUrl, however, resolves relative paths.

An application that obtains a drop originating from a drag with these MIME data first comes across the image data. If it cannot handle images, it then checks whether it can handle URLs, which would be the case for a file manager, for ex-ample. If these attempts are unsuccessful, the program can still access the path in text form, so that even an editor may act as a drop target. We will use this flexible variation in our example.

We have seen how to encode data in MIME format. But how do the MIME data from a widget in one part of our program manage to get to another part—or even into a completely different application? To illustrate this, Figure 7.2 shows the sequence of a typical drag-and-drop operation.

The source widget defines when a drag begins. If the widget cannot be clicked, which is the case for labels, it is sufficient to reimplement the mousePressEvent() event handler in a way that a drag is triggered by clicking:

// draglabel/draglabel.cpp (continued)

void DragLabel::mousePressEvent(QMouseEvent *event) {

if (event->button() == Qt::LeftButton) { QMimeData* data = prepareImageDrag(picPath);

QDrag *drag = new QDrag(this);

drag->setMimeData(data);

First we check whether the user is holding down the left mouse button. Then we prepare the QMimeData object with the help function prepareImageDrag() (page 195). We obtain the path from the member variable picPath. The constructor

retrieves the image displayed by the label from the specified path, with the help of the QLabel::setPixmap() method, as shown in the following code:

// draglabel/draglabel.cpp (continued)

#include "draglabel.h"

DragLabel::DragLabel(const QString& path, QWidget *parent) : QLabel(parent), picPath(path)

{

setPixmap(QPixmap(path));

}

In order to start the actual drag, we need to instantiate a new QDrag object in the mousePressEvent() and equip it with the MIME data using setMimeData().

In addition we assign the image from the label to the drag, for which we obtain a pointer with pixmap(). The graphical interface links it to the mouse cursor so that the content of the drag object is visualized. Therefore drags do not have to have an image set, although this is recommended from a usability point of view, since the user can then see what he is juggling with. We must ensure that the image is presented in the preview size. To do this we specify a version scaled down, with scaled(). KeepAspectRatio instructs the method to retain the page proportions, but not to exceed the maximum size of 100 pixels in either direction.

drag->start() begins the actual drag action. In the pattern from Figure 7.2, the source corresponds to our DragLabel.

To test the widget, we will write a small program that requires the path of an image file that can be read by Qt as a command-line argument. If this is available, we pass it to DragLabel during the instantiation:

// draglabel/main.cpp

#include <QtGui>

#include "draglabel.h"

int main( int argc, char* argv[] ) {

QApplication app( argc, argv );

if (argc < 2) return 1;

DragLabel w(argv[1]);

w.setWindowTitle(QObject::tr("Drag me!"));

w.show();

return app.exec();

}

The program then looks something like what is shown in Figure 7.3. We can drag the image into various programs, such as Gimp or Paint, and see what happens to it.

Figure 7.3:

TheDragLabelwith the Qt-4 logo

7.4.3 The Drop Side

So that we can better understand the drag-and-drop process illustrated in Figure 7.2 on page 196, we will now implement a label widget complementary to the DragLabel, which we will call DropLabel.

Each widget that should accept drops must first activate this capability in its con-structor, with setAcceptDrops(true):

// droplabel/droplabel.cpp

#include <QtGui>

#include "droplabel.h"

DropLabel::DropLabel(QWidget *parent) : QLabel(parent)

{

setAcceptDrops(true);

}

Events to be Handled

The first drop event that the widget needs to process occurs as soon as the mouse cursor moves into the widget. Accordingly, the widget’s dragEnterEvent() handler must check to see if the MIME types contained in the drag object are ones it can handle. For this purpose we access the the QMimeData object, via the mimeData() method:

Dans le document Qt 4 THE BOOK of THE BOOK of (Page 196-200)