title: Dragging and Copying Images in wxPython
body: <p>I am attempting to modify the DragImage demo from the wxPython examples so that instead of just moving the image around, dragging an image produces a new copy which <em>is</em> draggable, while the original "source" image remains. A decent analogy is that the original images are like a set of widgets to choose from; clicking and dragging on any one of those produces a widget for you to place wherever (and this can be done multiple times) while the source widget remains.</p>
  
  <pre><code>import os
  import glob
  
  import wx
  import wx.lib.scrolledpanel as scrolled
  
  class MainWindow(wx.Frame):
      def __init__(self, parent, title):
          wx.Frame.__init__(self, parent)
          frm_pnl = MainPanel(self)
  
          self.Show()
  
  class DragShape:
      def __init__(self, bmp):
          self.bmp = bmp
          self.pos = (0,0)
          self.shown = True
          self.text = None
          self.fullscreen = False
  
      def HitTest(self, pt):
          rect = self.GetRect()
          return rect.InsideXY(pt.x, pt.y)
  
      def GetRect(self):
          #return wx.Rect(self.pos[0], self.pos[1], self.bmp.GetWidth(), self.bmp.GetHeight())
          return wx.Rect(self.pos[0], self.pos[1], self.bmp.GetWidth()/2, self.bmp.GetHeight()/2)
  
      def Draw(self, dc, op = wx.COPY):
          if self.bmp.Ok():
              memDC = wx.MemoryDC()
              memDC.SelectObject(self.bmp)
  
              #dc.Blit(self.pos[0], self.pos[1],
              #        self.bmp.GetWidth(), self.bmp.GetHeight(),
              #        memDC, 0, 0, op, True)
  
              dc.Blit(self.pos[0], self.pos[1],
                      self.bmp.GetWidth()/2, self.bmp.GetHeight()/2,
                      memDC, 0, 0, op, True)
  
              return True
          else:
              return False
  
  class MainPanel(wx.Panel):
      def __init__(self, parent):
          wx.Panel.__init__(self, parent, -1, size = (900, 700))
  
          self.shapes = []
  
          #panel for mechanisms
          mechPnl = MechanismPanel(self)
  
          mechSzr = wx.BoxSizer(wx.HORIZONTAL)
          mechSzr.Add(mechPnl, 1)
  
          selfSizer = wx.BoxSizer(wx.VERTICAL)
          selfSizer.Add(mechSzr, 0, wx.EXPAND)
          selfSizer.Layout()
          self.SetSizer(selfSizer)
  
          self.dragImage = None
          self.dragShape = None
          self.hiliteShape = None
  
          self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
  
          #self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
          self.Bind(wx.EVT_PAINT, self.OnPaint)
          mechPnl.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
          mechPnl.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
          mechPnl.Bind(wx.EVT_MOTION, self.OnMotion)
          mechPnl.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
  
      # The mouse is moving
      def OnMotion(self, evt):
          print "On motion!"
  
          # Ignore mouse movement if we're not dragging.
          if not self.dragShape or not evt.Dragging() or not evt.LeftIsDown():
              return
  
          # if we have a shape, but haven't started dragging yet
          if self.dragShape and not self.dragImage:
  
              # only start the drag after having moved a couple pixels
              tolerance = 2
              pt = evt.GetPosition()
              dx = abs(pt.x - self.dragStartPos.x)
              dy = abs(pt.y - self.dragStartPos.y)
              if dx &lt;= tolerance and dy &lt;= tolerance:
                  return
  
              # refresh the area of the window where the shape was so it
              # will get erased.
              self.dragShape.shown = False
              self.RefreshRect(self.dragShape.GetRect(), True)
              self.Update()
  
              if self.dragShape.text:
                  self.dragImage = wx.DragString(self.dragShape.text,
                                                wx.StockCursor(wx.CURSOR_HAND))
              else:
                  self.dragImage = wx.DragImage(self.dragShape.bmp,
                                               wx.StockCursor(wx.CURSOR_HAND))
  
              hotspot = self.dragStartPos - self.dragShape.pos
              self.dragImage.BeginDrag(hotspot, self, self.dragShape.fullscreen)
  
              self.dragImage.Move(pt)
              self.dragImage.Show()
  
  
          # if we have shape and image then move it, posibly highlighting another shape.
          elif self.dragShape and self.dragImage:
              onShape = self.FindShape(evt.GetPosition())
              unhiliteOld = False
              hiliteNew = False
  
              # figure out what to hilite and what to unhilite
              if self.hiliteShape:
                  if onShape is None or self.hiliteShape is not onShape:
                      unhiliteOld = True
  
              if onShape and onShape is not self.hiliteShape and onShape.shown:
                  hiliteNew = True
  
              # if needed, hide the drag image so we can update the window
              if unhiliteOld or hiliteNew:
                  self.dragImage.Hide()
  
              if unhiliteOld:
                  dc = wx.ClientDC(self)
                  self.hiliteShape.Draw(dc)
                  self.hiliteShape = None
  
              if hiliteNew:
                  dc = wx.ClientDC(self)
                  self.hiliteShape = onShape
                  self.hiliteShape.Draw(dc, wx.INVERT)
  
              # now move it and show it again if needed
              self.dragImage.Move(evt.GetPosition())
              if unhiliteOld or hiliteNew:
                  self.dragImage.Show()
  
      # Left mouse button up.
      def OnLeftUp(self, evt):
          print "On left up!"
  
          if not self.dragImage or not self.dragShape:
              self.dragImage = None
              self.dragShape = None
              return
  
          # Hide the image, end dragging, and nuke out the drag image.
          self.dragImage.Hide()
          self.dragImage.EndDrag()
          self.dragImage = None
  
          if self.hiliteShape:
              self.RefreshRect(self.hiliteShape.GetRect())
              self.hiliteShape = None
  
          # reposition and draw the shape
  
          # Note by jmg 11/28/03 
          # Here's the original:
          #
          # self.dragShape.pos = self.dragShape.pos + evt.GetPosition() - self.dragStartPos
          #
          # So if there are any problems associated with this, use that as
          # a starting place in your investigation. I've tried to simulate the
          # wx.Point __add__ method here -- it won't work for tuples as we
          # have now from the various methods
          #
          # There must be a better way to do this :-)
          #
  
          self.dragShape.pos = (
              self.dragShape.pos[0] + evt.GetPosition()[0] - self.dragStartPos[0],
              self.dragShape.pos[1] + evt.GetPosition()[1] - self.dragStartPos[1]
              )
  
          self.dragShape.shown = True
          self.RefreshRect(self.dragShape.GetRect())
          self.dragShape = None
  
      # Fired whenever a paint event occurs
      def OnPaint(self, evt):
          print "On paint!"
  
          dc = wx.PaintDC(self)
          self.PrepareDC(dc)
          self.DrawShapes(dc)
  
      # Left mouse button is down.
      def OnLeftDown(self, evt):
          print "On left down!"
  
          # Did the mouse go down on one of our shapes?
          shape = self.FindShape(evt.GetPosition())
  
          # If a shape was 'hit', then set that as the shape we're going to
          # drag around. Get our start position. Dragging has not yet started.
          # That will happen once the mouse moves, OR the mouse is released.
          if shape:
              self.dragShape = shape
              self.dragStartPos = evt.GetPosition()
  
      # Go through our list of shapes and draw them in whatever place they are.
      def DrawShapes(self, dc):
          for shape in self.shapes:
              if shape.shown:
                  shape.Draw(dc)
  
      # This is actually a sophisticated 'hit test', but in this
      # case we're also determining which shape, if any, was 'hit'.
      def FindShape(self, pt):
          for shape in self.shapes:
              if shape.HitTest(pt):
                  return shape
          return None
  
      # Clears the background, then redraws it. If the DC is passed, then
      # we only do so in the area so designated. Otherwise, it's the whole thing.
      def OnEraseBackground(self, evt):
          dc = evt.GetDC()
          if not dc:
              dc = wx.ClientDC(self)
              rect = self.GetUpdateRegion().GetBox()
              dc.SetClippingRect(rect)
          self.TileBackground(dc)
  
      # tile the background bitmap
      def TileBackground(self, dc):
          sz = self.GetClientSize()
          w = self.bg_bmp.GetWidth()
          h = self.bg_bmp.GetHeight()
  
          x = 0
  
          while x &lt; sz.width:
              y = 0
  
              while y &lt; sz.height:
                  dc.DrawBitmap(self.bg_bmp, x, y)
                  y = y + h
  
              x = x + w
  
      # We're not doing anything here, but you might have reason to.
      # for example, if you were dragging something, you might elect to
      # 'drop it' when the cursor left the window.
      def OnLeaveWindow(self, evt):
          pass
  
  class MechanismPanel(scrolled.ScrolledPanel):
      def __init__(self, parent):
          scrolled.ScrolledPanel.__init__(self, parent, -1, size = (400, 140))
          self.SetBackgroundColour((211, 211, 211))
  
          mechPnlSzr = wx.BoxSizer(wx.HORIZONTAL)
  
          os.chdir("./figures")
          position = 50
          for file in glob.glob("icon*.png"):
              print file
              imgIcon = wx.Image(file, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
              staticBitmap = wx.StaticBitmap(self, -1, imgIcon, (position, 50), (50, 50))
              shape = DragShape(staticBitmap.GetBitmap())
              shape.pos = (position, 50)
              position = position + 100
              shape.fullscreen = True
              parent.shapes.append(shape)
              mechPnlSzr.Add(staticBitmap, 0, wx.FIXED, border = 20)
  
          self.SetSizer(mechPnlSzr)
  
          self.SetAutoLayout(1)
          self.SetupScrolling()#scroll_y = False)
  
  app = wx.App(False)
  frame = MainWindow(None, "Trading Client")
  app.MainLoop()
  </code></pre>