Tuesday, January 28, 2014

Qt's Drag-and-Drop Architecture for Python and PyQt5
Pt. 10 Hacking QMimeData

Modifying QDrag is no use. What about QMimeData, which is key to the Delayed Encoding hack? I set up the following modified MIME data class.

class MaimData(QMimeData):
    def __init__(self):
        super().__init__()
    def retrieveData(self,mt,ty):
        print('MT retrieveData')
        return super().retrieveData(mt,ty)
    def formats(self):
        print('MT formats')
        return super().formats()

The results were very interesting (on Mac OS X 10.9, this is).

starting drag with actions: Copy Move Link
MT formats
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
drag enters at 0 20 kbd mods 0 buttons 1 offering actions: Copy Move Link
MT formats
target moved to <class '__main__.TargWidj'>
drag moving at 1 20
drag moving at 3 19
drag moving at 4 19
drag moving at 5 19
dropping at 5 19 actions: Move
 -- setting copy action!
MT formats
MT retrieveData
exec returns 1 default 2 target <class '__main__.TargWidj'> source <class '__main__.SorcWidj'>

There is an immediate call to formats() and then five (5!) successive calls to retrieveData(). These all take place the moment the drag begins, while the mouse has barely moved. This immediately shows why the Delayed Encoding hack fails on Mac OS: not only would the expensive data conversion not be delayed; it would be done multiple times!

The next call to formats happens when the dragEnterEvent() method of the target widget accesses event.mimeData().hasText(). Then both methods are called during drop event processing.

Well, this looks promising. What happens if the drag is dropped onto a different application?

starting drag with actions: Copy Move Link
MT formats
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
exec returns 0 default 2 target <class 'NoneType'> source <class '__main__.SorcWidj'>

OK, what happens if it is dropped on the desktop?

starting drag with actions: Copy Move Link
MT formats
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
exec returns 0 default 2 target <class 'NoneType'> source <class '__main__.SorcWidj'>

Oops. What about a drop that fails, releasing the mouse over an ineligible receiver?

starting drag with actions: Copy Move Link
MT formats
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
MT retrieveData
exec returns 0 default 2 target <class 'NoneType'> source <class '__main__.SorcWidj'>

Depressing: the MIME data methods are just never called after the start of the drag, except when the drag enters a target in the same app.

It may be this behavior is peculiar to Mac OS and the code would behave differently on Windows or Linux. Doesn't matter; Mac OS is one of two main targets for my app, and anyway I want to keep it platform independent.

For the moment my desire to detect the drag of a tab off the edge of its parent window—and for that matter, the complementary desire to detect the drag of a QDialog onto a QTabBar—appears to be out of reach. But stay tuned, something may turn up.

No comments: