]>
Commit | Line | Data |
---|---|---|
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 |