title: Dragging and Dropping Images Across Panels in wxPython
body: <p>I'm currently unsure of how to implement dragging and dropping of some object (in this case, pngs) across panels. I have looked at the associated DragImage example provided in the wxPython examples, and most of the code here is derived from that. However, if you run the code below (you'll need to generate a sample PNG or two), I have three panels: the panel across the top, where I would like the PNGs to be loaded across in a row, and then two panels beneath it. The line of code which would line up the PNGs across the top panel is currently commented out (under the MechanismPanel class, which is toward the bottom), as it prevents me from getting any mouse events. I am unsure if it is possible to drag and drop images across panels, or if it is, if I'm going about it correctly.</p>
  
  <p><strong>Edit:</strong> A somewhat more concise explanation of what I'm looking for. An image in the top panel, where you can drag one of those images out and add it to one of the lower panels. Think of the top panel as a line of widgets to draw from, and the bottom two panels as places to arrange widgets. To help separate concerns, I have another question regarding the dragging and copying of the images in the top panel <a href="https://stackoverflow.com/questions/15103576/dragging-and-copying-images-in-wxpython">here</a>.</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())
  
      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)
  
              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)
  
          #panels for timeline
          posPnl = IdTimelinePanel(self)
          timelinePnl = TimelinePanel(self)
  
          mainSzr = wx.BoxSizer(wx.HORIZONTAL)
          mainSzr.Add(posPnl, 1, wx.EXPAND)
          mainSzr.Add(timelinePnl, 1, wx.EXPAND)
  
          selfSizer = wx.BoxSizer(wx.VERTICAL)
          selfSizer.Add(mechSzr, 0, wx.EXPAND)
          selfSizer.Add(mainSzr, 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)
          timelinePnl.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
          timelinePnl.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
          timelinePnl.Bind(wx.EVT_MOTION, self.OnMotion)
          timelinePnl.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 IdTimelinePanel(wx.Panel):
      def __init__(self, parent):
          wx.Panel.__init__(self, parent, -1, size = (400, 200))
          self.SetBackgroundColour((255, 0, 255))
  
          lbl1 = wx.StaticText(self, label="Position")
          lbl2 = wx.StaticText(self, label="Size")
  
          posPnlSzr = wx.BoxSizer(wx.VERTICAL)
          posPnlSzr.Add(lbl1, 1, wx.FIXED&amp;wx.LEFT)
          posPnlSzr.Add(lbl2, 1, wx.FIXED&amp;wx.LEFT)
  
          self.SetSizer(posPnlSzr)
  
          #wx.StaticText(self, -1, "This is the horizontal ID space for the timeline")
          self.SetAutoLayout(1)
  
  class TimelinePanel(scrolled.ScrolledPanel):
      def __init__(self, parent):
          scrolled.ScrolledPanel.__init__(self, parent, -1, size = (300, 200))
          self.SetBackgroundColour((255, 0, 0))
  
          lbl12 = wx.StaticText(self, label="Position")
          lbl22 = wx.StaticText(self, label="Size")
  
          posPnlSzr2 = wx.BoxSizer(wx.VERTICAL)
          posPnlSzr2.Add(lbl12, 1, wx.GROW)
          posPnlSzr2.Add(lbl22, 1, wx.GROW)
  
          self.SetSizer(posPnlSzr2)
  
          #wx.StaticText(self, -1, "This is the horizontal scroll space for the timeline")
          self.SetAutoLayout(1)
          self.SetupScrolling(scroll_y = False)
  
  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")
          for file in glob.glob("icon*.png"):
              print file
              imgIcon = wx.Image(file, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
              staticBitmap = wx.StaticBitmap(self, -1, imgIcon, (0, 0), (50, 50))
              shape = DragShape(staticBitmap.GetBitmap())
              shape.pos = (50, 50)
              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>