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