theodox / mGui

Python module for cleaner maya GUI layout syntax
MIT License
123 stars 23 forks source link

Feature Idea: Outside drag and drop #76

Closed theodox closed 7 years ago

theodox commented 7 years ago

We currently support Maya's limited internal drag-and-drop callbacks. however these don't work for drags from outside of Maya -- you can't for example drop a folder from Explorer onto a button and get a callback to fire.

I'm ambivalent about supporting this, because it's another QT-only feature. how complicated would it be to implement, assuming we decided to do it?

bob-white commented 7 years ago

So it looks like to enable drag/drop on the Qt side requires a few things.

  1. QWidget.setAcceptsDrops(True) has to be called.
  2. QWidget.dragEnterEvent method needs to be overloaded, and the event needs to be accepted.
  3. QWidget.dropEvent method needs to be overloaded, and the drop event needs to be processed.

Which for us means that we can't really patch this into existing mGui objects, instead we'd have to do something like my DialogWindow / ModalDialogWindow classes where we start with a subclassed QWidget setup the drag drop behavior, and then wrap it in a compatible mGui interface.

Adding support to the DialogWindow family turned out to be rather simple. It lets something like this work:

import sys
for mod in sys.modules.keys()[:]:
    if 'mGui' in mod:
        del sys.modules[mod]

from mGui import gui, forms
from mGui.qt.QDialog import ModalDialogWindow

with ModalDialogWindow() as win:
    with forms.FooterForm() as base:
        with forms.FillForm() as main:
            text = gui.ScrollField(editable=True, wordWrap=True)

        with forms.HorizontalStretchForm() as footer:
            okay = gui.Button(label='Okay')
            cancel = gui.Button(label='Cancel')

            okay.command += win.accept
            cancel.command += win.reject

def _accepted(*args, **kwargs):
    sender = kwargs['sender']
    print(sender.base.main.text.text)

def _rejected(*args, **kwargs):
    print('Rejected!')

def _file_dropped(file_path, *args, **kwargs):
    sender = kwargs['sender']
    sender.base.main.text.text += file_path + '\n'

def _folder_dropped(folder_path, *args, **kwargs):
    sender = kwargs['sender']
    sender.base.main.text.text += folder_path + '\n'

win.accepted += _accepted
win.rejected += _rejected
win.fileDropped += _file_dropped
win.folderDropped += _folder_dropped

win.show()

Basically any file / folder path dragged onto the dialog ends up as a line in the ScrollField.

Would be a bit more challenging if we want to isolate the drag / drop event to a specific control, instead of the whole dialog.

theodox commented 7 years ago

Ah, sounds too complex -- better nothing than a half measure I guess