| 1 | |
| 2 | from wxPython.wx import * |
| 3 | |
| 4 | import images |
| 5 | |
| 6 | #---------------------------------------------------------------------- |
| 7 | |
| 8 | class DragShape: |
| 9 | def __init__(self, bmp): |
| 10 | self.bmp = bmp |
| 11 | self.pos = wxPoint(0,0) |
| 12 | self.shown = True |
| 13 | self.text = None |
| 14 | self.fullscreen = False |
| 15 | |
| 16 | |
| 17 | def HitTest(self, pt): |
| 18 | rect = self.GetRect() |
| 19 | return rect.InsideXY(pt.x, pt.y) |
| 20 | |
| 21 | |
| 22 | def GetRect(self): |
| 23 | return wxRect(self.pos.x, self.pos.y, |
| 24 | self.bmp.GetWidth(), self.bmp.GetHeight()) |
| 25 | |
| 26 | |
| 27 | def Draw(self, dc, op = wxCOPY): |
| 28 | if self.bmp.Ok(): |
| 29 | memDC = wxMemoryDC() |
| 30 | memDC.SelectObject(self.bmp) |
| 31 | |
| 32 | dc.Blit(self.pos.x, self.pos.y, |
| 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(wxScrolledWindow): |
| 45 | def __init__(self, parent, ID): |
| 46 | wxScrolledWindow.__init__(self, parent, ID) |
| 47 | self.shapes = [] |
| 48 | self.dragImage = None |
| 49 | self.dragShape = None |
| 50 | self.hiliteShape = None |
| 51 | |
| 52 | self.SetCursor(wxStockCursor(wxCURSOR_ARROW)) |
| 53 | self.bg_bmp = images.getBackgroundBitmap() |
| 54 | |
| 55 | |
| 56 | # Make a shape from an image and mask. This one will demo |
| 57 | # dragging outside the window |
| 58 | bmp = images.getTestStarBitmap() |
| 59 | shape = DragShape(bmp) |
| 60 | shape.pos = wxPoint(5, 5) |
| 61 | shape.fullscreen = True |
| 62 | self.shapes.append(shape) |
| 63 | |
| 64 | |
| 65 | # Make a shape from some text |
| 66 | text = "Some Text" |
| 67 | bg_colour = wxColour(57, 115, 57) # matches the bg image |
| 68 | font = wxFont(15, wxROMAN, wxNORMAL, wxBOLD) |
| 69 | textExtent = self.GetFullTextExtent(text, font) |
| 70 | bmp = wxEmptyBitmap(textExtent[0], textExtent[1]) |
| 71 | dc = wxMemoryDC() |
| 72 | dc.SelectObject(bmp) |
| 73 | dc.SetBackground(wxBrush(bg_colour, wxSOLID)) |
| 74 | dc.Clear() |
| 75 | dc.SetTextForeground(wxRED) |
| 76 | dc.SetFont(font) |
| 77 | dc.DrawText(text, 0, 0) |
| 78 | dc.SelectObject(wxNullBitmap) |
| 79 | mask = wxMaskColour(bmp, bg_colour) |
| 80 | bmp.SetMask(mask) |
| 81 | shape = DragShape(bmp) |
| 82 | shape.pos = wxPoint(5, 100) |
| 83 | shape.text = "Some dragging text" |
| 84 | self.shapes.append(shape) |
| 85 | |
| 86 | |
| 87 | # Make some shapes from some playing card images. |
| 88 | x = 200 |
| 89 | for card in ['_01c_', '_12h_', '_13d_', '_10s_']: |
| 90 | bmpFunc = getattr(images, "get%sBitmap" % card) |
| 91 | bmp = bmpFunc() |
| 92 | shape = DragShape(bmp) |
| 93 | shape.pos = wxPoint(x, 5) |
| 94 | self.shapes.append(shape) |
| 95 | x = x + 80 |
| 96 | |
| 97 | |
| 98 | EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) |
| 99 | EVT_PAINT(self, self.OnPaint) |
| 100 | EVT_LEFT_DOWN(self, self.OnLeftDown) |
| 101 | EVT_LEFT_UP(self, self.OnLeftUp) |
| 102 | EVT_MOTION(self, self.OnMotion) |
| 103 | EVT_LEAVE_WINDOW(self, self.OnLeaveWindow) |
| 104 | |
| 105 | |
| 106 | def OnLeaveWindow(self, evt): |
| 107 | pass |
| 108 | |
| 109 | |
| 110 | def TileBackground(self, dc): |
| 111 | # tile the background bitmap |
| 112 | sz = self.GetClientSize() |
| 113 | w = self.bg_bmp.GetWidth() |
| 114 | h = self.bg_bmp.GetHeight() |
| 115 | |
| 116 | x = 0 |
| 117 | while x < sz.width: |
| 118 | y = 0 |
| 119 | while y < sz.height: |
| 120 | dc.DrawBitmap(self.bg_bmp, x, y) |
| 121 | y = y + h |
| 122 | x = x + w |
| 123 | |
| 124 | |
| 125 | def DrawShapes(self, dc): |
| 126 | for shape in self.shapes: |
| 127 | if shape.shown: |
| 128 | shape.Draw(dc) |
| 129 | |
| 130 | |
| 131 | def FindShape(self, pt): |
| 132 | for shape in self.shapes: |
| 133 | if shape.HitTest(pt): |
| 134 | return shape |
| 135 | return None |
| 136 | |
| 137 | |
| 138 | def EraseShape(self, shape, dc): |
| 139 | r = shape.GetRect() |
| 140 | dc.SetClippingRegion(r.x, r.y, r.width, r.height) |
| 141 | self.TileBackground(dc) |
| 142 | self.DrawShapes(dc) |
| 143 | dc.DestroyClippingRegion() |
| 144 | |
| 145 | |
| 146 | def OnEraseBackground(self, evt): |
| 147 | dc = evt.GetDC() |
| 148 | if not dc: |
| 149 | dc = wxClientDC(self) |
| 150 | rect = self.GetUpdateRegion().GetBox() |
| 151 | dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height) |
| 152 | self.TileBackground(dc) |
| 153 | |
| 154 | |
| 155 | def OnPaint(self, evt): |
| 156 | dc = wxPaintDC(self) |
| 157 | self.PrepareDC(dc) |
| 158 | self.DrawShapes(dc) |
| 159 | |
| 160 | |
| 161 | def OnLeftDown(self, evt): |
| 162 | shape = self.FindShape(evt.GetPosition()) |
| 163 | if shape: |
| 164 | # get ready to start dragging, but wait for the user to |
| 165 | # move it a bit first |
| 166 | self.dragShape = shape |
| 167 | self.dragStartPos = evt.GetPosition() |
| 168 | |
| 169 | |
| 170 | def OnLeftUp(self, evt): |
| 171 | if not self.dragImage or not self.dragShape: |
| 172 | self.dragImage = None |
| 173 | self.dragShape = None |
| 174 | return |
| 175 | |
| 176 | # end the dragging |
| 177 | self.dragImage.Hide() |
| 178 | self.dragImage.EndDrag() |
| 179 | self.dragImage = None |
| 180 | |
| 181 | dc = wxClientDC(self) |
| 182 | if self.hiliteShape: |
| 183 | self.hiliteShape.Draw(dc) |
| 184 | self.hiliteShape = None |
| 185 | |
| 186 | # reposition and draw the shape |
| 187 | self.dragShape.pos = self.dragShape.pos + evt.GetPosition() - self.dragStartPos |
| 188 | self.dragShape.shown = True |
| 189 | self.dragShape.Draw(dc) |
| 190 | self.dragShape = None |
| 191 | |
| 192 | |
| 193 | def OnMotion(self, evt): |
| 194 | if not self.dragShape or not evt.Dragging() or not evt.LeftIsDown(): |
| 195 | return |
| 196 | |
| 197 | # if we have a shape, but havn't started dragging yet |
| 198 | if self.dragShape and not self.dragImage: |
| 199 | |
| 200 | # only start the drag after having moved a couple pixels |
| 201 | tolerance = 2 |
| 202 | pt = evt.GetPosition() |
| 203 | dx = abs(pt.x - self.dragStartPos.x) |
| 204 | dy = abs(pt.y - self.dragStartPos.y) |
| 205 | if dx <= tolerance and dy <= tolerance: |
| 206 | return |
| 207 | |
| 208 | # erase the shape since it will be drawn independently now |
| 209 | dc = wxClientDC(self) |
| 210 | self.dragShape.shown = False |
| 211 | self.EraseShape(self.dragShape, dc) |
| 212 | |
| 213 | |
| 214 | if self.dragShape.text: |
| 215 | self.dragImage = wxDragString(self.dragShape.text, |
| 216 | wxStockCursor(wxCURSOR_HAND)) |
| 217 | else: |
| 218 | self.dragImage = wxDragImage(self.dragShape.bmp, |
| 219 | wxStockCursor(wxCURSOR_HAND)) |
| 220 | |
| 221 | hotspot = self.dragStartPos - self.dragShape.pos |
| 222 | self.dragImage.BeginDrag(hotspot, self, self.dragShape.fullscreen) |
| 223 | |
| 224 | self.dragImage.Move(pt) |
| 225 | self.dragImage.Show() |
| 226 | |
| 227 | |
| 228 | # if we have shape and image then move it, posibly highlighting another shape. |
| 229 | elif self.dragShape and self.dragImage: |
| 230 | onShape = self.FindShape(evt.GetPosition()) |
| 231 | unhiliteOld = False |
| 232 | hiliteNew = False |
| 233 | |
| 234 | # figure out what to hilite and what to unhilite |
| 235 | if self.hiliteShape: |
| 236 | if onShape is None or self.hiliteShape is not onShape: |
| 237 | unhiliteOld = True |
| 238 | |
| 239 | if onShape and onShape is not self.hiliteShape and onShape.shown: |
| 240 | hiliteNew = True |
| 241 | |
| 242 | # if needed, hide the drag image so we can update the window |
| 243 | if unhiliteOld or hiliteNew: |
| 244 | self.dragImage.Hide() |
| 245 | |
| 246 | if unhiliteOld: |
| 247 | dc = wxClientDC(self) |
| 248 | self.hiliteShape.Draw(dc) |
| 249 | self.hiliteShape = None |
| 250 | |
| 251 | if hiliteNew: |
| 252 | dc = wxClientDC(self) |
| 253 | self.hiliteShape = onShape |
| 254 | self.hiliteShape.Draw(dc, wxINVERT) |
| 255 | |
| 256 | # now move it and show it again if needed |
| 257 | self.dragImage.Move(evt.GetPosition()) |
| 258 | if unhiliteOld or hiliteNew: |
| 259 | self.dragImage.Show() |
| 260 | |
| 261 | |
| 262 | #---------------------------------------------------------------------- |
| 263 | |
| 264 | def runTest(frame, nb, log): |
| 265 | win = wxPanel(nb, -1) |
| 266 | canvas = DragCanvas(win, -1) |
| 267 | def onSize(evt, panel=win, canvas=canvas): canvas.SetSize(panel.GetSize()) |
| 268 | EVT_SIZE(win, onSize) |
| 269 | return win |
| 270 | |
| 271 | #---------------------------------------------------------------------- |
| 272 | |
| 273 | |
| 274 | |
| 275 | overview = """\ |
| 276 | """ |
| 277 | |
| 278 | |
| 279 | if __name__ == '__main__': |
| 280 | import sys,os |
| 281 | import run |
| 282 | run.main(['', os.path.basename(sys.argv[0])]) |
| 283 | |