]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/DragImage.py
ad8f880a5d4fb3ceeefeb1b0973a3953f9a882b7
[wxWidgets.git] / wxPython / demo / DragImage.py
1 # 11/15/2003 - Jeff Grimmett (grimmtooth@softhome.net)
2 #
3 # o Updated for wx namespace
4 #
5
6 import wx
7 import images
8
9 #----------------------------------------------------------------------
10
11 class DragShape:
12 def __init__(self, bmp):
13 self.bmp = bmp
14 self.pos = (0,0)
15 self.shown = True
16 self.text = None
17 self.fullscreen = False
18
19 def HitTest(self, pt):
20 rect = self.GetRect()
21 return rect.InsideXY(pt.x, pt.y)
22
23 def GetRect(self):
24 return wx.Rect(self.pos[0], self.pos[1],
25 self.bmp.GetWidth(), self.bmp.GetHeight())
26
27 def Draw(self, dc, op = wx.COPY):
28 if self.bmp.Ok():
29 memDC = wx.MemoryDC()
30 memDC.SelectObject(self.bmp)
31
32 dc.Blit((self.pos[0], self.pos[1]),
33 (self.bmp.GetWidth(), self.bmp.GetHeight()),
34 memDC, (0, 0), op, True)
35
36 return True
37 else:
38 return False
39
40
41
42 #----------------------------------------------------------------------
43
44 class DragCanvas(wx.ScrolledWindow):
45 def __init__(self, parent, ID):
46 wx.ScrolledWindow.__init__(self, parent, ID)
47 self.shapes = []
48 self.dragImage = None
49 self.dragShape = None
50 self.hiliteShape = None
51
52 self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
53 self.bg_bmp = images.getBackgroundBitmap()
54
55 # Make a shape from an image and mask. This one will demo
56 # dragging outside the window
57 bmp = images.getTestStarBitmap()
58 shape = DragShape(bmp)
59 shape.pos = (5, 5)
60 shape.fullscreen = True
61 self.shapes.append(shape)
62
63 # Make a shape from some text
64 text = "Some Text"
65 bg_colour = wx.Colour(57, 115, 57) # matches the bg image
66 font = wx.Font(15, wx.ROMAN, wx.NORMAL, wx.BOLD)
67 textExtent = self.GetFullTextExtent(text, font)
68
69 # create a bitmap the same size as our text
70 bmp = wx.EmptyBitmap(textExtent[0], textExtent[1])
71
72 # 'draw' the text onto the bitmap
73 dc = wx.MemoryDC()
74 dc.SelectObject(bmp)
75 dc.SetBackground(wx.Brush(bg_colour, wx.SOLID))
76 dc.Clear()
77 dc.SetTextForeground(wx.RED)
78 dc.SetFont(font)
79 dc.DrawText(text, (0, 0))
80 dc.SelectObject(wx.NullBitmap)
81 mask = wx.MaskColour(bmp, bg_colour)
82 bmp.SetMask(mask)
83 shape = DragShape(bmp)
84 shape.pos = (5, 100)
85 shape.text = "Some dragging text"
86 self.shapes.append(shape)
87
88
89 # Make some shapes from some playing card images.
90 x = 200
91
92 for card in ['_01c_', '_12h_', '_13d_', '_10s_']:
93 bmpFunc = getattr(images, "get%sBitmap" % card)
94 bmp = bmpFunc()
95 shape = DragShape(bmp)
96 shape.pos = (x, 5)
97 self.shapes.append(shape)
98 x = x + 80
99
100
101 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
102 self.Bind(wx.EVT_PAINT, self.OnPaint)
103 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
104 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
105 self.Bind(wx.EVT_MOTION, self.OnMotion)
106 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
107
108
109 # We're not doing anything here, but you might have reason to.
110 # for example, if you were dragging something, you might elect to
111 # 'drop it' when the cursor left the window.
112 def OnLeaveWindow(self, evt):
113 pass
114
115
116 # tile the background bitmap
117 def TileBackground(self, dc):
118 sz = self.GetClientSize()
119 w = self.bg_bmp.GetWidth()
120 h = self.bg_bmp.GetHeight()
121
122 x = 0
123
124 while x < sz.width:
125 y = 0
126
127 while y < sz.height:
128 dc.DrawBitmap(self.bg_bmp, (x, y))
129 y = y + h
130
131 x = x + w
132
133
134 # Go through our list of shapes and draw them in whatever place they are.
135 def DrawShapes(self, dc):
136 for shape in self.shapes:
137 if shape.shown:
138 shape.Draw(dc)
139
140 # This is actually a sophisticated 'hit test', but in this
141 # case we're also determining which shape, if any, was 'hit'.
142 def FindShape(self, pt):
143 for shape in self.shapes:
144 if shape.HitTest(pt):
145 return shape
146 return None
147
148 # Remove a shape from the display
149 def EraseShape(self, shape, dc):
150 r = shape.GetRect()
151 dc.SetClippingRect(r)
152 self.TileBackground(dc)
153 self.DrawShapes(dc)
154 dc.DestroyClippingRegion()
155
156 # Clears the background, then redraws it. If the DC is passed, then
157 # we only do so in the area so designated. Otherwise, it's the whole thing.
158 def OnEraseBackground(self, evt):
159 dc = evt.GetDC()
160
161 if not dc:
162 dc = wxClientDC(self)
163 rect = self.GetUpdateRegion().GetBox()
164 dc.SetClippingRect(rect)
165 self.TileBackground(dc)
166
167 # Fired whenever a paint event occurs
168 def OnPaint(self, evt):
169 dc = wx.PaintDC(self)
170 self.PrepareDC(dc)
171 self.DrawShapes(dc)
172
173 # Left mouse button is down.
174 def OnLeftDown(self, evt):
175 # Did the mouse go down on one of our shapes?
176 shape = self.FindShape(evt.GetPosition())
177
178 # If a shape was 'hit', then set that as the shape we're going to
179 # drag around. Get our start position. Dragging has not yet started.
180 # That will happen once the mouse moves, OR the mouse is released.
181 if shape:
182 self.dragShape = shape
183 self.dragStartPos = evt.GetPosition()
184
185 # Left mouse button up.
186 def OnLeftUp(self, evt):
187 if not self.dragImage or not self.dragShape:
188 self.dragImage = None
189 self.dragShape = None
190 return
191
192 # Hide the image, end dragging, and nuke out the drag image.
193 self.dragImage.Hide()
194 self.dragImage.EndDrag()
195 self.dragImage = None
196
197 dc = wx.ClientDC(self)
198
199 if self.hiliteShape:
200 self.hiliteShape.Draw(dc)
201 self.hiliteShape = None
202
203 # reposition and draw the shape
204
205 # Note by jmg 11/28/03
206 # Here's the original:
207 #
208 # self.dragShape.pos = self.dragShape.pos + evt.GetPosition() - self.dragStartPos
209 #
210 # So if there are any problems associated with this, use that as
211 # a starting place in your investigation. I've tried to simulate the
212 # wx.Point __add__ method here -- it won't work for tuples as we
213 # have now from the various methods
214 #
215 # There must be a better way to do this :-)
216 #
217
218 self.dragShape.pos = (
219 self.dragShape.pos[0] + evt.GetPosition()[0] - self.dragStartPos[0],
220 self.dragShape.pos[1] + evt.GetPosition()[1] - self.dragStartPos[1]
221 )
222
223 self.dragShape.shown = True
224 self.dragShape.Draw(dc)
225 self.dragShape = None
226
227 # The mouse is moving
228 def OnMotion(self, evt):
229 # Ignore mouse movement if we're not dragging.
230 if not self.dragShape or not evt.Dragging() or not evt.LeftIsDown():
231 return
232
233 # if we have a shape, but haven't started dragging yet
234 if self.dragShape and not self.dragImage:
235
236 # only start the drag after having moved a couple pixels
237 tolerance = 2
238 pt = evt.GetPosition()
239 dx = abs(pt.x - self.dragStartPos.x)
240 dy = abs(pt.y - self.dragStartPos.y)
241 if dx <= tolerance and dy <= tolerance:
242 return
243
244 # erase the shape since it will be drawn independently now
245 dc = wx.ClientDC(self)
246 self.dragShape.shown = False
247 self.EraseShape(self.dragShape, dc)
248
249
250 if self.dragShape.text:
251 self.dragImage = wx.DragString(self.dragShape.text,
252 wx.StockCursor(wx.CURSOR_HAND))
253 else:
254 self.dragImage = wx.DragImage(self.dragShape.bmp,
255 wx.StockCursor(wx.CURSOR_HAND))
256
257 hotspot = self.dragStartPos - self.dragShape.pos
258 self.dragImage.BeginDrag(hotspot, self, self.dragShape.fullscreen)
259
260 self.dragImage.Move(pt)
261 self.dragImage.Show()
262
263
264 # if we have shape and image then move it, posibly highlighting another shape.
265 elif self.dragShape and self.dragImage:
266 onShape = self.FindShape(evt.GetPosition())
267 unhiliteOld = False
268 hiliteNew = False
269
270 # figure out what to hilite and what to unhilite
271 if self.hiliteShape:
272 if onShape is None or self.hiliteShape is not onShape:
273 unhiliteOld = True
274
275 if onShape and onShape is not self.hiliteShape and onShape.shown:
276 hiliteNew = True
277
278 # if needed, hide the drag image so we can update the window
279 if unhiliteOld or hiliteNew:
280 self.dragImage.Hide()
281
282 if unhiliteOld:
283 dc = wx.ClientDC(self)
284 self.hiliteShape.Draw(dc)
285 self.hiliteShape = None
286
287 if hiliteNew:
288 dc = wx.ClientDC(self)
289 self.hiliteShape = onShape
290 self.hiliteShape.Draw(dc, wx.INVERT)
291
292 # now move it and show it again if needed
293 self.dragImage.Move(evt.GetPosition())
294 if unhiliteOld or hiliteNew:
295 self.dragImage.Show()
296
297
298 #----------------------------------------------------------------------
299
300 def runTest(frame, nb, log):
301
302 win = wx.Panel(nb, -1)
303 canvas = DragCanvas(win, -1)
304
305 def onSize(evt, panel=win, canvas=canvas):
306 canvas.SetSize(panel.GetSize())
307
308 win.Bind(wx.EVT_SIZE, onSize)
309 return win
310
311 #----------------------------------------------------------------------
312
313
314
315 overview = """\
316 DragImage is used when you wish to drag an object on the screen, and a simple
317 cursor is not enough.
318
319 On Windows, the WIN32 API is used to do achieve smooth dragging. On other
320 platforms, <code>GenericDragImage</code> is used. Applications may also prefer to use
321 <code>GenericDragImage</code> on Windows, too.
322
323 <b>wxPython note</b>: wxPython uses <code>GenericDragImage</code> on all
324 platforms, but uses the <code>DragImage</code> name.
325
326 To use this class, when you wish to start dragging an image, create a
327 <code>DragImage</code> object and store it somewhere you can access it as the
328 drag progresses. Call BeginDrag to start, and EndDrag to stop the drag. To move
329 the image, initially call Show and then Move. If you wish to update the screen
330 contents during the drag (for example, highlight an item as in the example), first
331 call Hide, update the screen, call Move, and then call Show.
332
333 You can drag within one window, or you can use full-screen dragging either across
334 the whole screen, or just restricted to one area of the screen to save resources.
335 If you want the user to drag between two windows, then you will need to use
336 full-screen dragging.
337
338 """
339
340
341 if __name__ == '__main__':
342 import sys,os
343 import run
344 run.main(['', os.path.basename(sys.argv[0])])
345