]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/CustomDragAndDrop.py
pop the menu up on mouse down, not up (this is more common behaviour in Windows and...
[wxWidgets.git] / wxPython / demo / CustomDragAndDrop.py
CommitLineData
8fa876ca
RD
1
2import cPickle
3import wx
b1462dfa
RD
4
5#----------------------------------------------------------------------
6
7
8fa876ca 8class DoodlePad(wx.Window):
b1462dfa 9 def __init__(self, parent, log):
8fa876ca 10 wx.Window.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
b1462dfa 11 self.log = log
8fa876ca 12 self.SetBackgroundColour(wx.WHITE)
b1462dfa
RD
13 self.lines = []
14 self.x = self.y = 0
163f2606 15 self.SetMode("Draw")
b1462dfa 16
8fa876ca
RD
17 wx.EVT_LEFT_DOWN(self, self.OnLeftDown)
18 wx.EVT_LEFT_UP(self, self.OnLeftUp)
19 wx.EVT_RIGHT_UP(self, self.OnRightUp)
20 wx.EVT_MOTION(self, self.OnMotion)
21 wx.EVT_PAINT(self, self.OnPaint)
b1462dfa
RD
22
23
163f2606
RD
24 def SetMode(self, mode):
25 self.mode = mode
26 if self.mode == "Draw":
8fa876ca 27 self.SetCursor(wx.StockCursor(wx.CURSOR_PENCIL))
163f2606 28 else:
8fa876ca 29 self.SetCursor(wx.STANDARD_CURSOR)
163f2606
RD
30
31
b1462dfa 32 def OnPaint(self, event):
8fa876ca 33 dc = wx.PaintDC(self)
b1462dfa
RD
34 self.DrawSavedLines(dc)
35
36 def DrawSavedLines(self, dc):
37 dc.BeginDrawing()
8fa876ca 38 dc.SetPen(wx.Pen(wx.BLUE, 3))
b1462dfa
RD
39 for line in self.lines:
40 for coords in line:
0dfb2393 41 dc.DrawLine(*coords)
b1462dfa
RD
42 dc.EndDrawing()
43
44
45 def OnLeftDown(self, event):
163f2606 46 if self.mode == "Drag":
b1462dfa 47 self.StartDragOpperation()
163f2606 48 elif self.mode == "Draw":
b1462dfa
RD
49 self.curLine = []
50 self.x, self.y = event.GetPositionTuple()
51 self.CaptureMouse()
163f2606 52 else:
8fa876ca 53 wx.Bell()
163f2606 54 self.log.write("unknown mode!\n")
b1462dfa
RD
55
56
57 def OnLeftUp(self, event):
f78241f6
RD
58 if self.HasCapture():
59 self.lines.append(self.curLine)
60 self.curLine = []
61 self.ReleaseMouse()
b1462dfa
RD
62
63 def OnRightUp(self, event):
64 self.lines = []
65 self.Refresh()
66
67 def OnMotion(self, event):
f78241f6 68 if self.HasCapture() and event.Dragging() and not self.mode == "Drag":
8fa876ca 69 dc = wx.ClientDC(self)
b1462dfa 70 dc.BeginDrawing()
8fa876ca 71 dc.SetPen(wx.Pen(wx.BLUE, 3))
d7403ad2 72 coords = (self.x, self.y) + event.GetPositionTuple()
b1462dfa 73 self.curLine.append(coords)
0dfb2393 74 dc.DrawLine(*coords)
b1462dfa
RD
75 self.x, self.y = event.GetPositionTuple()
76 dc.EndDrawing()
77
78
79 def StartDragOpperation(self):
80 # pickle the lines list
81 linesdata = cPickle.dumps(self.lines, 1)
82
83 # create our own data format and use it in a
84 # custom data object
8fa876ca 85 ldata = wx.CustomDataObject(wx.CustomDataFormat("DoodleLines"))
b1462dfa
RD
86 ldata.SetData(linesdata)
87
88 # Also create a Bitmap version of the drawing
89 size = self.GetSize()
8fa876ca
RD
90 bmp = wx.EmptyBitmap(size.width, size.height)
91 dc = wx.MemoryDC()
b1462dfa 92 dc.SelectObject(bmp)
8fa876ca 93 dc.SetBackground(wx.WHITE_BRUSH)
b1462dfa
RD
94 dc.Clear()
95 self.DrawSavedLines(dc)
8fa876ca 96 dc.SelectObject(wx.NullBitmap)
b1462dfa
RD
97
98 # Now make a data object for the bitmap and also a composite
99 # data object holding both of the others.
8fa876ca
RD
100 bdata = wx.BitmapDataObject(bmp)
101 data = wx.DataObjectComposite()
b1462dfa
RD
102 data.Add(ldata)
103 data.Add(bdata)
104
105 # And finally, create the drop source and begin the drag
106 # and drop opperation
8fa876ca 107 dropSource = wx.DropSource(self)
b1462dfa
RD
108 dropSource.SetData(data)
109 self.log.WriteText("Begining DragDrop\n")
8fa876ca 110 result = dropSource.DoDragDrop(wx.Drag_AllowMove)
b1462dfa 111 self.log.WriteText("DragDrop completed: %d\n" % result)
8fa876ca
RD
112
113 if result == wx.DragMove:
163f2606
RD
114 self.lines = []
115 self.Refresh()
f6bcfd97
BP
116
117
b1462dfa
RD
118#----------------------------------------------------------------------
119
120
8fa876ca 121class DoodleDropTarget(wx.PyDropTarget):
b1462dfa 122 def __init__(self, window, log):
8fa876ca 123 wx.PyDropTarget.__init__(self)
b1462dfa
RD
124 self.log = log
125 self.dv = window
f6bcfd97
BP
126
127 # specify the type of data we will accept
8fa876ca
RD
128 self.df = wx.CustomDataFormat("DoodleLines")
129 self.data = wx.CustomDataObject(self.df)
b1462dfa
RD
130 self.SetDataObject(self.data)
131
f6bcfd97
BP
132
133 # some virtual methods that track the progress of the drag
b1462dfa
RD
134 def OnEnter(self, x, y, d):
135 self.log.WriteText("OnEnter: %d, %d, %d\n" % (x, y, d))
163f2606
RD
136 return d
137
b1462dfa
RD
138 def OnLeave(self):
139 self.log.WriteText("OnLeave\n")
163f2606 140
b1462dfa
RD
141 def OnDrop(self, x, y):
142 self.log.WriteText("OnDrop: %d %d\n" % (x, y))
1e4a197e 143 return True
163f2606
RD
144
145 def OnDragOver(self, x, y, d):
146 #self.log.WriteText("OnDragOver: %d, %d, %d\n" % (x, y, d))
147
148 # The value returned here tells the source what kind of visual
149 # feedback to give. For example, if wxDragCopy is returned then
150 # only the copy cursor will be shown, even if the source allows
151 # moves. You can use the passed in (x,y) to determine what kind
152 # of feedback to give. In this case we return the suggested value
153 # which is based on whether the Ctrl key is pressed.
154 return d
155
b1462dfa 156
f6bcfd97 157
1e4a197e 158 # Called when OnDrop returns True. We need to get the data and
f6bcfd97 159 # do something with it.
b1462dfa
RD
160 def OnData(self, x, y, d):
161 self.log.WriteText("OnData: %d, %d, %d\n" % (x, y, d))
f6bcfd97 162
163f2606 163 # copy the data from the drag source to our data object
b1462dfa 164 if self.GetData():
f6bcfd97 165 # convert it back to a list of lines and give it to the viewer
b1462dfa 166 linesdata = self.data.GetData()
0dfb2393 167 lines = cPickle.loads(linesdata)
b1462dfa 168 self.dv.SetLines(lines)
8fa876ca
RD
169
170 # what is returned signals the source what to do
171 # with the original data (move, copy, etc.) In this
172 # case we just return the suggested value given to us.
173 return d
174
b1462dfa
RD
175
176
8fa876ca 177class DoodleViewer(wx.Window):
b1462dfa 178 def __init__(self, parent, log):
8fa876ca 179 wx.Window.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
b1462dfa 180 self.log = log
8fa876ca 181 self.SetBackgroundColour(wx.WHITE)
b1462dfa
RD
182 self.lines = []
183 self.x = self.y = 0
184 dt = DoodleDropTarget(self, log)
185 self.SetDropTarget(dt)
8fa876ca 186 wx.EVT_PAINT(self, self.OnPaint)
b1462dfa 187
163f2606 188
b1462dfa
RD
189 def SetLines(self, lines):
190 self.lines = lines
191 self.Refresh()
192
193 def OnPaint(self, event):
8fa876ca 194 dc = wx.PaintDC(self)
b1462dfa
RD
195 self.DrawSavedLines(dc)
196
197 def DrawSavedLines(self, dc):
198 dc.BeginDrawing()
8fa876ca
RD
199 dc.SetPen(wx.Pen(wx.RED, 3))
200
b1462dfa
RD
201 for line in self.lines:
202 for coords in line:
0dfb2393 203 dc.DrawLine(*coords)
b1462dfa
RD
204 dc.EndDrawing()
205
206#----------------------------------------------------------------------
207
8fa876ca 208class CustomDnDPanel(wx.Panel):
b1462dfa 209 def __init__(self, parent, log):
8fa876ca 210 wx.Panel.__init__(self, parent, -1)
b1462dfa 211
8fa876ca 212 self.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False))
b1462dfa 213
163f2606 214 # Make the controls
8fa876ca 215 text1 = wx.StaticText(self, -1,
899493dd 216 "Draw a little picture in this window\n"
7f3d6cbc
RD
217 "then switch the mode below and drag the\n"
218 "picture to the lower window or to another\n"
219 "application that accepts BMP's as a drop\n"
220 "target.\n"
163f2606
RD
221 )
222
8fa876ca 223 rb1 = wx.RadioButton(self, -1, "Draw", style=wx.RB_GROUP)
1e4a197e 224 rb1.SetValue(True)
8fa876ca 225 rb2 = wx.RadioButton(self, -1, "Drag")
1e4a197e 226 rb2.SetValue(False)
163f2606 227
8fa876ca 228 text2 = wx.StaticText(self, -1,
163f2606
RD
229 "The lower window is accepting a\n"
230 "custom data type that is a pickled\n"
231 "Python list of lines data.")
232
233 self.pad = DoodlePad(self, log)
234 view = DoodleViewer(self, log)
b1462dfa 235
163f2606 236 # put them in sizers
8fa876ca
RD
237 sizer = wx.BoxSizer(wx.HORIZONTAL)
238 box = wx.BoxSizer(wx.VERTICAL)
239 rbox = wx.BoxSizer(wx.HORIZONTAL)
163f2606
RD
240
241 rbox.Add(rb1)
242 rbox.Add(rb2)
372bde9b
RD
243 box.Add(text1, 0, wx.ALL, 10)
244 box.Add(rbox, 0, wx.ALIGN_CENTER)
fd3f2efe 245 box.Add((10,90))
372bde9b 246 box.Add(text2, 0, wx.ALL, 10)
163f2606
RD
247
248 sizer.Add(box)
249
8fa876ca
RD
250 dndsizer = wx.BoxSizer(wx.VERTICAL)
251 dndsizer.Add(self.pad, 1, wx.EXPAND|wx.ALL, 5)
252 dndsizer.Add(view, 1, wx.EXPAND|wx.ALL, 5)
163f2606 253
8fa876ca 254 sizer.Add(dndsizer, 1, wx.EXPAND)
b1462dfa 255
1e4a197e 256 self.SetAutoLayout(True)
b1462dfa
RD
257 self.SetSizer(sizer)
258
163f2606 259 # Events
8fa876ca
RD
260 wx.EVT_RADIOBUTTON(self, rb1.GetId(), self.OnRadioButton)
261 wx.EVT_RADIOBUTTON(self, rb2.GetId(), self.OnRadioButton)
163f2606 262
b1462dfa 263
163f2606
RD
264 def OnRadioButton(self, evt):
265 rb = self.FindWindowById(evt.GetId())
266 self.pad.SetMode(rb.GetLabel())
b1462dfa
RD
267
268
269#----------------------------------------------------------------------
270#----------------------------------------------------------------------
271
8fa876ca 272class TestPanel(wx.Panel):
b1462dfa 273 def __init__(self, parent, log):
8fa876ca 274 wx.Panel.__init__(self, parent, -1)
b1462dfa 275
1e4a197e 276 self.SetAutoLayout(True)
8fa876ca 277 sizer = wx.BoxSizer(wx.VERTICAL)
b1462dfa 278
4120ef2b 279 msg = "Custom Drag-And-Drop"
8fa876ca
RD
280 text = wx.StaticText(self, -1, "", style=wx.ALIGN_CENTRE)
281 text.SetFont(wx.Font(24, wx.SWISS, wx.NORMAL, wx.BOLD, False))
4120ef2b
RD
282 text.SetLabel(msg)
283 w,h = text.GetTextExtent(msg)
8fa876ca
RD
284 text.SetSize(wx.Size(w,h+1))
285 text.SetForegroundColour(wx.BLUE)
286 sizer.Add(text, 0, wx.EXPAND|wx.ALL, 5)
287 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND)
b1462dfa 288
8fa876ca 289 sizer.Add(CustomDnDPanel(self, log), 1, wx.EXPAND)
b1462dfa
RD
290
291 self.SetSizer(sizer)
292
293
294#----------------------------------------------------------------------
295
296def runTest(frame, nb, log):
163f2606
RD
297 #win = TestPanel(nb, log)
298 win = CustomDnDPanel(nb, log)
b1462dfa
RD
299 return win
300
899493dd
RD
301
302if __name__ == '__main__':
303 import sys
8fa876ca 304
899493dd
RD
305 class DummyLog:
306 def WriteText(self, text):
307 sys.stdout.write(text)
308
8fa876ca 309 class TestApp(wx.App):
899493dd 310 def OnInit(self):
8fa876ca 311 wx.InitAllImageHandlers()
899493dd 312 self.MakeFrame()
1e4a197e 313 return True
899493dd
RD
314
315 def MakeFrame(self, event=None):
8fa876ca
RD
316 frame = wx.Frame(None, -1, "Custom Drag and Drop", size=(550,400))
317 menu = wx.Menu()
899493dd 318 menu.Append(6543, "Window")
8fa876ca 319 mb = wx.MenuBar()
899493dd
RD
320 mb.Append(menu, "New")
321 frame.SetMenuBar(mb)
8fa876ca 322 wx.EVT_MENU(frame, 6543, self.MakeFrame)
899493dd 323 panel = TestPanel(frame, DummyLog())
1e4a197e 324 frame.Show(True)
899493dd
RD
325 self.SetTopWindow(frame)
326
8fa876ca 327 #----------------------------------------------------------------------
899493dd
RD
328
329 app = TestApp(0)
330 app.MainLoop()
331
b1462dfa
RD
332#----------------------------------------------------------------------
333
334
1e4a197e
RD
335overview = """<html><body>
336This demo shows Drag and Drop using a custom data type and a custom
337data object. A type called "DoodleLines" is created and a Python
338Pickle of a list is actually transfered in the drag and drop
339opperation.
b1462dfa 340
1e4a197e
RD
341A second data object is also created containing a bitmap of the image
342and is made available to any drop target that accepts bitmaps, such as
343MS Word.
b1462dfa 344
8fa876ca 345The two data objects are combined in a wx.DataObjectComposite and the
1e4a197e
RD
346rest is handled by the framework.
347</body></html>
348"""
b1462dfa 349