Added support for drag'n drop variable from one graph to another with visible graph
authorLaurent Bessard
Wed, 06 Feb 2013 01:18:56 +0100
changeset 925 5f9dd88a605b
parent 924 5f2cc382be8c
child 926 2323981f5d41
Added support for drag'n drop variable from one graph to another with visible graph
controls/DebugVariablePanel.py
--- a/controls/DebugVariablePanel.py	Tue Feb 05 16:42:52 2013 +0100
+++ b/controls/DebugVariablePanel.py	Wed Feb 06 01:18:56 2013 +0100
@@ -36,6 +36,8 @@
     matplotlib.use('WX')
     import matplotlib.pyplot
     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
+    from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
+    from matplotlib.backends.backend_agg import FigureCanvasAgg
     from mpl_toolkits.mplot3d import Axes3D
     USE_MPL = True
 except:
@@ -330,12 +332,9 @@
                     else:
                         self.ParentWindow.InsertValue(values[0], target_idx, force=True)
                 else:
-                    ax, ay, aw, ah = self.ParentControl.Axes.get_position().bounds
-                    rect = wx.Rect(ax * width, height - (ay + ah) * height,
-                                   aw * width, ah * height)
+                    rect = self.ParentControl.GetAxesBoundingBox()
                     if rect.InsideXY(x, y):
-                        merge_rect = wx.Rect(ax * width, height - (ay + ah) * height,
-                                             aw * width / 2., ah * height)
+                        merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
                         if merge_rect.InsideXY(x, y):
                             merge_type = GRAPH_ORTHOGONAL
                         wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True)
@@ -619,7 +618,49 @@
             else:
                 mask.append("*")
         return mask
-        
+    
+    class DraggingFigureCanvas(FigureCanvas):
+        
+        def __init__(self, parent, window, *args, **kwargs):
+            FigureCanvas.__init__(self, parent, *args, **kwargs)
+            
+            self.ParentWindow = window
+            
+        def draw(self, drawDC=None):
+            FigureCanvasAgg.draw(self)
+    
+            self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
+            if self.ParentWindow.IsDragging():
+                destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self.Parent)
+                if destBBox.width > 0 and destBBox.height > 0:
+                    srcPanel = self.ParentWindow.DraggingAxesPanel
+                    srcBBox = srcPanel.GetAxesBoundingBox()
+                    
+                    if destBBox.x == 0:
+                        srcX = srcBBox.x + srcBBox.width - destBBox.width
+                    else:
+                        srcX = srcBBox.x
+                    if destBBox.y == 0:
+                        srcY = srcBBox.y + srcBBox.height - destBBox.height
+                    else:
+                        srcY = srcBBox.y
+                    
+                    srcBmp = _convert_agg_to_wx_bitmap(srcPanel.Canvas.get_renderer(), None)
+                    srcDC = wx.MemoryDC()
+                    srcDC.SelectObject(srcBmp)
+                    
+                    destDC = wx.MemoryDC()
+                    destDC.SelectObject(self.bitmap)
+                    
+                    destDC.BeginDrawing()
+                    destDC.Blit(destBBox.x, destBBox.y, 
+                                int(destBBox.width), int(destBBox.height), 
+                                srcDC, srcX, srcY)
+                    destDC.EndDrawing()
+                    
+            self._isDrawn = True
+            self.gui_repaint(drawDC=drawDC)
+    
     class DebugVariableGraphic(DebugVariableViewer):
         
         def __init__(self, parent, window, items, graph_type):
@@ -633,11 +674,12 @@
         def AddViewer(self):
             self.Figure = matplotlib.figure.Figure(facecolor='w')
             
-            self.Canvas = FigureCanvas(self, -1, self.Figure)
+            self.Canvas = DraggingFigureCanvas(self, self.ParentWindow, -1, self.Figure)
             self.Canvas.SetMinSize(wx.Size(200, 200))
             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
+            self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasLeftDown)
             self.Canvas.mpl_connect('motion_notify_event', self.OnCanvasMotion)
-            self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick)
+            self.Canvas.mpl_connect('button_release_event', self.OnCanvasLeftUp)
             
             self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW)
         
@@ -659,16 +701,26 @@
                 setattr(self, name, button)
                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
-    
-        def OnCanvasClick(self, event):
+        
+        def GetAxesBoundingBox(self, absolute=False):
+            width, height = self.Canvas.GetSize()
+            ax, ay, aw, ah = self.Axes.get_position().bounds
+            bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
+                           aw * width + 2, ah * height + 1)
+            if absolute:
+                xw, yw = self.GetPosition()
+                bbox.x += xw
+                bbox.y += yw
+            return bbox
+        
+        def OnCanvasLeftDown(self, event):
             x, y = event.GetPosition()
             width, height = self.Canvas.GetSize()
             if len(self.Items) == 1:
-                ax, ay, aw, ah = self.Axes.get_position().bounds
-                rect = wx.Rect(ax * width, height - (ay + ah) * height,
-                               aw * width, ah * height)
+                rect = self.GetAxesBoundingBox()
                 if rect.InsideXY(x, y):
-                    self.DoDragDrop(0)
+                    xw, yw = self.GetPosition()
+                    self.ParentWindow.StartDragNDrop(self, x + xw, y + yw)
                     return
             elif self.Legend is not None:
                 item_idx = None
@@ -683,6 +735,15 @@
                     return
             event.Skip()
         
+        def OnCanvasLeftUp(self, event):
+            if self.ParentWindow.IsDragging():
+                width, height = self.Canvas.GetSize()
+                xw, yw = self.GetPosition()
+                self.ParentWindow.StopDragNDrop(
+                    self.Items[0].GetVariable(),
+                    xw + event.x, 
+                    yw + height - event.y)
+        
         def DoDragDrop(self, item_idx):
             self.ParentWindow.ResetCursorTickRatio()
             data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move")))
@@ -698,7 +759,13 @@
                     Axes3D._on_move(self.Axes, event)
         
         def OnCanvasMotion(self, event):
-            if not self.Is3DCanvas():
+            if self.ParentWindow.IsDragging():
+                width, height = self.Canvas.GetSize()
+                xw, yw = self.GetPosition()
+                self.ParentWindow.MoveDragNDrop(
+                    xw + event.x, 
+                    yw + height - event.y)
+            elif not self.Is3DCanvas():
                 if event.inaxes == self.Axes:
                     start_tick, end_tick = self.ParentWindow.GetRange()
                     cursor_tick_ratio = None
@@ -956,6 +1023,10 @@
             self.StartTick = 0
             self.Fixed = False
             self.Force = False
+            self.CursorTickRatio = None
+            self.DraggingAxesPanel = None
+            self.DraggingAxesBoundingBox = None
+            self.DraggingAxesMousePos = None
             
             self.GraphicPanels = []
             
@@ -1005,7 +1076,6 @@
             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
             
             self.RefreshCanvasRange()
-            self.CursorTickRatio = None
             
         else:
             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
@@ -1150,6 +1220,60 @@
             label += "(%s)" % duration
         return label
     
+    def StartDragNDrop(self, panel, x_mouse, y_mouse):
+        self.DraggingAxesPanel = panel
+        self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(absolute=True)
+        self.DraggingAxesMousePos = wx.Point(
+            x_mouse - self.DraggingAxesBoundingBox.x, 
+            y_mouse - self.DraggingAxesBoundingBox.y)
+        self.ResetCursorTickRatio()
+        
+    def MoveDragNDrop(self, x_mouse, y_mouse):
+        self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
+        self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
+        self.ForceRefresh()
+    
+    def IsDragging(self):
+        return self.DraggingAxesPanel is not None
+    
+    def GetDraggingAxesClippingRegion(self, panel):
+        x, y = panel.GetPosition()
+        width, height = panel.Canvas.GetSize()
+        bbox = wx.Rect(x, y, width, height)
+        bbox = bbox.Intersect(self.DraggingAxesBoundingBox)
+        bbox.x -= x
+        bbox.y -= y
+        return bbox
+    
+    def StopDragNDrop(self, variable, x_mouse, y_mouse):
+        self.DraggingAxesPanel = None
+        self.DraggingAxesBoundingBox = None
+        self.DraggingAxesMousePos = None
+        for idx, panel in enumerate(self.GraphicPanels):
+            xw, yw = panel.GetPosition()
+            width, height = panel.Canvas.GetSize()
+            bbox = wx.Rect(xw, yw, width, height)
+            if bbox.InsideXY(x_mouse, y_mouse):
+                merge_type = GRAPH_PARALLEL
+                if panel.Is3DCanvas():
+                    if y_mouse > yw + height / 2:
+                        idx += 1
+                    wx.CallAfter(self.MoveGraph, variable, idx)
+                    return
+                else:
+                    rect = panel.GetAxesBoundingBox(True)
+                    if rect.InsideXY(x_mouse, y_mouse):
+                        merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
+                        if merge_rect.InsideXY(x_mouse, y_mouse):
+                            merge_type = GRAPH_ORTHOGONAL
+                        wx.CallAfter(self.MergeGraphs, variable, idx, merge_type, force=True)
+                    else:
+                        if y_mouse > yw + height / 2:
+                            idx += 1
+                        wx.CallAfter(self.MoveGraph, variable, idx)
+                break
+        self.ForceRefresh()
+    
     def RefreshView(self, only_values=False):
         if USE_MPL:
             self.RefreshCanvasPosition()