[wxPython] wxTreeCtrl, drag 'n drop and event.Allow()

Hi,

wxPython is great! But it looks like it doesn't allow drag'ndrop in
wxTreeCtrl. I attempt to handle EVT_TREE_BEGIN_DRAG but
event.IsAllowed() returns 0 and event does not seem to have the Allow()
function.

Run:
  C:\>python treectrldnd.py
       (see source below)

Then drag an item in the tree ctrl to produce this dump to stdout:

OnTreeBeginDrag: IsAllowed 0
event.Allow() threw exception: 'wxTreeEventPtr' instance has no
attribute 'Allow'
event object: <C wxTreeEvent instance at _63f224_wxTreeEvent_p>

(Running Python20, Windows98, wxPython222)

Is there a better way to do DnD than the way I am trying? Is this in
the TODO list?

TIA,

Jack.

##treectrldnd.py -- butchered version of CustomDragAndDrop.py
from wxPython.wx import *

···

#----------------------------------------------------------------------

class DropSourceTreeCtrlPanel(wxPanel):
    def __init__(self, parent, log):
        wxPanel.__init__(self, parent, -1, style=wxWANTS_CHARS)
        EVT_SIZE(self, self.OnSize)

        self.log = log
        tID = NewId()

        self.tree = wxTreeCtrl(self, tID, wxDefaultPosition,
wxDefaultSize,
                               wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS)

        self.root = self.tree.AddRoot("The Root Item")
        for x in range(5):
            child = self.tree.AppendItem(self.root, "Item %d" % x)
            for y in range(5):
                last = self.tree.AppendItem(child, "item %d-%s" % (x,
chr(ord("a")+y)))
                for z in range(5):
                    item = self.tree.AppendItem(last, "item %d-%s-%d" %
(x, chr(ord("a")+y), z))

        self.tree.Expand(self.root)
        EVT_TREE_BEGIN_DRAG(self.tree, tID, self.OnTreeBeginDrag)

    def OnSize(self, event):
        w,h = self.GetClientSizeTuple()
        self.tree.SetDimensions(0, 0, w, h)

    def OnTreeBeginDrag(self, event):
      self.log.WriteText("OnTreeBeginDrag: IsAllowed %s\n" %
`event.IsAllowed()`)
      try:
          event.Allow()
      except:
          print "event.Allow() threw exception: ", sys.exc_value
            print "event object:", event

        sel = self.tree.GetSelection()
        text = self.tree.GetItemText(sel)

        data = wxCustomDataObject(wxCustomDataFormat("x"))
        data.SetData(text)
        dropSource = wxDropSource(self)
        dropSource.SetData(data)
        result = dropSource.DoDragDrop()

#----------------------------------------------------------------------

class DropTarget(wxPyDropTarget):
    def __init__(self, window, log):
        wxPyDropTarget.__init__(self)
        self.log = log
        self.window = window

        # specify the type of data we will accept
        self.data = wxCustomDataObject(wxCustomDataFormat("x"))
        self.SetDataObject(self.data)

    def OnDrop(self, x, y):
        self.log.WriteText("OnData: %d, %d, %d\n" % (x, y, d))
        # copy the data from the drag source to out data object
        dc = wxClientDC(self.window)
        dc.BeginDrawing()
        dc.SetPen(wxPen(wxBLUE, 3))
        dc.DrawLine(x,y,x+20,y)
        dc.EndDrawing()
        return true

class DropTargetWindow(wxWindow):
    def __init__(self, parent, log):
        wxWindow.__init__(self, parent, -1, style=wxSUNKEN_BORDER)
        self.log = log
        self.SetBackgroundColour(wxWHITE)
        self.lines = []
        self.x = self.y = 0
        dt = DropTarget(self, log)
        self.SetDropTarget(dt)

#----------------------------------------------------------------------

class CustomDnDPanel(wxPanel):
    def __init__(self, parent, log):
        wxPanel.__init__(self, parent, -1)
        sizer = wxBoxSizer(wxHORIZONTAL)
        sizer.Add(DropSourceTreeCtrlPanel(self, log), 1, wxEXPAND|wxALL,
5)
        sizer.Add(DropTargetWindow(self, log), 1, wxEXPAND|wxALL, 5)
        self.SetAutoLayout(true)
        self.SetSizer(sizer)

#----------------------------------------------------------------------

if __name__ == '__main__':
    import sys
    class DummyLog:
        def WriteText(self, text):
            sys.stdout.write(text)

    class TestApp(wxApp):
        def OnInit(self):
            frame = wxFrame(None, -1, "Custom Drag and Drop",
size=(550,400))
            panel = CustomDnDPanel(frame, DummyLog())
            frame.Show(true)
            self.SetTopWindow(frame)
            return true

    app = TestApp(0)
    app.MainLoop()

#----------------------------------------------------------------------
_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

Hi,

In answer to my previous question:

wxPython is great! But it looks like it doesn't allow drag'ndrop in
wxTreeCtrl. I attempt to handle EVT_TREE_BEGIN_DRAG but
event.IsAllowed() returns 0 and event does not seem to have the Allow()
function.

I've tried to handle drag 'n drop from a tree ctrl without using
EVT_TREE_BEGIN_DRAG -- the source code below does a pretty good job.
But I can't (for the life of me) work out how to stop the cursor
changing to the 'banned' cursor (on MSWindows--the cursor that is a
circle with a line diagonally through it).

I've tried SetCursor() in a number of spots -- maybe I've just not tried
the right place... I'm pretty new to this so I've tried www searches for
'wxWindows' and 'wxTreeCtrl' and 'drag', and there's not much out there
:frowning:

the source:

from wxPython.wx import *

···

#----------------------------------------------------------------------
class FeedBackDropSource(wxDropSource):
    def __init__(self, win, dropTarget, log):
        wxDropSource.__init__(self, win)
        self.dropTarget = dropTarget
        self.log = log
        self.fedback = 0
        self.win = win
        
    def GiveFeedback(self, effect):
        if not self.fedback:
            self.log.WriteText("GiveFeedback:\n")
            self.dropTarget.HighlightDropZone()
            self.fedback = 1
        return true
      
class DropSourceTreeCtrl(wxTreeCtrl):
    def __init__(self, win, tID, dropTarget, log):
        wxTreeCtrl.__init__(self, win, tID, wxDefaultPosition,
wxDefaultSize,wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS)
        self.log = log
        self.root = self.AddRoot("The root item")
        for x in range(3):
            child = self.AppendItem(self.root, "Item %d" % x)
            for y in range(5):
                last = self.AppendItem(child, "item %d-%s" % (x,
chr(ord("a")+y)))
                for z in range(5):
                    item = self.AppendItem(last, "item %d-%s-%d" % (x,
chr(ord("a")+y), z))
        self.Expand(self.root)

        self.handling_drag=0
        self.dropSource = None
        self.dropTarget = dropTarget

        EVT_LEFT_DOWN(self, self.OnLeftDown)
        EVT_MOTION(self, self.OnMotion)
        
    def OnLeftDown(self, event):
        self.log.WriteText("OnLeftDown:\n")
        self.itemOnLastLeftDown=None
        (self.itemOnLastLeftDown, flags) =
self.HitTest(event.GetPosition())
        event.Skip()

    def OnMotion(self, event):
        if event.Dragging():
            if self.dropSource == None:
                self.log.WriteText("OnMotionDragging:\n")
                if self.itemOnLastLeftDown:
                    self.log.WriteText("OnMotionDraggin: %s\n" %
self.GetItemText(self.itemOnLastLeftDown))
                    self.CreateDropSource()
        else: # we don't handle anything other than a drag
            event.Skip()
            
    def CreateDropSource(self):
        text = self.GetItemText(self.itemOnLastLeftDown)
        data = wxTextDataObject()
        data.SetText(text)
        self.dropSource = FeedBackDropSource(self, self.dropTarget,
self.log)
        self.dropSource.SetData(data)
        result = self.dropSource.DoDragDrop(1)
        self.dropSource = None

class DropSourceTreeCtrlPanel(wxPanel):
    def __init__(self, parent, target, log):
        wxPanel.__init__(self, parent, -1, style=wxWANTS_CHARS)
        tID = NewId()
        self.tree = DropSourceTreeCtrl(self, tID, target, log)
        EVT_SIZE(self, self.OnSize)
        EVT_MOTION(self, self.OnMotion)

    def OnMotion(self, event):
        if not event.Dragging():
            event.Skip()
    def OnSize(self, event):
        w,h = self.GetClientSizeTuple()
        self.tree.SetDimensions(0, 0, w, h)

#----------------------------------------------------------------------

class DropTarget(wxPyDropTarget):
    def __init__(self, window, log):
        wxPyDropTarget.__init__(self)
        self.log = log
        self.window = window
        self.data = wxTextDataObject()
        self.SetDataObject(self.data)

    def OnEnter(self,x,y,d):
        self.log.WriteText("OnEnter\n")
        return wxDragCopy
    def OnLeave(self):
        self.log.WriteText("OnLeave\n")
    def OnDrop(self, x, y):
        self.log.WriteText("OnDrop: %d %d\n" % (x, y))
        self.window.HighlightDropZone(0)
        return true
    def OnDragOver(self, x, y, d):
        self.log.WriteText("OnDragOver: %d, %d, %d\n" % (x, y, d))
        return wxDragCopy
    def OnData(self, x, y, d):
        self.log.WriteText("OnData: %d, %d, %d\n" % (x, y, d))
        # copy the data from the drag source to out data object
        text = None
        if self.GetData():
            self.window.SetDroppedData(self.data.GetText(),x,y)
        return d

class DropTargetWindow(wxWindow):
    def __init__(self, parent, log):
        wxWindow.__init__(self, parent, -1, style=wxSUNKEN_BORDER)
        self.log = log
        self.SetBackgroundColour(wxWHITE)
        self.lines =
        self.x = self.y = 0
        dt = DropTarget(self, log)
        self.SetDropTarget(dt)
        self.highlightDropZone = 0
        self.dropped = 0
        
        EVT_PAINT(self, self.OnPaint)

    def HighlightDropZone(self, highlight = 1):
        self.highlightDropZone = highlight
        self.Refresh()

    def DrawDropZone(self, dc):
        dc.Clear()
        dc.SetTextForeground(wxRED)
        dc.DrawText("Drop me here!",40,60)
        dc.DrawText("ignore 'banned' cursor",40,80)

    def DrawInfo(self, dc):
        dc.Clear()
        dc.SetTextForeground(wxBLUE)
        dc.DrawText("Drag an item from",40,40)
        (width,height) = dc.GetTextExtent("Drag an item from")
        dc.DrawText("the tree control",40,40+height+1)
        if self.dropped:
            if self.text:
                dc.SetTextForeground(wxBLACK)
                dc.DrawText(self.text,self.x,self.y)
            else:
                dc.SetPen(wxPen(wxBLUE, 3))
                dc.DrawLine(self.x,self.y,self.x+20,self.y)
            
    def SetDroppedData(self,text,x,y):
        self.dropped = 1
        self.x = x
        self.y = y
        self.text = text

    def OnPaint(self, event):
        self.log.WriteText("OnPaint\n")
        dc = wxPaintDC(self)
        dc.BeginDrawing()
        if self.highlightDropZone:
            self.DrawDropZone(dc)
        else:
            self.DrawInfo(dc)
        dc.EndDrawing()

#----------------------------------------------------------------------

class TreeDnDPanel(wxPanel):
    def __init__(self, parent, log):
        wxPanel.__init__(self, parent, -1)
        sizer = wxBoxSizer(wxHORIZONTAL)
        target = DropTargetWindow(self, log)
        source = DropSourceTreeCtrlPanel(self, target, log)
        sizer.Add(source, 1, wxEXPAND|wxALL, 5)
        sizer.Add(target, 1, wxEXPAND|wxALL, 5)
        self.SetAutoLayout(true)
        self.SetSizer(sizer)

#----------------------------------------------------------------------

if __name__ == '__main__':
    import sys
    class DummyLog:
        def WriteText(self, text):
            sys.stdout.write(text)

    class TreeDnDApp(wxApp):
        def OnInit(self):
            frame = wxFrame(None, -1, "Custom Drag and Drop",
size=(550,400))
            panel = TreeDnDPanel(frame, DummyLog())
            frame.Show(true)
            self.SetTopWindow(frame)
            return true

    app = TreeDnDApp(0)
    app.MainLoop()

#----------------------------------------------------------------------
_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users

In answer to my previous question:

> wxPython is great! But it looks like it doesn't allow drag'ndrop in
> wxTreeCtrl. I attempt to handle EVT_TREE_BEGIN_DRAG but
> event.IsAllowed() returns 0 and event does not seem to have the Allow()
> function.

I've added the wxNotifyEvent.Allow() method, and it will be in CVS soon.
Apparently it snuck in while I wasn't looking...

After playing with it a bit, and doing some digging in the code, I don't
think EVT_TREE_BEGIN_DRAG is what you are looking for anyway. It actually
uses a wxDragImage internally and seems to be useful for dragging things
within the tree, but not to other windows.

I've tried to handle drag 'n drop from a tree ctrl without using
EVT_TREE_BEGIN_DRAG -- the source code below does a pretty good job.
But I can't (for the life of me) work out how to stop the cursor
changing to the 'banned' cursor (on MSWindows--the cursor that is a
circle with a line diagonally through it).

Return false from your GiveFeedback method to get default feedback behavior,
or just use the standard wxDropSource instead of your custom one.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com
http://wxPython.org Java give you jitters?
http://wxPROs.com Relax with wxPython!

_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/mailman/listinfo/wxpython-users