Introduction

What Objects are Involved

Process Overview

wxDataObjects

Code Samples

   1 # A very simple Drag and Drop Example
   2 # provided with no warranty whatsoever for any purpose, ever
   3 
   4 # A very simple Drag and Drop Example
   5 # provided with no warranty whatsoever for any purpose, ever
   6 
   7 # This code creates a Text Control from which Text can be dragged,
   8 # a Text Control to which Text can be dragged (from the first Text Control or from other applications),
   9 # and a Text Control to which Files can be dragged from outside this application.
  10 # While the later two windows can receive data from outside the application, the first window
  11 # does not appear to be able to provide text to other applications.  Please feel free to fix
  12 # this if you know how, as I think that would be more useful as an example.
  13 
  14 # It is designed to demonstrate the fundamentals of very simple drag-and-drop operations.
  15 
  16 """ This mini-app is designed to demonstrate simple Drag and Drop functioning in wx.Python """
  17 
  18 __author__ = 'David Woods, Wisconsin Center for Education Research <dwoods@wcer.wisc.edu>'
  19 
  20 # Import wx.Python
  21 import wx
  22 
  23 # Declare GUI Constants
  24 MENU_FILE_EXIT = wx.NewId()
  25 DRAG_SOURCE    = wx.NewId()
  26 
  27 # Define Text Drop Target class
  28 class TextDropTarget(wx.TextDropTarget):
  29    """ This object implements Drop Target functionality for Text """
  30    def __init__(self, obj):
  31       """ Initialize the Drop Target, passing in the Object Reference to
  32           indicate what should receive the dropped text """
  33       # Initialize the wx.TextDropTarget Object
  34       wx.TextDropTarget.__init__(self)
  35       # Store the Object Reference for dropped text
  36       self.obj = obj
  37 
  38    def OnDropText(self, x, y, data):
  39       """ Implement Text Drop """
  40       # When text is dropped, write it into the object specified
  41       self.obj.WriteText(data + '\n\n')
  42 
  43 # Define File Drop Target class
  44 class FileDropTarget(wx.FileDropTarget):
  45    """ This object implements Drop Target functionality for Files """
  46    def __init__(self, obj):
  47       """ Initialize the Drop Target, passing in the Object Reference to
  48           indicate what should receive the dropped files """
  49       # Initialize the wxFileDropTarget Object
  50       wx.FileDropTarget.__init__(self)
  51       # Store the Object Reference for dropped files
  52       self.obj = obj
  53 
  54    def OnDropFiles(self, x, y, filenames):
  55       """ Implement File Drop """
  56       # For Demo purposes, this function appends a list of the files dropped at the end of the widget's text
  57       # Move Insertion Point to the end of the widget's text
  58       self.obj.SetInsertionPointEnd()
  59       # append a list of the file names dropped
  60       self.obj.WriteText("%d file(s) dropped at %d, %d:\n" % (len(filenames), x, y))
  61       for file in filenames:
  62          self.obj.WriteText(file + '\n')
  63       self.obj.WriteText('\n')
  64 
  65 
  66 
  67 class MainWindow(wx.Frame):
  68    """ This window displays the GUI Widgets. """
  69    def __init__(self,parent,id,title):
  70        wx.Frame.__init__(self,parent, wx.ID_ANY, title, size = (800,600), style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)
  71        self.SetBackgroundColour(wx.WHITE)
  72 
  73        # Menu Bar
  74        # Create a MenuBar
  75        menuBar = wx.MenuBar()
  76        # Build a Menu Object to go into the Menu Bar
  77        menu1 = wx.Menu()
  78        menu1.Append(MENU_FILE_EXIT, "E&xit", "Quit Application")
  79        # Place the Menu Item in the Menu Bar
  80        menuBar.Append(menu1, "&File")
  81        # Place the Menu Bar on the ap
  82        self.SetMenuBar(menuBar)
  83        #Define Events for the Menu Items
  84        wx.EVT_MENU(self, MENU_FILE_EXIT, self.CloseWindow)
  85 
  86        # GUI Widgets
  87        # Define a Text Control from which Text can be dragged for dropping
  88        # Label the control
  89        wx.StaticText(self, -1, "Text Drag Source  (left-click to select, right-click to drag)", (10, 1))
  90        # Create a Text Control
  91        self.text = wx.TextCtrl(self, DRAG_SOURCE, "", pos=(10,15), size=(350,500), style = wx.TE_MULTILINE|wx.HSCROLL)
  92        # Make this control a Text Drop Target
  93        # Create a Text Drop Target object
  94        dt1 = TextDropTarget(self.text)
  95        # Link the Drop Target Object to the Text Control
  96        self.text.SetDropTarget(dt1)
  97        # Put some text in the control as a starting place to have something to copy
  98        for x in range(20):
  99           self.text.WriteText("This is line %d of some text to drag.\n" % x)
 100        # Define Right-Click as start of Drag
 101        wx.EVT_RIGHT_DOWN(self.text, self.OnDragInit)
 102 
 103        # Define a Text Control to recieve Dropped Text
 104        # Label the control
 105        wx.StaticText(self, -1, "Text Drop Target", (370, 1))
 106        # Create a read-only Text Control
 107        self.text2 = wx.TextCtrl(self, -1, "", pos=(370,15), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)
 108        # Make this control a Text Drop Target
 109        # Create a Text Drop Target object
 110        dt2 = TextDropTarget(self.text2)
 111        # Link the Drop Target Object to the Text Control
 112        self.text2.SetDropTarget(dt2)
 113 
 114        # Define a Text Control to receive Dropped Files
 115        # Label the control
 116        wx.StaticText(self, -1, "File Drop Target (from off application only)", (370, 261))
 117        # Create a read-only Text Control
 118        self.text3 = wx.TextCtrl(self, -1, "", pos=(370,275), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)
 119        # Make this control a File Drop Target
 120        # Create a File Drop Target object
 121        dt3 = FileDropTarget(self.text3)
 122        # Link the Drop Target Object to the Text Control
 123        self.text3.SetDropTarget(dt3)
 124 
 125        # Display the Window
 126        self.Show(True)
 127 
 128 
 129    def CloseWindow(self, event):
 130        """ Close the Window """
 131        self.Close()
 132 
 133    def OnDragInit(self, event):
 134        """ Begin a Drag Operation """
 135        # Create a Text Data Object, which holds the text that is to be dragged
 136        tdo = wx.PyTextDataObject(self.text.GetStringSelection())
 137        # Create a Drop Source Object, which enables the Drag operation
 138        tds = wx.DropSource(self.text)
 139        # Associate the Data to be dragged with the Drop Source Object
 140        tds.SetData(tdo)
 141        # Initiate the Drag Operation
 142        tds.DoDragDrop(True)
 143 
 144 
 145 
 146 class MyApp(wx.App):
 147    """ Define the Drag and Drop Example Application """
 148    def OnInit(self):
 149       """ Initialize the Application """
 150       # Declare the Main Application Window
 151       frame = MainWindow(None, -1, "Drag and Drop Example")
 152       # Show the Application as the top window
 153       self.SetTopWindow(frame)
 154       return True
 155 
 156 
 157 # Declare the Application and start the Main Loop
 158 app = MyApp(0)
 159 app.MainLoop()

- David K. Woods, Wisconsin Center for Education Research, University of Wisconsin, Madison

(If you like this example, there's another, more complex example of Drag and Drop behavior in the TreeControls section.)

wxDataObjectComposite

This class is used when you wish to be able to deal with multiple data formats. The sample code should demonstrate this I've left out any processing, and just show you how to "get" the required data.

   1 class YourDropTarget(wx.PyDropTarget):
   2     """Implements drop target functionality to receive files, bitmaps and text"""
   3     def __init__(self):
   4         wx.PyDropTarget.__init__(self)
   5         self.do = wx.DataObjectComposite()  # the dataobject that gets filled with the appropriate data
   6         self.filedo = wx.FileDataObject()
   7         self.textdo = wx.TextDataObject()
   8         self.bmpdo = wx.BitmapDataObject()
   9         self.do.Add(self.filedo)
  10         self.do.Add(self.bmpdo)
  11         self.do.Add(self.textdo)
  12         self.SetDataObject(self.do)
  13 
  14 
  15     def OnData(self, x, y, d):
  16         """
  17         Handles drag/dropping files/text or a bitmap
  18         """
  19         if self.GetData():
  20             df = self.do.GetReceivedFormat().GetType()
  21 
  22             if df in [wx.DF_UNICODETEXT, wx.DF_TEXT]:
  23 
  24                 text = self.textdo.GetText()
  25 
  26             elif df == wx.DF_FILENAME:
  27                 for name in self.filedo.GetFilenames():
  28                     print name
  29 
  30             elif df == wx.DF_BITMAP:
  31                 bmp = self.bmpdo.GetBitmap()
  32 
  33         return d  # you must return this

Accepting Thunderbird drag and drops

Here's an example of how to use wx.DataObjectComposite to accept Thunderbird drag and drops as well as text and filenames.

   1 import wx
   2 
   3 
   4 class DropTarget(wx.DropTarget):
   5     def __init__(self, textCtrl, *args, **kwargs):
   6         super(DropTarget, self).__init__(*args, **kwargs)
   7         self.textCtrl = textCtrl
   8         self.composite = wx.DataObjectComposite()
   9         self.textDropData = wx.TextDataObject()
  10         self.fileDropData = wx.FileDataObject()
  11         self.thunderbirdDropData = wx.CustomDataObject('text/x-moz-message')
  12         self.composite.Add(self.thunderbirdDropData)
  13         self.composite.Add(self.textDropData)
  14         self.composite.Add(self.fileDropData)
  15         self.SetDataObject(self.composite)
  16 
  17     def OnDrop(self, x, y):
  18         return True
  19 
  20     def OnData(self, x, y, result):
  21         self.GetData()
  22         formatType, formatId = self.GetReceivedFormatAndId()
  23         if formatId == 'text/x-moz-message':
  24             return self.OnThunderbirdDrop()
  25         elif formatType in (wx.DF_TEXT, wx.DF_UNICODETEXT):
  26             return self.OnTextDrop()
  27         elif formatType == wx.DF_FILENAME:
  28             return self.OnFileDrop()
  29 
  30     def GetReceivedFormatAndId(self):
  31         format = self.composite.GetReceivedFormat()
  32         formatType = format.GetType()
  33         try:
  34             formatId = format.GetId() # May throw exception on unknown formats
  35         except:
  36             formatId = None
  37         return formatType, formatId
  38 
  39     def OnThunderbirdDrop(self):
  40         self.textCtrl.AppendText(self.thunderbirdDropData.GetData().decode('utf-16'))
  41         self.textCtrl.AppendText('\n')
  42         return wx.DragCopy
  43 
  44     def OnTextDrop(self):
  45         self.textCtrl.AppendText(self.textDropData.GetText() + '\n')
  46         return wx.DragCopy
  47 
  48     def OnFileDrop(self):
  49         for filename in self.fileDropData.GetFilenames():
  50             self.textCtrl.AppendText(filename + '\n')
  51         return wx.DragCopy
  52 
  53 
  54 class Frame(wx.Frame):
  55     def __init__(self, *args, **kwargs):
  56         super(Frame, self).__init__(*args, **kwargs)
  57         textCtrl = wx.TextCtrl(self, style=wx.TE_MULTILINE)
  58         textCtrl.SetDropTarget(DropTarget(textCtrl))
  59 
  60 
  61 app = wx.App(False)
  62 frame = Frame(None)
  63 frame.Show()
  64 app.MainLoop()

Comments...

I will add a example for dragging items in a wxTreeCtrl and what is more interesting how to drag a branch in a wxTreeCtrl. if added it will be found under ListAndTreeControls.

--René Freund


Files-and-Folders drop target Classes

The following are reusable classes based on wx.TextCtrl and wx.ListCtrl, each which receive file and folder names dropped from the filesystem GUI. These classes uses the file drag-and-drop technique shown above. When each drop is made to any of these controls, a "drop data" dictionary is created for each drop event that holds the dropFile data processed into more directly useful forms, i.e., the common parent path, a list of basenames and leaf folders, etc.

To be able to get and process the dropped files' data dictionary without altering the base classes a callback-function reference is passed in when instantiating any of the classes. The drop handler simply calls the main app's callback function with the drop-data dictionary as the only call parameter. This way the app can define a custom callback function that can process the file and folder data uniquely to each instantiated control. Since the drop controls are classes, multiple controls based on these classes may be individual drop targets in the same app having completely separate file/folder drop processing.For instance (no pun intended), Both the multi-files drop control and the multi-folder/directory controls are both derived from the [ FilesDirsDropCtrl.py ] class. One simply filters the drop dictionary for only files and links while the other accepts only folders.

When adding files and folders, duplicate filenames and folders are ignored. Each control displays a simple message in the form of a pseudo (false) row data item. On the first drop this message is replaced by row(s) of true file and folder path info. The multi-line controls have the additional capability in which the user can delete line items using a context (right-click) menu. Deleting all the listed dropped files causes the help message to be redisplayed.More context menu items can easily be added, such as "delete all row data", "save row data to file", "add file contents to row data", etc.

When files and folders are dropped on the multi-line controls, the first column widths undergo true autosizing of both the row data entries and the column headers (whichever is wider) which, unfortunately, is lacking in tha basic wx.ListCtrl column autosizing method.

FilesDirsDrop_Demo.zip

FilesDirsDrop_Demo.png

Ray Pasco 2012-06-05


DragAndDrop (last edited 2012-06-05 19:58:29 by pool-71-175-100-238)