Drag and Drop, often referred to as DnD, is an essential technology in modern software development. It provides a mechanism for exchanging information within an application or even between applications. The exchange of clipboard contents between the operating system and the application can also be considered a part of drag and drop. , sans-serif] Dragging is to move the dragged object, and releasing is to drop the dragged object. The former is a process of pressing the mouse button and moving, and the latter is a process of releasing the mouse button; usually the mouse button is kept pressed between these two operations. Of course, this is just a general situation, and other situations still depend on the specific implementation of the application. For Qt, a component can be dragged as a dragged object, as a released destination object, or both. In the following example (from C++ GUI Programming with Qt4, 2nd Edition), we will create a program that drags in a text file from the operating system and reads the contents in a window. - class MainWindow: public QMainWindow
- {
- Q_OBJECT
- public:
- MainWindow(QWidget *parent = 0);
- ~MainWindow();
- protected:
- void dragEnterEvent(QDragEnterEvent *event);
- void dropEvent(QDropEvent *event);
- private:
- bool readFile(const QString &fileName);
- QTextEdit *textEdit;
- };
[color=rgb(0, 204, 255) !important]Copy code Notice that we need to rewrite the dragEnterEvent() and dropEvent() functions. As the name implies, the former is the event of dragging and dropping, and the latter is the event of releasing the mouse. - MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- textEdit = new QTextEdit;
- setCentralWidget(textEdit);
-
- textEdit->setAcceptDrops(false);
- setAcceptDrops(true);
-
- setWindowTitle(tr("Text Editor"));
- }
-
- MainWindow::~MainWindow()
- {
- }
[color=rgb(0, 204, 255) !important]Copy code In the constructor, we create a QTextEdit object. By default, QTextEdit can accept text data dragged and dropped from other applications. If the user drags a file to this side, the file name will be inserted at the cursor position by default. But we want MainWindow to read the file content instead of just inserting the file name, so we add drag and drop operations to MainWindow. First, set the setAcceptDrops() function of QTextEdit to false, and set the setAcceptDrops() of MainWindow to true, so that we can let MainWindow intercept the drag and drop event instead of letting QTextEdit handle it. - void MainWindow::dragEnterEvent(QDragEnterEvent *event)
- {
- if (event->mimeData()->hasFormat("text/uri-list")) {
- event->acceptProposedAction();
- }
- }
[color=rgb(0, 204, 255) !important]Copy code When the user drags an object onto a component, the system calls back the dragEnterEvent() function. If we call the acceptProposeAction() function in the event handling code, we can hint to the user that you can drop the dragged object on this component. By default, components will not accept drag and drop. If we call this function, Qt will automatically prompt the user whether the object can be dropped on the component by changing the cursor style. Here, we want to tell the user that the window can accept drag and drop, but we only accept a certain type of file, not all files. We first check the MIME type information of the dragged and dropped file. MIME types are defined by the Internet Assigned Numbers Authority (IANA), and Qt's drag and drop events use MIME types to determine the type of the dragged and dropped object. For more information about MIME types, please refer to http://www.iana.org/assignments/media-types/. The MIME type text/uri-list is usually used to describe a list of URIs. These URIs can be file names, URLs, or other resource descriptors. If we find that the user drags and drops a text/uri-list data (that is, a file name), we accept this action. - void MainWindow::dropEvent(QDropEvent *event)
- {
- QList urls = event->mimeData()->urls();
- if (urls.isEmpty()) {
- return;
- }
-
- QString fileName = urls.first().toLocalFile();
- if (fileName.isEmpty()) {
- return;
- }
-
- if (readFile(fileName)) {
- setWindowTitle(tr("%1 - %2").arg(fileName, tr("Drag File")));
- }
- }
-
- bool MainWindow::readFile(const QString &fileName)
- {
- bool r = false;
- QFile file(fileName);
- QString content;
- if(file.open(QIODevice::ReadOnly)) {
- content = file.readAll();
- r = true;
- }
- textEdit->setText(content);
- return r;
- }
[color=rgb(0, 204, 255) !important]Copy code When the user releases the object onto the component, the system calls back the dropEvent() function. We use QMimeData::urls() to get a list of QUrls. Usually, this kind of drag should have only one file, but it does not rule out dragging multiple files together. So we need to check whether this list is empty. If it is not empty, take out the first one, otherwise return immediately. Finally, we call the readFile() function to read the file content. The content of this function is very simple. We have also explained the operation of files before, so I will not repeat it here. Now you can run it to see the effect. The following example is also from C++ GUI Programming with Qt4, 2nd Edition. In this example, we will create two parallel lists on the left and right, and drag the data between them. - class ProjectListWidget : public QListWidget
- {
- Q_OBJECT
- public:
- ProjectListWidget(QWidget *parent = 0);
- protected:
- void mousePressEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
- void dragEnterEvent(QDragEnterEvent *event);
- void dragMoveEvent(QDragMoveEvent *event);
- void dropEvent(QDropEvent *event);
- private:
- void performDrag();
- QPoint startPos; ProjectListWidget is the implementation of our list. This class inherits from QListWidget. In the final program, there will be two ProjectListWidgets side by side.
- ProjectListWidget::ProjectListWidget(QWidget *parent)
- : QListWidget(parent)
- {
- setAcceptDrops(true);
- }
[color=rgb(0, 204, 255) !important]Copy code Constructor We set setAcceptDrops() to enable ProjectListWidget to support drag operations. - void ProjectListWidget::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton)
- startPos = event->pos();
- QListWidget::mousePressEvent(event);
- }
-
- void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
- {
- if (event->buttons() & Qt::LeftButton) {
- int distance = (event->pos() - startPos).manhattanLength();
- if (distance >= QApplication::startDragDistance())
- performDrag();
- }
- QListWidget::mouseMoveEvent(event);
- }
-
- void ProjectListWidget::performDrag()
- {
- QListWidgetItem *item = currentItem();
- if (item) {
- QMimeData *mimeData = new QMimeData;
- mimeData->setText(item->text());
-
- QDrag *drag = new QDrag(this);
- drag->setMimeData(mimeData);
- drag->setPixmap(QPixmap(":/images/person.png"));
- if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
- delete item;
- }
- }
[color=rgb(0, 204, 255) !important]Copy code In the mousePressEvent() function, we detect the left mouse button click, and if it is, we record the current position. It should be noted that this function finally needs to call the system's own processing function in order to implement the usual operation. This needs to be noted in some functions that rewrite events, and we have repeatedly emphasized this point before. The mouseMoveEvent() function determines if the left button is held down while the mouse is moving (that is, the content in the if statement), then a manhattanLength() value is calculated. Literally translated, this is the "Manhattan length". First, let's take a look at what event.pos() - startPos is. In the mousePressEvent() function, we record the coordinates of the mouse being pressed as startPos, and event.pos() is the current coordinates of the mouse: one point minus another point is a displacement vector. The so-called Manhattan distance is the distance between two points (calculated according to the Pythagorean theorem), which is the length of this vector. Then continue to judge, if it is greater than QApplication::startDragDistance(), we will release the operation. Of course, the system default mouse drag function must be called in the end. The significance of this judgment is to prevent the user from dragging the mouse due to factors such as hand shaking. The user must drag the mouse for a certain distance before we think he wants to drag, and this distance is provided by QApplication::startDragDistance(), and this value is usually 4px. performDrag() starts the drag and drop process. Here, we create a QDrag object and use this as the parent. QDrag uses QMimeData to store data. For example, we use the QMimeData::setText() function to store a string as text/plain type data. QMimeData provides many functions for storing data such as URLs, colors, and other types. Use QDrag::setPixmap() to set the mouse style when dragging occurs. QDrag::exec() blocks the dragging operation until the user completes the operation or cancels it. It accepts different types of actions as parameters, and the return value is the actual action performed. These action types are generally Qt::CopyAction, Qt::MoveAction and Qt::LinkAction. The return value will have these actions, and there will also be a Qt::IgnoreAction to indicate that the user canceled the drag and drop. These actions depend on the types allowed by the drag source object, the types accepted by the destination object, and the keyboard keys pressed during the drag and drop. After the exec() call, Qt will release the drag and drop object when it is no longer needed. - void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)
- {
- ProjectListWidget *source =
- qobject_cast(event->source());
- if (source && source != this) {
- event->setDropAction(Qt::MoveAction);
- event->accept();
- }
- }
-
- void ProjectListWidget::dropEvent(QDropEvent *event)
- {
- ProjectListWidget *source =
- qobject_cast(event->source());
- if dragMoveEvent() is similar to dropEvent(). First, determine the source of the event. Since we are dragging two ProjectListWidgets, the source should be of ProjectListWidget type (of course, this source cannot be itself, so we still have to determine the source!= this). In dragMoveEvent() we check the object being dragged; in dropEvent() we check the object being dropped: these two are different.
|