controls/DebugVariablePanel.py
changeset 930 4be515ac635e
parent 929 c562031146e4
child 931 da4970f6c46c
equal deleted inserted replaced
929:c562031146e4 930:4be515ac635e
    37     import matplotlib.pyplot
    37     import matplotlib.pyplot
    38     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    38     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    39     from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
    39     from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
    40     from matplotlib.backends.backend_agg import FigureCanvasAgg
    40     from matplotlib.backends.backend_agg import FigureCanvasAgg
    41     from mpl_toolkits.mplot3d import Axes3D
    41     from mpl_toolkits.mplot3d import Axes3D
    42     color_cycle = matplotlib.rcParams['axes.color_cycle']
    42     color_cycle = ['r', 'b', 'g', 'm', 'y', 'k']
       
    43     cursor_color = '#800080'
    43     USE_MPL = True
    44     USE_MPL = True
    44 except:
    45 except:
    45     USE_MPL = False
    46     USE_MPL = False
    46 
    47 
    47 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
    48 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
   616      HIGHLIGHT_RIGHT] = range(5)
   617      HIGHLIGHT_RIGHT] = range(5)
   617     
   618     
   618     HIGHLIGHT_PEN = wx.Pen(wx.Colour(0, 128, 255))
   619     HIGHLIGHT_PEN = wx.Pen(wx.Colour(0, 128, 255))
   619     HIGHLIGHT_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128))
   620     HIGHLIGHT_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128))
   620     
   621     
   621     if wx.Platform == '__WXMSW__':
   622     #CANVAS_SIZE_TYPES
   622         popupclass = wx.PopupTransientWindow
   623     [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = range(3)
   623     else:
   624     
   624         popupclass = wx.PopupWindow
   625     class GraphButton():
   625     
   626         
   626     class PopupWithButtons(popupclass):
   627         def __init__(self, x, y, bitmap, callback):
   627     
   628             self.Position = wx.Point(x, y)
   628         def __init__(self, parent, window, item, style=wx.HORIZONTAL):
   629             self.Bitmap = bitmap
   629             popupclass.__init__(self, parent, wx.NO_BORDER)
   630             self.Shown = True
   630             self.SetBackgroundColour(wx.WHITE)
   631             self.Callback = callback
   631             
   632         
   632             self.ParentWindow = window
   633         def __del__(self):
   633             self.Item = item
   634             self.callback = None
   634             
   635         
   635             main_sizer = wx.BoxSizer(style)
   636         def GetSize(self):
   636             
   637             return self.Bitmap.GetSize()
   637             if self.Item.IsForced():
   638         
   638                 buttons = [("ReleaseButton", "release", _("Release value"))]
   639         def SetPosition(self, x, y):
   639             
   640             self.Position = wx.Point(x, y)
   640             else:
   641         
   641                 buttons = [("ForceButton", "force", _("Force value"))]
   642         def SetBitmap(self, bitmap):
   642             buttons.append(("DeleteButton", "delete_graph", _("Remove debug variable")))
   643             self.Bitmap = bitmap
   643             
   644             
   644             for name, bitmap, help in buttons:
   645         def SetCallback(self, callback):
   645                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
   646             self.Callback = callback
   646                       size=wx.Size(20, 20), style=wx.NO_BORDER)
   647         
   647                 button.SetToolTipString(help)
   648         def Show(self):
   648                 setattr(self, name, button)
   649             self.Shown = True
   649                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
   650             
   650                 main_sizer.AddWindow(button)
   651         def Hide(self):
   651             main_sizer.Layout()
   652             self.Shown = False
   652             
   653         
   653             self.SetSizer(main_sizer)
   654         def HitTest(self, x, y):
   654             main_sizer.Fit(self)
   655             if self.Shown:
   655         
   656                 w, h = self.Bitmap.GetSize()
   656         def GetItem(self):
   657                 rect = wx.Rect(self.Position.x, self.Position.y, w, h)
   657             return self.Item
   658                 if rect.InsideXY(x, y):
   658         
   659                     return True
   659         def OnForceButton(self, event):
   660             return False
   660             wx.CallAfter(self.Parent.DismissButtons)
   661         
   661             wx.CallAfter(self.Parent.ForceValue, self.Item)
   662         def ProcessCallback(self):
   662             event.Skip()
   663             if self.Callback is not None:
   663         
   664                 wx.CallAfter(self.Callback)
   664         def OnReleaseButton(self, event):
   665                 
   665             wx.CallAfter(self.Parent.DismissButtons)
   666         def Draw(self, dc):
   666             wx.CallAfter(self.Parent.ReleaseValue, self.Item)
   667             if self.Shown:
   667             event.Skip()
   668                 dc.DrawBitmap(self.Bitmap, self.Position.x, self.Position.y, True)
   668         
       
   669         def OnDeleteButton(self, event):            
       
   670             wx.CallAfter(self.Parent.DismissButtons)
       
   671             wx.CallAfter(self.ParentWindow.DeleteValue, self.Parent, self.Item)
       
   672             event.Skip()
       
   673     
       
   674         def OnDismiss(self):
       
   675             wx.CallAfter(self.Parent.DismissButtons)
       
   676     
   669     
   677     class DraggingFigureCanvas(FigureCanvas):
   670     class DraggingFigureCanvas(FigureCanvas):
   678         
   671         
   679         def __init__(self, parent, window, *args, **kwargs):
   672         def __init__(self, parent, window, *args, **kwargs):
   680             FigureCanvas.__init__(self, parent, *args, **kwargs)
   673             FigureCanvas.__init__(self, parent, *args, **kwargs)
   683             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
   676             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
   684             self.Bind(wx.EVT_SIZE, self.OnResizeWindow)
   677             self.Bind(wx.EVT_SIZE, self.OnResizeWindow)
   685             
   678             
   686             self.ParentWindow = window
   679             self.ParentWindow = window
   687             self.Highlight = HIGHLIGHT_NONE
   680             self.Highlight = HIGHLIGHT_NONE
   688             
   681             self.CanvasSize = SIZE_MAXI
   689             self.ChangeSizeButton = wx.lib.buttons.GenBitmapToggleButton(self, 
   682             
   690                     bitmap=GetBitmap("minimize_graph"), 
   683             self.Buttons = []
   691                     size=wx.Size(20, 20), style=wx.NO_BORDER)
   684             self.ContextualButtons = []
   692             self.ChangeSizeButton.SetBitmapSelected(GetBitmap("maximize_graph"))
   685             self.ContextualButtonsItem = None
   693             self.Bind(wx.EVT_BUTTON, self.OnChangeSizeButton, self.ChangeSizeButton)
   686             
   694             
   687             self.Buttons.append(
   695             self.CloseButton = wx.lib.buttons.GenBitmapButton(self, 
   688                 GraphButton(0, 0, GetBitmap("minimize_graph"), self.OnChangeSizeButton))
   696                     bitmap=GetBitmap("delete_graph"), 
   689             self.Buttons.append(
   697                     size=wx.Size(20, 20), style=wx.NO_BORDER)
   690                 GraphButton(0, 0, GetBitmap("delete_graph"), self.OnCloseButton))
   698             self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton)
       
   699             
   691             
   700             self.ShowButtons(False)
   692             self.ShowButtons(False)
   701             
   693             
   702         def SetHighlight(self, highlight):
   694         def SetHighlight(self, highlight):
   703             if self.Highlight != highlight:
   695             if self.Highlight != highlight:
   721             
   713             
   722             destDC = wx.MemoryDC()
   714             destDC = wx.MemoryDC()
   723             destDC.SelectObject(self.bitmap)
   715             destDC.SelectObject(self.bitmap)
   724             
   716             
   725             destGC = wx.GCDC(destDC)
   717             destGC = wx.GCDC(destDC)
   726                     
   718             
   727             destGC.BeginDrawing()
   719             destGC.BeginDrawing()
   728             destGC.SetPen(HIGHLIGHT_PEN)
   720             destGC.SetPen(HIGHLIGHT_PEN)
   729             destGC.SetBrush(HIGHLIGHT_BRUSH)
   721             destGC.SetBrush(HIGHLIGHT_BRUSH)
   730             if self.Highlight == HIGHLIGHT_BEFORE:
   722             if self.Highlight == HIGHLIGHT_BEFORE:
   731                 destGC.DrawLine(0, 0, width - 1, 0)
   723                 destGC.DrawLine(0, 0, width - 1, 0)
   735                 destGC.DrawRectangle(bbox.x, bbox.y, 
   727                 destGC.DrawRectangle(bbox.x, bbox.y, 
   736                                      bbox.width / 2, bbox.height)
   728                                      bbox.width / 2, bbox.height)
   737             elif self.Highlight == HIGHLIGHT_RIGHT:
   729             elif self.Highlight == HIGHLIGHT_RIGHT:
   738                 destGC.DrawRectangle(bbox.x + bbox.width / 2, bbox.y, 
   730                 destGC.DrawRectangle(bbox.x + bbox.width / 2, bbox.y, 
   739                                      bbox.width / 2, bbox.height)
   731                                      bbox.width / 2, bbox.height)
       
   732             
       
   733             for button in self.Buttons + self.ContextualButtons:
       
   734                 button.Draw(destGC)
   740             
   735             
   741             if self.ParentWindow.IsDragging():
   736             if self.ParentWindow.IsDragging():
   742                 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self.Parent)
   737                 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self.Parent)
   743                 if destBBox.width > 0 and destBBox.height > 0:
   738                 if destBBox.width > 0 and destBBox.height > 0:
   744                     srcPanel = self.ParentWindow.DraggingAxesPanel
   739                     srcPanel = self.ParentWindow.DraggingAxesPanel
   764             destGC.EndDrawing()
   759             destGC.EndDrawing()
   765             
   760             
   766             self._isDrawn = True
   761             self._isDrawn = True
   767             self.gui_repaint(drawDC=drawDC)
   762             self.gui_repaint(drawDC=drawDC)
   768         
   763         
       
   764         def HandleButtons(self, x, y):
       
   765             for button in self.Buttons + self.ContextualButtons:
       
   766                 if button.HitTest(x, y):
       
   767                     button.ProcessCallback()
       
   768                     return True
       
   769             return False
       
   770         
       
   771         def PopupContextualButtons(self, item, rect, style=wx.HORIZONTAL):
       
   772             if self.ContextualButtonsItem is not None and item != self.ContextualButtonsItem:
       
   773                 self.DismissContextualButtons()
       
   774             
       
   775             if self.ContextualButtonsItem is None:
       
   776                 self.ContextualButtonsItem = item
       
   777                 
       
   778                 if self.ContextualButtonsItem.IsForced():
       
   779                     self.ContextualButtons.append(
       
   780                         GraphButton(0, 0, GetBitmap("release"), self.OnReleaseButton))
       
   781                 else:
       
   782                     self.ContextualButtons.append(
       
   783                         GraphButton(0, 0, GetBitmap("force"), self.OnForceButton))
       
   784                 self.ContextualButtons.append(
       
   785                     GraphButton(0, 0, GetBitmap("delete_graph"), self.OnRemoveItemButton))
       
   786                 
       
   787                 offset = 0
       
   788                 buttons = self.ContextualButtons[:]
       
   789                 if style == wx.VERTICAL:
       
   790                      buttons.reverse()
       
   791                 for button in buttons:
       
   792                     w, h = button.GetSize()
       
   793                     if style == wx.HORIZONTAL:
       
   794                         x = rect.x + rect.width + offset
       
   795                         y = rect.y + (rect.height - h) / 2
       
   796                         offset += w
       
   797                     else:
       
   798                         x = rect.x + (rect.width - w ) / 2
       
   799                         y = rect.y - h - offset
       
   800                         offset += h
       
   801                     button.SetPosition(x, y)
       
   802             self.ParentWindow.ForceRefresh()
       
   803         
       
   804         def DismissContextualButtons(self):
       
   805             if self.ContextualButtonsItem is not None:
       
   806                 self.ContextualButtonsItem = None
       
   807                 self.ContextualButtons = []
       
   808             self.ParentWindow.ForceRefresh()
       
   809         
       
   810         def IsOverButton(self, x, y):
       
   811             for button in self.Buttons + self.ContextualButtons:
       
   812                 if button.HitTest(x, y):
       
   813                     return True
       
   814             return False
       
   815         
       
   816         def IsOverContextualButton(self, x, y):
       
   817             for button in self.ContextualButtons:
       
   818                 if button.HitTest(x, y):
       
   819                     return True
       
   820             return False
       
   821         
   769         def ShowButtons(self, show):
   822         def ShowButtons(self, show):
   770             if show:
   823             for button in self.Buttons:
   771                 self.ChangeSizeButton.Show()
   824                 if show:
   772                 self.CloseButton.Show()
   825                     button.Show()
   773             else:
   826                 else:
   774                 self.ChangeSizeButton.Hide()
   827                     button.Hide()
   775                 self.CloseButton.Hide()
   828             self.ParentWindow.ForceRefresh()
   776         
   829         
   777         def OnEnterWindow(self, event):
   830         def OnEnterWindow(self, event):
   778             self.ShowButtons(True)
   831             self.ShowButtons(True)
   779             event.Skip()
   832             event.Skip()
   780             
   833             
   784             if (x <= 0 or x >= width - 1 or
   837             if (x <= 0 or x >= width - 1 or
   785                 y <= 0 or y >= height - 1):
   838                 y <= 0 or y >= height - 1):
   786                 self.ShowButtons(False)
   839                 self.ShowButtons(False)
   787             event.Skip()
   840             event.Skip()
   788         
   841         
   789         def OnChangeSizeButton(self, event):
   842         def OnChangeSizeButton(self):
   790             if self.ChangeSizeButton.GetToggle():
   843             if self.CanvasSize == SIZE_MAXI:
       
   844                 self.CanvasSize = SIZE_MIDDLE
   791                 self.Parent.Minimize()
   845                 self.Parent.Minimize()
   792             else:
   846             else:
       
   847                 self.CanvasSize = SIZE_MAXI
   793                 self.Parent.Maximize()
   848                 self.Parent.Maximize()
   794             event.Skip()
   849         
   795         
   850         def OnCloseButton(self):
   796         def OnCloseButton(self, event):
   851             self.ParentWindow.DeleteValue(self.Parent)
   797             wx.CallAfter(self.ParentWindow.DeleteValue, self.Parent)
   852         
   798             event.Skip()
   853         def OnForceButton(self):
   799     
   854             wx.CallAfter(self.Parent.ForceValue, 
       
   855                          self.ContextualButtonsItem)
       
   856             self.DismissContextualButtons()
       
   857             
       
   858         def OnReleaseButton(self):
       
   859             wx.CallAfter(self.Parent.ReleaseValue, 
       
   860                          self.ContextualButtonsItem)
       
   861             self.DismissContextualButtons()
       
   862             
       
   863         def OnRemoveItemButton(self):            
       
   864             wx.CallAfter(self.ParentWindow.DeleteValue, self.Parent, 
       
   865                          self.ContextualButtonsItem)
       
   866             self.DismissContextualButtons()
       
   867             
   800         def OnResizeWindow(self, event):
   868         def OnResizeWindow(self, event):
   801             width, height = self.GetSize()
   869             width, height = self.GetSize()
   802             self.ChangeSizeButton.SetPosition(wx.Point(width - 50, 5))
   870             offset = 0
   803             self.CloseButton.SetPosition(wx.Point(width - 25, 5))
   871             buttons = self.Buttons[:]
       
   872             buttons.reverse()
       
   873             for button in buttons:
       
   874                 w, h = button.GetSize()
       
   875                 button.SetPosition(width - 5 - w - offset, 5)
       
   876                 offset += w
   804             event.Skip()
   877             event.Skip()
   805     
   878     
   806     class DebugVariableGraphic(DebugVariableViewer):
   879     class DebugVariableGraphic(DebugVariableViewer):
   807         
   880         
   808         def __init__(self, parent, window, items, graph_type):
   881         def __init__(self, parent, window, items, graph_type):
   815             self.ItemButtons = None
   888             self.ItemButtons = None
   816             
   889             
   817             main_sizer = wx.BoxSizer(wx.VERTICAL)
   890             main_sizer = wx.BoxSizer(wx.VERTICAL)
   818             
   891             
   819             self.Figure = matplotlib.figure.Figure(facecolor='w')
   892             self.Figure = matplotlib.figure.Figure(facecolor='w')
       
   893             self.Figure.subplotpars.update(top=0.95, left=0.1, bottom=0.1, right=0.95)
   820             
   894             
   821             self.Canvas = DraggingFigureCanvas(self, self.ParentWindow, -1, self.Figure)
   895             self.Canvas = DraggingFigureCanvas(self, self.ParentWindow, -1, self.Figure)
   822             self.Canvas.SetMinSize(wx.Size(200, 200))
   896             self.Canvas.SetMinSize(wx.Size(200, 200))
   823             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
   897             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
   824             self.Canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasMouseWheel)
   898             self.Canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasMouseWheel)
   830             main_sizer.AddWindow(self.Canvas, 1, flag=wx.GROW)
   904             main_sizer.AddWindow(self.Canvas, 1, flag=wx.GROW)
   831             self.SetSizer(main_sizer)
   905             self.SetSizer(main_sizer)
   832             
   906             
   833             self.ResetGraphics()
   907             self.ResetGraphics()
   834         
   908         
       
   909         def RefreshLabelsPosition(self, ratio):
       
   910             self.MaskLabel.set_position((0.05, 1.0 - 0.1 * ratio))
       
   911             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
   912                 num_item = len(self.Items)
       
   913                 for idx in xrange(num_item):
       
   914                     if not self.Is3DCanvas():
       
   915                         self.AxesLabels[idx].set_position((0.05, 1.0 - (0.1 + 0.075 * (idx + 1)) * ratio))
       
   916                     self.Labels[idx].set_position((0.95, 0.1 + (num_item - idx - 1) * 0.1 * ratio))
       
   917             else:
       
   918                 self.AxesLabels[0].set_position((0.1, 0.05 * ratio))
       
   919                 self.Labels[0].set_position((0.95, 0.05 * ratio))
       
   920                 self.AxesLabels[1].set_position((0.05, 0.1 * ratio))
       
   921                 self.Labels[1].set_position((0.05, 1.0 - 0.05 * ratio))
       
   922         
   835         def Minimize(self):
   923         def Minimize(self):
   836             self.Canvas.SetMinSize(wx.Size(200, 100))
   924             self.Canvas.SetMinSize(wx.Size(200, 100))
   837             self.Figure.subplotpars.update(bottom=0.20)
   925             self.Figure.subplotpars.update(top=0.9, bottom=0.2)
       
   926             self.RefreshLabelsPosition(2)
       
   927             self.Figure.subplots_adjust()
   838             self.ParentWindow.RefreshGraphicsSizer()
   928             self.ParentWindow.RefreshGraphicsSizer()
   839         
   929             
   840         def Maximize(self):
   930         def Maximize(self):
   841             self.Canvas.SetMinSize(wx.Size(200, 200))
   931             self.Canvas.SetMinSize(wx.Size(200, 200))
   842             self.Figure.subplotpars.update(bottom=0.1)
   932             self.Figure.subplotpars.update(top=0.95, bottom=0.1)
       
   933             self.RefreshLabelsPosition(1)
       
   934             self.Figure.subplots_adjust()
   843             self.ParentWindow.RefreshGraphicsSizer()
   935             self.ParentWindow.RefreshGraphicsSizer()
   844         
   936             
   845         def GetAxesBoundingBox(self, absolute=False):
   937         def GetAxesBoundingBox(self, absolute=False):
   846             bbox = self.Canvas.GetAxesBoundingBox()
   938             bbox = self.Canvas.GetAxesBoundingBox()
   847             if absolute:
   939             if absolute:
   848                 xw, yw = self.GetPosition()
   940                 xw, yw = self.GetPosition()
   849                 bbox.x += xw
   941                 bbox.x += xw
   850                 bbox.y += yw
   942                 bbox.y += yw
   851             return bbox
   943             return bbox
   852         
   944         
   853         def OnCanvasButtonPressed(self, event):
   945         def OnCanvasButtonPressed(self, event):
   854             if not self.Is3DCanvas():
   946             width, height = self.Canvas.GetSize()
   855                 width, height = self.Canvas.GetSize()
   947             x, y = event.x, height - event.y
   856                 x, y = event.x, height - event.y
   948             if not self.Canvas.IsOverButton(x, y) and not self.Is3DCanvas():
   857                 rect = self.GetAxesBoundingBox()
   949                 rect = self.GetAxesBoundingBox()
   858                 if rect.InsideXY(x, y):
   950                 if rect.InsideXY(x, y):
   859                     self.MouseStartPos = wx.Point(x, y)
   951                     self.MouseStartPos = wx.Point(x, y)
   860                 if self.Legend is not None:
       
   861                     texts = self.Legend.get_texts()
       
   862                 elif len(self.AxesLabels) > 0:
       
   863                     texts = self.AxesLabels
       
   864                 else:
       
   865                     texts = []
       
   866                 item_idx = None
   952                 item_idx = None
   867                 for i, t in enumerate(texts):
   953                 for i, t in enumerate(self.AxesLabels):
   868                     (x0, y0), (x1, y1) = t.get_window_extent().get_points()
   954                     (x0, y0), (x1, y1) = t.get_window_extent().get_points()
   869                     rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
   955                     rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
   870                     if rect.InsideXY(x, y):
   956                     if rect.InsideXY(x, y):
   871                         item_idx = i
   957                         item_idx = i
   872                         break
   958                         break
   873                 if item_idx is not None:
   959                 if item_idx is not None:
   874                     self.Canvas.ShowButtons(False)
   960                     self.Canvas.ShowButtons(False)
   875                     self.DismissButtons()
   961                     self.Canvas.DismissContextualButtons()
   876                     xw, yw = self.GetPosition()
   962                     xw, yw = self.GetPosition()
   877                     self.ParentWindow.StartDragNDrop(self, 
   963                     self.ParentWindow.StartDragNDrop(self, 
   878                         self.Items[item_idx], x + xw, y + yw, x + xw, y + yw)
   964                         self.Items[item_idx], x + xw, y + yw, x + xw, y + yw)
   879                 elif event.button == 1:
   965                 elif event.button == 1 and event.inaxes == self.Axes:
   880                     self.HandleCursorMove(event)
   966                     self.HandleCursorMove(event)
   881                 elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
   967                 elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
   882                     width, height = self.Canvas.GetSize()
   968                     width, height = self.Canvas.GetSize()
   883                     start_tick, end_tick = self.ParentWindow.GetRange()
   969                     start_tick, end_tick = self.ParentWindow.GetRange()
   884                     self.MouseStartPos = wx.Point(event.x, height - event.y)
   970                     self.MouseStartPos = wx.Point(x, y)
   885                     self.StartCursorTick = start_tick
   971                     self.StartCursorTick = start_tick
   886         
   972         
   887         def OnCanvasButtonReleased(self, event):
   973         def OnCanvasButtonReleased(self, event):
   888             if self.ParentWindow.IsDragging():
   974             if self.ParentWindow.IsDragging():
   889                 width, height = self.Canvas.GetSize()
   975                 width, height = self.Canvas.GetSize()
   893                     xw + event.x, 
   979                     xw + event.x, 
   894                     yw + height - event.y)
   980                     yw + height - event.y)
   895             else:
   981             else:
   896                 self.MouseStartPos = None
   982                 self.MouseStartPos = None
   897                 self.StartCursorTick = None
   983                 self.StartCursorTick = None
       
   984                 width, height = self.Canvas.GetSize()
       
   985                 self.Canvas.HandleButtons(event.x, height - event.y)
   898         
   986         
   899         def OnCanvasMotion(self, event):
   987         def OnCanvasMotion(self, event):
   900             width, height = self.Canvas.GetSize()
   988             width, height = self.Canvas.GetSize()
   901             if self.ParentWindow.IsDragging():
   989             if self.ParentWindow.IsDragging():
   902                 xw, yw = self.GetPosition()
   990                 xw, yw = self.GetPosition()
   919                     rect = self.GetAxesBoundingBox()
  1007                     rect = self.GetAxesBoundingBox()
   920                     self.ParentWindow.SetCanvasPosition(
  1008                     self.ParentWindow.SetCanvasPosition(
   921                         self.StartCursorTick + (self.MouseStartPos.x - event.x) *
  1009                         self.StartCursorTick + (self.MouseStartPos.x - event.x) *
   922                         (end_tick - start_tick) / rect.width)
  1010                         (end_tick - start_tick) / rect.width)
   923                 elif event.button is None:
  1011                 elif event.button is None:
   924                     if self.Legend is not None:
  1012                     if self.GraphType == GRAPH_PARALLEL:
   925                         labels = self.Legend.get_texts()
  1013                         orientation = [wx.HORIZONTAL] * len(self.AxesLabels)
   926                         texts = zip(labels, [wx.HORIZONTAL] * len(labels))
       
   927                     elif len(self.AxesLabels) > 0:
  1014                     elif len(self.AxesLabels) > 0:
   928                         texts = zip(self.AxesLabels, [wx.HORIZONTAL, wx.VERTICAL])
  1015                         orientation = [wx.HORIZONTAL, wx.VERTICAL]
   929                     else:
       
   930                         texts = []
       
   931                     item_idx = None
  1016                     item_idx = None
   932                     item_style = None
  1017                     item_style = None
   933                     for i, (t, style) in enumerate(texts):
  1018                     for i, (t, style) in enumerate(zip(self.AxesLabels, orientation)):
   934                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
  1019                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
   935                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
  1020                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
   936                         if rect.InsideXY(event.x, height - event.y):
  1021                         if rect.InsideXY(event.x, height - event.y):
   937                             item_idx = i
  1022                             item_idx = i
   938                             item_style = style
  1023                             item_style = style
   939                             break
  1024                             break
   940                     if item_idx is not None:
  1025                     if item_idx is not None:
   941                         self.PopupButtons(item_idx, rect, item_style)
  1026                         self.Canvas.PopupContextualButtons(self.Items[item_idx], rect, item_style)
   942                         return 
  1027                         return 
   943                     if self.ItemButtons is not None:
  1028                     if not self.Canvas.IsOverContextualButton(event.x, height - event.y):
   944                         self.DismissButtons()
  1029                         self.Canvas.DismissContextualButtons()
   945         
  1030         
   946         def OnCanvasDragging(self, x, y, refresh=True):
  1031         def OnCanvasDragging(self, x, y, refresh=True):
   947             width, height = self.Canvas.GetSize()
  1032             width, height = self.Canvas.GetSize()
   948             bbox = self.Canvas.GetAxesBoundingBox()
  1033             bbox = self.Canvas.GetAxesBoundingBox()
   949             if bbox.InsideXY(x, y) and not self.Is3DCanvas():
  1034             if bbox.InsideXY(x, y) and not self.Is3DCanvas():
   994             if cursor_tick is not None:
  1079             if cursor_tick is not None:
   995                 self.ParentWindow.SetCursorTick(cursor_tick)
  1080                 self.ParentWindow.SetCursorTick(cursor_tick)
   996         
  1081         
   997         def DoDragDrop(self, item_idx):
  1082         def DoDragDrop(self, item_idx):
   998             self.Canvas.ShowButtons(False)
  1083             self.Canvas.ShowButtons(False)
   999             self.DismissButtons()
  1084             self.Canvas.DismissContextualButtons()
  1000             data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move")))
  1085             data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move")))
  1001             dragSource = wx.DropSource(self.Canvas)
  1086             dragSource = wx.DropSource(self.Canvas)
  1002             dragSource.SetData(data)
  1087             dragSource.SetData(data)
  1003             dragSource.DoDragDrop()
  1088             dragSource.DoDragDrop()
  1004         
       
  1005         def PopupButtons(self, item_idx, rect, style=wx.HORIZONTAL):
       
  1006             item = self.Items[item_idx]
       
  1007             if self.ItemButtons is not None and item != self.ItemButtons.GetItem():
       
  1008                 self.DismissButtons()
       
  1009             if self.ItemButtons is None:
       
  1010                 
       
  1011                 self.ItemButtons = PopupWithButtons(self, self.ParentWindow, item, style)
       
  1012                 
       
  1013                 # Show the popup right below or above the button
       
  1014                 # depending on available screen space...
       
  1015                 w, h = self.ItemButtons.GetSize()
       
  1016                 if style == wx.HORIZONTAL:
       
  1017                     x = rect.x + rect.width
       
  1018                     y = rect.y + (rect.height - h) / 2
       
  1019                 else:
       
  1020                     x = rect.x + (rect.width - w ) / 2
       
  1021                     y = rect.y - h
       
  1022                 self.ItemButtons.SetPosition(self.ClientToScreen((x, y)))
       
  1023                 
       
  1024                 if wx.Platform == '__WXMSW__':
       
  1025                     self.ItemButtons.Popup()
       
  1026                 else:
       
  1027                     self.ItemButtons.Show()
       
  1028         
       
  1029         def DismissButtons(self):
       
  1030             if self.ItemButtons:
       
  1031                 if wx.Platform == '__WXMSW__':
       
  1032                     self.ItemButtons.Dismiss()
       
  1033                 else:
       
  1034                     self.ItemButtons.Destroy()
       
  1035                 self.ItemButtons = None
       
  1036         
  1089         
  1037         def OnAxesMotion(self, event):
  1090         def OnAxesMotion(self, event):
  1038             if self.Is3DCanvas():
  1091             if self.Is3DCanvas():
  1039                 current_time = gettime()
  1092                 current_time = gettime()
  1040                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
  1093                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
  1047                 self.Axes = self.Figure.gca(projection='3d')
  1100                 self.Axes = self.Figure.gca(projection='3d')
  1048                 self.Axes.set_color_cycle(['b'])
  1101                 self.Axes.set_color_cycle(['b'])
  1049                 self.LastMotionTime = gettime()
  1102                 self.LastMotionTime = gettime()
  1050                 setattr(self.Axes, "_on_move", self.OnAxesMotion)
  1103                 setattr(self.Axes, "_on_move", self.OnAxesMotion)
  1051                 self.Axes.mouse_init()
  1104                 self.Axes.mouse_init()
       
  1105                 self.Axes.tick_params(axis='z', labelsize='small')
  1052             else:
  1106             else:
  1053                 self.Axes = self.Figure.gca()
  1107                 self.Axes = self.Figure.gca()
  1054                 self.Figure.subplotpars.update(top=0.95, right=0.95)
  1108                 self.Axes.set_color_cycle(color_cycle)
       
  1109             self.Axes.tick_params(axis='x', labelsize='small')
       
  1110             self.Axes.tick_params(axis='y', labelsize='small')
  1055             self.Plots = []
  1111             self.Plots = []
  1056             self.VLine = None
  1112             self.VLine = None
  1057             self.HLine = None
  1113             self.HLine = None
  1058             self.Legend = None
       
  1059             self.Labels = []
  1114             self.Labels = []
  1060             self.AxesLabels = []
  1115             self.AxesLabels = []
       
  1116             if not self.Is3DCanvas():
       
  1117                 text_func = self.Axes.text
       
  1118             else:
       
  1119                 text_func = self.Axes.text2D
       
  1120             self.MaskLabel = text_func(0, 0, "", size='small', 
       
  1121                                        transform=self.Axes.transAxes)
  1061             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
  1122             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
  1062                 num_item = len(self.Items)
  1123                 num_item = len(self.Items)
  1063                 if not self.Is3DCanvas():
       
  1064                     text_func = self.Axes.text
       
  1065                 else:
       
  1066                     text_func = self.Axes.text2D
       
  1067                 for idx in xrange(num_item):
  1124                 for idx in xrange(num_item):
       
  1125                     if num_item == 1:
       
  1126                         color = 'k'
       
  1127                     else:
       
  1128                         color = color_cycle[idx % len(color_cycle)]
       
  1129                     if not self.Is3DCanvas():
       
  1130                         self.AxesLabels.append(
       
  1131                             text_func(0, 0, "", size='small', 
       
  1132                                       color=color,
       
  1133                                       transform=self.Axes.transAxes))
  1068                     self.Labels.append(
  1134                     self.Labels.append(
  1069                         text_func(0.95, 0.05 + (num_item - idx - 1) * 0.1, 
  1135                         text_func(0, 0, "", size='large', 
  1070                                   "", size='large', 
       
  1071                                   horizontalalignment='right',
  1136                                   horizontalalignment='right',
  1072                                   color=color_cycle[idx % len(color_cycle)],
  1137                                   color=color,
  1073                                   transform=self.Axes.transAxes))
  1138                                   transform=self.Axes.transAxes))
  1074             else:
  1139             else:
  1075                 self.AxesLabels.append(
  1140                 self.AxesLabels.append(
  1076                     self.Axes.text(0.1, 0.05, "", size='small',
  1141                     self.Axes.text(0, 0, "", size='small',
  1077                                    transform=self.Axes.transAxes))
  1142                                    transform=self.Axes.transAxes))
  1078                 self.Labels.append(
  1143                 self.Labels.append(
  1079                     self.Axes.text(0.95, 0.05, "", size='large',
  1144                     self.Axes.text(0, 0, "", size='large',
  1080                                    horizontalalignment='right',
  1145                                    horizontalalignment='right',
  1081                                    transform=self.Axes.transAxes))
  1146                                    transform=self.Axes.transAxes))
  1082                 self.AxesLabels.append(
  1147                 self.AxesLabels.append(
  1083                     self.Axes.text(0.05, 0.1, "", size='small',
  1148                     self.Axes.text(0, 0, "", size='small',
  1084                                    rotation='vertical',
  1149                                    rotation='vertical',
  1085                                    verticalalignment='bottom',
  1150                                    verticalalignment='bottom',
  1086                                    transform=self.Axes.transAxes))
  1151                                    transform=self.Axes.transAxes))
  1087                 self.Labels.append(
  1152                 self.Labels.append(
  1088                     self.Axes.text(0.05, 0.95, "", size='large',
  1153                     self.Axes.text(0, 0, "", size='large',
  1089                                    rotation='vertical',
  1154                                    rotation='vertical',
  1090                                    verticalalignment='top',
  1155                                    verticalalignment='top',
  1091                                    transform=self.Axes.transAxes))
  1156                                    transform=self.Axes.transAxes))
       
  1157             if self.Canvas.CanvasSize == SIZE_MAXI:
       
  1158                 self.RefreshLabelsPosition(1)
       
  1159             else:
       
  1160                 self.RefreshLabelsPosition(2)
  1092             
  1161             
  1093         def AddItem(self, item):
  1162         def AddItem(self, item):
  1094             DebugVariableViewer.AddItem(self, item)
  1163             DebugVariableViewer.AddItem(self, item)
  1095             self.ResetGraphics()
  1164             self.ResetGraphics()
  1096             
  1165             
  1148                     x_min, x_max = start_tick, end_tick
  1217                     x_min, x_max = start_tick, end_tick
  1149                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
  1218                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
  1150                     
  1219                     
  1151                     if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
  1220                     if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
  1152                         if self.VLine is None:
  1221                         if self.VLine is None:
  1153                             self.VLine = self.Axes.axvline(self.CursorTick, color='r')
  1222                             self.VLine = self.Axes.axvline(self.CursorTick, color=cursor_color)
  1154                         else:
  1223                         else:
  1155                             self.VLine.set_xdata((self.CursorTick, self.CursorTick))
  1224                             self.VLine.set_xdata((self.CursorTick, self.CursorTick))
  1156                         self.VLine.set_visible(True)
  1225                         self.VLine.set_visible(True)
  1157                     else:
  1226                     else:
  1158                         if self.VLine is not None:
  1227                         if self.VLine is not None:
  1182                                     x_data[:, 1][:length], 
  1251                                     x_data[:, 1][:length], 
  1183                                     y_data[:, 1][:length])
  1252                                     y_data[:, 1][:length])
  1184                         
  1253                         
  1185                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
  1254                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
  1186                             if self.VLine is None:
  1255                             if self.VLine is None:
  1187                                 self.VLine = self.Axes.axvline(x_cursor, color='r')
  1256                                 self.VLine = self.Axes.axvline(x_cursor, color=cursor_color)
  1188                             else:
  1257                             else:
  1189                                 self.VLine.set_xdata((x_cursor, x_cursor))
  1258                                 self.VLine.set_xdata((x_cursor, x_cursor))
  1190                             if self.HLine is None:
  1259                             if self.HLine is None:
  1191                                 self.HLine = self.Axes.axhline(y_cursor, color='r')
  1260                                 self.HLine = self.Axes.axhline(y_cursor, color=cursor_color)
  1192                             else:
  1261                             else:
  1193                                 self.HLine.set_ydata((y_cursor, y_cursor))
  1262                                 self.HLine.set_ydata((y_cursor, y_cursor))
  1194                             self.VLine.set_visible(True)
  1263                             self.VLine.set_visible(True)
  1195                             self.HLine.set_visible(True)
  1264                             self.HLine.set_visible(True)
  1196                         else:
  1265                         else:
  1216                                            {"zs": numpy.array([z_min, z_max])}]:
  1285                                            {"zs": numpy.array([z_min, z_max])}]:
  1217                                 for param, value in [("xs", numpy.array([x_cursor, x_cursor])),
  1286                                 for param, value in [("xs", numpy.array([x_cursor, x_cursor])),
  1218                                                      ("ys", numpy.array([y_cursor, y_cursor])),
  1287                                                      ("ys", numpy.array([y_cursor, y_cursor])),
  1219                                                      ("zs", numpy.array([z_cursor, z_cursor]))]:
  1288                                                      ("zs", numpy.array([z_cursor, z_cursor]))]:
  1220                                     kwargs.setdefault(param, value)
  1289                                     kwargs.setdefault(param, value)
  1221                                 kwargs["color"] = 'r'
  1290                                 kwargs["color"] = cursor_color
  1222                                 self.Axes.plot(**kwargs)
  1291                                 self.Axes.plot(**kwargs)
  1223                     
  1292                     
  1224                 self.Axes.set_xlim(x_min, x_max)
  1293                 self.Axes.set_xlim(x_min, x_max)
  1225                 self.Axes.set_ylim(y_min, y_max)
  1294                 self.Axes.set_ylim(y_min, y_max)
  1226             
  1295             
  1228             if self.CursorTick is not None:
  1297             if self.CursorTick is not None:
  1229                 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items])
  1298                 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items])
  1230             else:
  1299             else:
  1231                 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items])
  1300                 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items])
  1232             labels = [item.GetVariable(variable_name_mask) for item in self.Items]
  1301             labels = [item.GetVariable(variable_name_mask) for item in self.Items]
  1233             colors = map(lambda x: {True: 'b', False: 'k'}[x], forced)
  1302             styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced)
  1234             if self.GraphType == GRAPH_PARALLEL:
  1303             self.MaskLabel.set_text('.'.join(variable_name_mask))
  1235                 if self.Legend is None:
  1304             if self.Is3DCanvas():
  1236                     self.Legend = self.Axes.legend(self.Plots, labels, 
  1305                 for idx, label_func in enumerate([self.Axes.set_xlabel, 
  1237                         loc="upper left", frameon=False, prop={'size':'small'}, 
  1306                                                   self.Axes.set_ylabel,
  1238                         title = '.'.join(variable_name_mask))
  1307                                                   self.Axes.set_zlabel]):
  1239                     self.Legend.get_title().set_fontsize('small')
  1308                     label_func(labels[idx], fontdict={'size': 'small','color': color_cycle[idx]})
  1240                 for t, color in zip(self.Legend.get_texts(), colors):
  1309             else:
  1241                     t.set_color(color)
  1310                 for label, text in zip(self.AxesLabels, labels):
  1242             else:
  1311                     label.set_text(text)
  1243                 self.Legend = None
  1312             for label, value, style in zip(self.Labels, values, styles):
  1244                 if self.Is3DCanvas():
       
  1245                     self.Axes.set_xlabel(labels[0], fontdict={'size':'small','color':colors[0]})
       
  1246                     self.Axes.set_ylabel(labels[1], fontdict={'size':'small','color':colors[1]})
       
  1247                     self.Axes.set_zlabel(labels[2], fontdict={'size':'small','color':colors[2]})
       
  1248                 else:
       
  1249                     for label, text, color in zip(self.AxesLabels, labels, colors):
       
  1250                         label.set_text(text)
       
  1251                         label.set_color(color)
       
  1252             for label, value in zip(self.Labels, values):
       
  1253                 label.set_text(value)
  1313                 label.set_text(value)
       
  1314                 label.set_style(style)
  1254                         
  1315                         
  1255             self.Canvas.draw()
  1316             self.Canvas.draw()
  1256     
  1317     
  1257 class DebugVariablePanel(wx.Panel, DebugViewer):
  1318 class DebugVariablePanel(wx.Panel, DebugViewer):
  1258     
  1319