Ralf Schmitt wrote:
- Hi, As I haven't found an example of using drag'n drop between multiple
wxTreeCtrl's, I'm posting a short example program, just in case that someone else will have the same problem... It also allows dnd between two programs, and dragging of files onto
wxTreeCtrl's. - Ralf
1 # Drag and Drop contributed by Sam Anderson, reposted by Dirk Krause
2
3 import cPickle as pickle
4 from xml.parsers import expat
5
6 import wx # Use the new namespace
7
8
9 class DropData(wx.CustomDataObject):
10 def __init__(self):
11 wx.CustomDataObject.__init__(self, wx.CustomDataFormat("MyDropData"))
12 self.setObject(None)
13
14 def setObject(self, obj):
15 self.SetData(pickle.dumps(obj))
16
17 def getObject(self):
18 return pickle.loads(self.GetData())
19
20
21 class MyDropTarget(wx.PyDropTarget):
22 def __init__(self, tree):
23 wx.PyDropTarget.__init__(self)
24 self._makeObjects()
25 self.tree = tree
26 self.selections=[]
27
28 def _makeObjects(self):
29 self.data = DropData()
30 self.fileObject = wx.FileDataObject()
31 comp = wx.DataObjectComposite()
32 comp.Add(self.data)
33 comp.Add(self.fileObject)
34 self.comp = comp
35 self.SetDataObject(comp)
36
37 def _saveSelection(self):
38 self.selections = self.tree.GetSelections()
39 self.tree.UnselectAll()
40
41 def _restoreSelection(self):
42 self.tree.UnselectAll()
43 for i in self.selections:
44 self.tree.SelectItem(i)
45 self.selections=[]
46
47
48 def OnEnter(self, x, y, d):
49 self._saveSelection()
50 return d
51
52 def OnLeave(self):
53 self._restoreSelection()
54
55 def OnDrop(self, x, y):
56 self._restoreSelection()
57 #item, flags = self.tree.HitTest((x, y))
58 print "got an drop event at", x, y
59 return True
60
61 def OnDragOver(self, x, y, d):
62 # provide visual feedback by selecting the item the mouse is over
63 item, flags = self.tree.HitTest((x,y))
64 selections = self.tree.GetSelections()
65 if item:
66 if selections != [item]:
67 self.tree.UnselectAll()
68 self.tree.SelectItem(item)
69 elif selections:
70 self.tree.UnselectAll()
71
72 # The value returned here tells the source what kind of visual
73 # feedback to give. For example, if wx.DragCopy is returned then
74 # only the copy cursor will be shown, even if the source allows
75 # moves. You can use the passed in (x,y) to determine what kind
76 # of feedback to give. In this case we return the suggested value
77 # which is based on whether the Ctrl key is pressed.
78 return d
79
80
81
82 # Called when OnDrop returns True. We need to get the data and
83 # do something with it.
84 def OnData(self, x, y, d):
85 if self.GetData():
86 filenames = self.fileObject.GetFilenames()
87 data = self.data.getObject()
88
89 print "====>",
90 if filenames:
91 print "files", filenames,
92
93 if data:
94 print "item", repr(data),
95
96 item, flags = self.tree.HitTest((x,y))
97 if item:
98 print "dropped on item:", self.tree.GetItemText(item)
99 else:
100 print "dropped nowhere"
101
102 self._makeObjects() # reset data objects..
103
104 return d # what is returned signals the source what to do
105 # with the original data (move, copy, etc.) In this
106 # case we just return the suggested value given to us.
107
108
109 # dummy xml file
110 xml='''<?xml version="1.0" encoding="UTF-8"?>
111 <Test title="Channels">
112 <Channels title="Favourites">
113 <Channel type="channel" title="Slashdot" uri="http://slashdot.org"/>
114 </Channels>
115 <Channels title="News">
116 <Channel title="CNN" uri="http://cnn.com"/>
117 <Channel title="BBC" uri="http://bbc.co.uk"/>
118 <Channel title="The Economist" uri="http://economist.com"/>
119 <Channel title="MSNB" uri="http://msnbc.com"/>
120 </Channels>
121 <Channels title="Sports">
122 <Channel type="channel" title="ESPN" uri="http://espn.com"/>
123 </Channels>
124 <Trash title="Trash"/>
125 </Test>'''
126
127 class TestApp(wx.App):
128 def OnInit(self):
129 # create navigation frame
130 frame = NavFrame(None, -1, 'wx.TreeCtrl DragNDrop Test')
131 for tree in frame.trees:
132 # expand a few nodes
133 # NOTE: I know this is pathetic but I couldn't figure out how
134 # to walk through and expand all nodes
135 root = tree.nodeStack[0]
136 child, cookie = tree.GetFirstChild(root)
137 nchild, cookie = tree.GetFirstChild(child)
138 tree.Expand(root)
139 tree.Expand(child)
140 tree.Expand(nchild)
141
142 # display navigation frame
143 frame.Show(True)
144 self.SetTopWindow(frame)
145 return True
146
147 class NavFrame(wx.Frame):
148 def __init__(self, parent, ID, title):
149 wx.Frame.__init__(self, parent, ID, title,
150 wx.DefaultPosition,
151 wx.Size(650,400))
152 self.ils = []
153 self.trees = []
154 self.newTree((20,20))
155 self.newTree((300,20))
156 # other events
157 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
158
159 def newTree(self, pos):
160 # give the tree a unique id
161 tID = wx.NewId()
162
163 # call the tree
164 tree = XMLTree(self, tID, pos, wx.Size(270,290), # wx.DefaultPosition, wx.Size(270,490),
165 wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE)# | wx.TR_EDIT_LABELS)
166 # Trees need an image list to do DnD...
167 tree.SetImageList(self.MakeImageList())
168
169 # Load XML into tree
170 tree.LoadXML()
171
172
173 md=MyDropTarget(tree)
174 tree.SetDropTarget(md)
175
176 tree.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
177 self.trees.append(tree)
178
179 def OnBeginDrag(self, event):
180 item = event.GetItem()
181 tree = event.GetEventObject()
182
183 if item != tree.GetRootItem(): # prevent dragging root item
184 def DoDragDrop():
185 txt = tree.GetItemText(item)
186 print "Starting drag'n'drop with %s..." % repr(txt)
187 dd = DropData()
188 dd.setObject(txt)
189
190 comp = wx.DataObjectComposite()
191 comp.Add(dd)
192 dropSource = wx.DropSource(self)
193 dropSource.SetData(comp)
194 result = dropSource.DoDragDrop(wx.Drag_AllowMove)
195 print "drag'n'drop finished with:", result, "\n"
196
197 wx.CallAfter(DoDragDrop) # can't call dropSource.DoDragDrop here..
198
199
200 def OnCloseWindow(self, event):
201 self.Destroy()
202
203 def MakeImageList(self):
204 il = wx.ImageList(16, 16)
205 il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, (16, 16)))
206 self.ils.append(il)
207 return il
208
209 class XMLTree(wx.TreeCtrl):
210 def __init__(self, parent, id, pos, size, style):
211 wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
212
213 # create nodeStack and add root node
214 self.nodeStack = [self.AddRoot("My InfoSpace")]
215
216 def StartElement(self, name, attrs ):
217 name = name.encode()
218
219 id = self.AppendItem(self.nodeStack[-1], name)
220
221 # for each element map xml attributes to an associated dictionary
222 self.SetPyData(id, attrs)
223
224 # set title of tree node based on element title attribute
225 if 'title' in attrs:
226 title = attrs['title']
227 self.SetItemText(id, str(title))
228
229 # add element to tree
230 self.nodeStack.append(id)
231
232 def EndElement(self, name ):
233 self.nodeStack = self.nodeStack[:-1]
234
235 def CharacterData(self, data ):
236 if data.strip():
237 data = ""
238 self.AppendItem(self.nodeStack[-1], data)
239
240 def LoadXML(self):
241 Parser = expat.ParserCreate()
242
243 Parser.StartElementHandler = self.StartElement
244 Parser.EndElementHandler = self.EndElement
245 Parser.CharacterDataHandler = self.CharacterData
246
247 ParserStatus = Parser.Parse(xml, 1)
248
249
250 if __name__ == '__main__':
251 app = TestApp(False)
252 app.MainLoop()