Monday, January 27, 2014

Qt's Drag-and-Drop Architecture for Python and PyQt5
Pt 1, an Overview

In the following series of posts I will review the classes and methods needed to implement Drag-and-Drop functionality in a Qt5 program written in PyQt5.

The official documentation (for C++ of course) is found in this overview. It contains links to the reference pages for most of the the important classes, and it covers the basics for a C++ programmer. Doing the mental translation from C++ syntax to Python/PyQt syntax is a habit that the Python programmer needs to learn.

However, I found the official overview somewhat confusing. One problem is that it does not clearly distinguish the design of a drop target, a widget that receives dropped data, and the design of a drag source, a widget that recognizes a mouse drag motion and initiates a drag. These two are quite distinct. They are executed at different times and might be executed in different apps, with the drag beginning in one app and the drop ending in another. Thus you can have a drop without a drag and vice versa. They use different classes and require you to override different class methods. All told, they need separate treatment, which I will give them in this series of posts.

The User's View

The user thinks of drag-and-drop as a single smooth mouse operation: click down on something; move the mouse to something else; let go. During the drag the mouse cursor may change its appearance in some familiar way, perhaps acquiring a plus-sign to indicate a copy will happen or a slashed-circle to indicate that no dropping is allowed.

It is possible for the mouse to acquire a little thumbnail image of the thing being dragged, as a reminder. For example when dragging text in an editor, a translucent copy of the dragged text, or part of it, may follow the mouse cursor.

The user will often be dragging from one place in an application to another place in the same application: dragging a paragraph of text from one place in a document to another, for example; or dragging a list item to a different position in the same list.

But it may be that the user is dragging something from one application and dropping it into a completely different application: for example, dragging text from a Qt editor and dropping it on the host Desktop as a "clipping"; or dragging a URL from a browser window and dropping it into a Qt widget of some kind.

All in all, drag-and-drop is a simple, quick, familiar operation to the user—or should be. But making it happen at the level of program code turns out to be quite complicated.

The Program's View

To the program written in Qt (and specifically PyQt5: in these posts, "Qt" and "PyQt" are synonyms), it is actually not correct to speak of "drag-and-drop" as a single thing. There is drag, the initiating of a drag operation, and there is drop, the delivery of content to a target. These two use completely different classes and methods and are designed in isolation from each other.

Moreover, remember that the drag might start in a completely different application, so it arrives at your Qt code as an unheralded drop with data you didn't prepare. Or a drag that you initiate in your Qt code might end being dropped in some completely unrelated program.

The Qt drag-and-drop support is rather unhelpful in these cases of dragging between different applications. It only works fully as documented when the drag and drop are between widgets in the same application. I'll point out these issues as they come up.

In the next post we'll take a high-level look at drop target code.

No comments: