Drag and Drop🌶️

Drag and drop provides a simple visual mechanism which users can use to transfer information between and within widgets. Drag and drop is similar in function to the clipboard’s cut and paste mechanism.

https://github.com/user-attachments/assets/857fd144-7a2a-4173-80b3-d135e62b8235

This document describes the basic drag and drop mechanism and outlines the approach used to enable it in custom controls. Drag and drop operations are also supported by many of TermTk’s controls, such as TTkList or TTkTabWidget.

Drag and Drop Classes🌶️

These classes deal with drag and drop and the necessary mime type encoding and decoding.

TTkGui.TTkDrag(**kwargs)

Dragging🌶️

To start a drag, create a TTkDrag object, and call its TTkDrag.exec() function. In most applications, it is a good idea to begin a drag and drop operation only after a mouse button has been pressed and the cursor has been moved a certain distance. However, the simplest way to enable dragging from a widget is to reimplement the widget’s TTkWidget.mouseDragEvent() and start a drag and drop operation:

def mouseDragEvent(self, evt:ttk.TTkMouseEvent) -> bool:
    if evt.key == ttk.TTkMouseEvent.LeftButton:
        drag = ttk.TTkDrag()
        drag.setData("LeftClick Drag")
        drag.exec()
    return True

Note that the TTkDrag.exec() function does not block the main event loop.

Dropping🌶️

To be able to receive the content dropped on a widget, reimplement the TDragEvents.dropEvent() event handler functions.

def dropEvent(self, evt:ttk.TTkDnDEvent) -> bool:
    ttk.TTkLog.debug(f"Drop data: {evt.data()}, Position: {evt.pos()}")
    return True

Events🌶️

There are several events that can be used to customize the drag and drop operation:

def dragEnterEvent(self, evt:ttk.TTkDnDEvent) -> bool:
    ttk.TTkLog.debug(f"Drag Enter: {evt.data()}, Position: {evt.pos()}")
    return True

def dragLeaveEvent(self, evt:ttk.TTkDnDEvent) -> bool:
    ttk.TTkLog.debug(f"Drag Leave: {evt.data()}, Position: {evt.pos()}")
    return True

def dragMoveEvent(self, evt:ttk.TTkDnDEvent) -> bool:
    ttk.TTkLog.debug(f"Drag Move: {evt.data()}, Position: {evt.pos()}")
    return True

def dropEvent(self, evt:ttk.TTkDnDEvent) -> bool:
    ttk.TTkLog.debug(f"Drop: {evt.data()}, Position: {evt.pos()}")
    return True

Pixmap🌶️

The visual representation of the drag can be customized by setting a pixmap with TTkDrag.setPixmap(). By default the pixmap is initialized as a simple text string (“[…]”) but it can be customized by using a TTkWidget or TTkCanvas as a pixmap.

https://github.com/user-attachments/assets/7a23f5a9-444b-4e5a-878b-91c4b35ee8d8

You can use the same object as pixmap to have a visual feedback of the widget being dragged:

def mouseDragEvent(self, evt:ttk.TTkMouseEvent) -> bool:
    drag = ttk.TTkDrag()
    drag.setPixmap(self)
    drag.exec()
    return True

Or define another TTkWidget as pixmap:

def mouseDragEvent(self, evt:ttk.TTkMouseEvent) -> bool:
    button = ttk.TTkButton(text=f"DnD", border=True, size=(25,5))
    drag = ttk.TTkDrag()
    drag.setPixmap(button)
    drag.exec()
    return True

Or use a TTkCanvas as pixmap and draw the required content on it:

def mouseDragEvent(self, evt:ttk.TTkMouseEvent) -> bool:
    pixmap = ttk.TTkCanvas(width=17,height=5)

    pixmap.drawText(pos=(0,0),text="╭╼ TXT ╾────────╮")
    pixmap.drawText(pos=(0,1),text="│Lorem ipsum dol│")
    pixmap.drawText(pos=(0,2),text="│consectetur adi│")
    pixmap.drawText(pos=(0,3),text="│sed do eiusmod │")
    pixmap.drawText(pos=(0,4),text="╰────────╼ End ╾╯")

    drag = ttk.TTkDrag()
    drag.setPixmap(pixmap)
    drag.exec()
    return True

HotSpot🌶️

The hotspot is the offset of the pixmap related to the cursor position. It can be set using TTkDrag.setHotSpot(). It is useful when the pixmap is not centered on the cursor or when you want to define an offset to allow the object being dragged from the clicked position:

https://github.com/user-attachments/assets/8d999365-c787-4eff-84f2-03ef2b22c37a
def mouseDragEvent(self, evt:ttk. TTkMouseEvent) -> bool:
    drag = ttk.TTkDrag()
    drag.setHotSpot((evt.x, evt.y))
    drag.setPixmap(self)
    drag.exec()
    return True

Examples🌶️