controls/DebugVariablePanel/DebugVariablePanel.py
changeset 1193 59c196884fec
parent 1192 d8783c0c7d80
child 1194 0cf48602ee24
equal deleted inserted replaced
1192:d8783c0c7d80 1193:59c196884fec
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 from types import TupleType, ListType, FloatType
       
    26 from time import time as gettime
       
    27 import math
       
    28 import numpy
       
    29 
       
    30 import wx
       
    31 import wx.lib.buttons
       
    32 
       
    33 try:
       
    34     import matplotlib
       
    35     matplotlib.use('WX')
       
    36     import matplotlib.pyplot
       
    37     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
       
    38     from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
       
    39     from matplotlib.backends.backend_agg import FigureCanvasAgg
       
    40     from mpl_toolkits.mplot3d import Axes3D
       
    41     color_cycle = ['r', 'b', 'g', 'm', 'y', 'k']
       
    42     cursor_color = '#800080'
       
    43     USE_MPL = True
       
    44 except:
       
    45     USE_MPL = False
       
    46 
       
    47 from graphics.DebugDataConsumer import DebugDataConsumer
       
    48 from editors.DebugViewer import DebugViewer, REFRESH_PERIOD
       
    49 from controls import CustomGrid, CustomTable
       
    50 from controls.DebugVariablePanel.DebugVariableItem import DebugVariableItem
       
    51 from dialogs.ForceVariableDialog import ForceVariableDialog
       
    52 from util.BitmapLibrary import GetBitmap
       
    53 
       
    54 def AppendMenu(parent, help, id, kind, text):
       
    55     parent.Append(help=help, id=id, kind=kind, text=text)
       
    56 
       
    57 def GetDebugVariablesTableColnames():
       
    58     _ = lambda x : x
       
    59     return [_("Variable"), _("Value")]
       
    60 
       
    61 class DebugVariableTable(CustomTable):
       
    62     
       
    63     def GetValue(self, row, col):
       
    64         if row < self.GetNumberRows():
       
    65             return self.GetValueByName(row, self.GetColLabelValue(col, False))
       
    66         return ""
       
    67     
       
    68     def SetValue(self, row, col, value):
       
    69         if col < len(self.colnames):
       
    70             self.SetValueByName(row, self.GetColLabelValue(col, False), value)
       
    71             
       
    72     def GetValueByName(self, row, colname):
       
    73         if row < self.GetNumberRows():
       
    74             if colname == "Variable":
       
    75                 return self.data[row].GetVariable()
       
    76             elif colname == "Value":
       
    77                 return self.data[row].GetValue()
       
    78         return ""
       
    79 
       
    80     def SetValueByName(self, row, colname, value):
       
    81         if row < self.GetNumberRows():
       
    82             if colname == "Variable":
       
    83                 self.data[row].SetVariable(value)
       
    84             elif colname == "Value":
       
    85                 self.data[row].SetValue(value)
       
    86     
       
    87     def IsForced(self, row):
       
    88         if row < self.GetNumberRows():
       
    89             return self.data[row].IsForced()
       
    90         return False
       
    91     
       
    92     def IsNumVariable(self, row):
       
    93         if row < self.GetNumberRows():
       
    94             return self.data[row].IsNumVariable()
       
    95         return False
       
    96     
       
    97     def _updateColAttrs(self, grid):
       
    98         """
       
    99         wx.grid.Grid -> update the column attributes to add the
       
   100         appropriate renderer given the column name.
       
   101 
       
   102         Otherwise default to the default renderer.
       
   103         """
       
   104         
       
   105         for row in range(self.GetNumberRows()):
       
   106             for col in range(self.GetNumberCols()):
       
   107                 colname = self.GetColLabelValue(col, False)
       
   108                 if colname == "Value":
       
   109                     if self.IsForced(row):
       
   110                         grid.SetCellTextColour(row, col, wx.BLUE)
       
   111                     else:
       
   112                         grid.SetCellTextColour(row, col, wx.BLACK)
       
   113                 grid.SetReadOnly(row, col, True)
       
   114             self.ResizeRow(grid, row)
       
   115                 
       
   116     def AppendItem(self, data):
       
   117         self.data.append(data)
       
   118     
       
   119     def InsertItem(self, idx, data):
       
   120         self.data.insert(idx, data)
       
   121     
       
   122     def RemoveItem(self, idx):
       
   123         self.data.pop(idx)
       
   124     
       
   125     def MoveItem(self, idx, new_idx):
       
   126         self.data.insert(new_idx, self.data.pop(idx))
       
   127         
       
   128     def GetItem(self, idx):
       
   129         return self.data[idx]
       
   130 
       
   131 class DebugVariableDropTarget(wx.TextDropTarget):
       
   132     
       
   133     def __init__(self, parent, control=None):
       
   134         wx.TextDropTarget.__init__(self)
       
   135         self.ParentWindow = parent
       
   136         self.ParentControl = control
       
   137     
       
   138     def __del__(self):
       
   139         self.ParentWindow = None
       
   140         self.ParentControl = None
       
   141     
       
   142     def OnDragOver(self, x, y, d):
       
   143         if not isinstance(self.ParentControl, CustomGrid):
       
   144             if self.ParentControl is not None:
       
   145                 self.ParentControl.OnMouseDragging(x, y)
       
   146             else:
       
   147                 self.ParentWindow.RefreshHighlight(x, y)
       
   148         return wx.TextDropTarget.OnDragOver(self, x, y, d)
       
   149         
       
   150     def OnDropText(self, x, y, data):
       
   151         message = None
       
   152         try:
       
   153             values = eval(data)
       
   154         except:
       
   155             message = _("Invalid value \"%s\" for debug variable")%data
       
   156             values = None
       
   157         if not isinstance(values, TupleType):
       
   158             message = _("Invalid value \"%s\" for debug variable")%data
       
   159             values = None
       
   160         
       
   161         if message is not None:
       
   162             wx.CallAfter(self.ShowMessage, message)
       
   163         elif values is not None and values[1] == "debug":
       
   164             if isinstance(self.ParentControl, CustomGrid):
       
   165                 x, y = self.ParentControl.CalcUnscrolledPosition(x, y)
       
   166                 row = self.ParentControl.YToRow(y - self.ParentControl.GetColLabelSize())
       
   167                 if row == wx.NOT_FOUND:
       
   168                     row = self.ParentWindow.Table.GetNumberRows()
       
   169                 self.ParentWindow.InsertValue(values[0], row, force=True)
       
   170             elif self.ParentControl is not None:
       
   171                 width, height = self.ParentControl.GetSize()
       
   172                 target_idx = self.ParentControl.GetIndex()
       
   173                 merge_type = GRAPH_PARALLEL
       
   174                 if isinstance(self.ParentControl, DebugVariableGraphic):
       
   175                     if self.ParentControl.Is3DCanvas():
       
   176                         if y > height / 2:
       
   177                             target_idx += 1
       
   178                         if len(values) > 1 and values[2] == "move":
       
   179                             self.ParentWindow.MoveValue(values[0], target_idx)
       
   180                         else:
       
   181                             self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   182                         
       
   183                     else:
       
   184                         rect = self.ParentControl.GetAxesBoundingBox()
       
   185                         if rect.InsideXY(x, y):
       
   186                             merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
       
   187                             if merge_rect.InsideXY(x, y):
       
   188                                 merge_type = GRAPH_ORTHOGONAL
       
   189                             wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True)
       
   190                         else:
       
   191                             if y > height / 2:
       
   192                                 target_idx += 1
       
   193                             if len(values) > 2 and values[2] == "move":
       
   194                                 self.ParentWindow.MoveValue(values[0], target_idx)
       
   195                             else:
       
   196                                 self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   197                 else:
       
   198                     if y > height / 2:
       
   199                         target_idx += 1
       
   200                     if len(values) > 2 and values[2] == "move":
       
   201                         self.ParentWindow.MoveValue(values[0], target_idx)
       
   202                     else:
       
   203                         self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   204                     
       
   205             elif len(values) > 2 and values[2] == "move":
       
   206                 self.ParentWindow.MoveValue(values[0])
       
   207             else:
       
   208                 self.ParentWindow.InsertValue(values[0], force=True)
       
   209     
       
   210     def OnLeave(self):
       
   211         if not isinstance(self.ParentControl, CustomGrid):
       
   212             self.ParentWindow.ResetHighlight()
       
   213         return wx.TextDropTarget.OnLeave(self)
       
   214     
       
   215     def ShowMessage(self, message):
       
   216         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
       
   217         dialog.ShowModal()
       
   218         dialog.Destroy()
       
   219 
       
   220 if USE_MPL:
       
   221     MILLISECOND = 1000000
       
   222     SECOND = 1000 * MILLISECOND
       
   223     MINUTE = 60 * SECOND
       
   224     HOUR = 60 * MINUTE
       
   225     DAY = 24 * HOUR
       
   226     
       
   227     ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
   228     RANGE_VALUES = \
       
   229         [("%dms" % i, i * MILLISECOND) for i in (10, 20, 50, 100, 200, 500)] + \
       
   230         [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
       
   231         [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
       
   232         [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
       
   233     
       
   234     GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
       
   235     
       
   236     SCROLLBAR_UNIT = 10
       
   237     
       
   238     #CANVAS_HIGHLIGHT_TYPES
       
   239     [HIGHLIGHT_NONE,
       
   240      HIGHLIGHT_BEFORE,
       
   241      HIGHLIGHT_AFTER,
       
   242      HIGHLIGHT_LEFT,
       
   243      HIGHLIGHT_RIGHT,
       
   244      HIGHLIGHT_RESIZE] = range(6)
       
   245     
       
   246     HIGHLIGHT_DROP_PEN = wx.Pen(wx.Colour(0, 128, 255))
       
   247     HIGHLIGHT_DROP_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128))
       
   248     HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200))
       
   249     HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200))
       
   250     
       
   251     #CANVAS_SIZE_TYPES
       
   252     [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200]
       
   253     
       
   254     DEFAULT_CANVAS_HEIGHT = 200.
       
   255     CANVAS_BORDER = (20., 10.)
       
   256     CANVAS_PADDING = 8.5
       
   257     VALUE_LABEL_HEIGHT = 17.
       
   258     AXES_LABEL_HEIGHT = 12.75
       
   259     
       
   260     def compute_mask(x, y):
       
   261         mask = []
       
   262         for xp, yp in zip(x, y):
       
   263             if xp == yp:
       
   264                 mask.append(xp)
       
   265             else:
       
   266                 mask.append("*")
       
   267         return mask
       
   268     
       
   269     def NextTick(variables):
       
   270         next_tick = None
       
   271         for item, data in variables:
       
   272             if len(data) > 0:
       
   273                 if next_tick is None:
       
   274                     next_tick = data[0][0]
       
   275                 else:
       
   276                     next_tick = min(next_tick, data[0][0])
       
   277         return next_tick
       
   278     
       
   279     def OrthogonalData(item, start_tick, end_tick):
       
   280         data = item.GetData(start_tick, end_tick)
       
   281         min_value, max_value = item.GetValueRange()
       
   282         if min_value is not None and max_value is not None:
       
   283             center = (min_value + max_value) / 2.
       
   284             range = max(1.0, max_value - min_value)
       
   285         else:
       
   286             center = 0.5
       
   287             range = 1.0
       
   288         return data, center - range * 0.55, center + range * 0.55
       
   289     
       
   290     class GraphButton():
       
   291         
       
   292         def __init__(self, x, y, bitmap, callback):
       
   293             self.Position = wx.Point(x, y)
       
   294             self.Bitmap = bitmap
       
   295             self.Shown = True
       
   296             self.Enabled = True
       
   297             self.Callback = callback
       
   298         
       
   299         def __del__(self):
       
   300             self.callback = None
       
   301         
       
   302         def GetSize(self):
       
   303             return self.Bitmap.GetSize()
       
   304         
       
   305         def SetPosition(self, x, y):
       
   306             self.Position = wx.Point(x, y)
       
   307         
       
   308         def Show(self):
       
   309             self.Shown = True
       
   310             
       
   311         def Hide(self):
       
   312             self.Shown = False
       
   313         
       
   314         def IsShown(self):
       
   315             return self.Shown
       
   316         
       
   317         def Enable(self):
       
   318             self.Enabled = True
       
   319         
       
   320         def Disable(self):
       
   321             self.Enabled = False
       
   322         
       
   323         def IsEnabled(self):
       
   324             return self.Enabled
       
   325         
       
   326         def HitTest(self, x, y):
       
   327             if self.Shown and self.Enabled:
       
   328                 w, h = self.Bitmap.GetSize()
       
   329                 rect = wx.Rect(self.Position.x, self.Position.y, w, h)
       
   330                 if rect.InsideXY(x, y):
       
   331                     return True
       
   332             return False
       
   333         
       
   334         def ProcessCallback(self):
       
   335             if self.Callback is not None:
       
   336                 wx.CallAfter(self.Callback)
       
   337                 
       
   338         def Draw(self, dc):
       
   339             if self.Shown and self.Enabled:
       
   340                 dc.DrawBitmap(self.Bitmap, self.Position.x, self.Position.y, True)
       
   341     
       
   342     class DebugVariableViewer:
       
   343         
       
   344         def __init__(self, window, items=[]):
       
   345             self.ParentWindow = window
       
   346             self.Items = items
       
   347             
       
   348             self.Highlight = HIGHLIGHT_NONE
       
   349             self.Buttons = []
       
   350         
       
   351         def __del__(self):
       
   352             self.ParentWindow = None
       
   353         
       
   354         def GetIndex(self):
       
   355             return self.ParentWindow.GetViewerIndex(self)
       
   356         
       
   357         def GetItem(self, variable):
       
   358             for item in self.Items:
       
   359                 if item.GetVariable() == variable:
       
   360                     return item
       
   361             return None
       
   362         
       
   363         def GetItems(self):
       
   364             return self.Items
       
   365         
       
   366         def GetVariables(self):
       
   367             if len(self.Items) > 1:
       
   368                 variables = [item.GetVariable() for item in self.Items]
       
   369                 if self.GraphType == GRAPH_ORTHOGONAL:
       
   370                     return tuple(variables)
       
   371                 return variables
       
   372             return self.Items[0].GetVariable()
       
   373         
       
   374         def AddItem(self, item):
       
   375             self.Items.append(item)
       
   376             
       
   377         def RemoveItem(self, item):
       
   378             if item in self.Items:
       
   379                 self.Items.remove(item)
       
   380             
       
   381         def Clear(self):
       
   382             for item in self.Items:
       
   383                 self.ParentWindow.RemoveDataConsumer(item)
       
   384             self.Items = []
       
   385             
       
   386         def IsEmpty(self):
       
   387             return len(self.Items) == 0
       
   388         
       
   389         def UnregisterObsoleteData(self):
       
   390             for item in self.Items[:]:
       
   391                 iec_path = item.GetVariable()
       
   392                 if self.ParentWindow.GetDataType(iec_path) is None:
       
   393                     self.ParentWindow.RemoveDataConsumer(item)
       
   394                     self.RemoveItem(item)
       
   395                 else:
       
   396                     self.ParentWindow.AddDataConsumer(iec_path.upper(), item)
       
   397                     item.RefreshVariableType()
       
   398             
       
   399         def ResetData(self):
       
   400             for item in self.Items:
       
   401                 item.ResetData()
       
   402         
       
   403         def RefreshViewer(self):
       
   404             pass
       
   405         
       
   406         def SetHighlight(self, highlight):
       
   407             if self.Highlight != highlight:
       
   408                 self.Highlight = highlight
       
   409                 return True
       
   410             return False
       
   411         
       
   412         def GetButtons(self):
       
   413             return self.Buttons
       
   414         
       
   415         def HandleButtons(self, x, y):
       
   416             for button in self.GetButtons():
       
   417                 if button.HitTest(x, y):
       
   418                     button.ProcessCallback()
       
   419                     return True
       
   420             return False
       
   421         
       
   422         def IsOverButton(self, x, y):
       
   423             for button in self.GetButtons():
       
   424                 if button.HitTest(x, y):
       
   425                     return True
       
   426             return False
       
   427         
       
   428         def ShowButtons(self, show):
       
   429             for button in self.Buttons:
       
   430                 if show:
       
   431                     button.Show()
       
   432                 else:
       
   433                     button.Hide()
       
   434             self.RefreshButtonsState()
       
   435             self.ParentWindow.ForceRefresh()
       
   436         
       
   437         def RefreshButtonsState(self, refresh_positions=False):
       
   438             if self:
       
   439                 width, height = self.GetSize()
       
   440                 if refresh_positions:
       
   441                     offset = 0
       
   442                     buttons = self.Buttons[:]
       
   443                     buttons.reverse()
       
   444                     for button in buttons:
       
   445                         w, h = button.GetSize()
       
   446                         button.SetPosition(width - 5 - w - offset, 5)
       
   447                         offset += w + 2
       
   448                     self.ParentWindow.ForceRefresh()
       
   449         
       
   450         def DrawCommonElements(self, dc, buttons=None):
       
   451             width, height = self.GetSize()
       
   452             
       
   453             dc.SetPen(HIGHLIGHT_DROP_PEN)
       
   454             dc.SetBrush(HIGHLIGHT_DROP_BRUSH)
       
   455             if self.Highlight in [HIGHLIGHT_BEFORE]:
       
   456                 dc.DrawLine(0, 1, width - 1, 1)
       
   457             elif self.Highlight in [HIGHLIGHT_AFTER]:
       
   458                 dc.DrawLine(0, height - 1, width - 1, height - 1)
       
   459             
       
   460             if buttons is None:
       
   461                 buttons = self.Buttons
       
   462             for button in buttons:
       
   463                 button.Draw(dc)
       
   464                 
       
   465             if self.ParentWindow.IsDragging():
       
   466                 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self)
       
   467                 srcPos = self.ParentWindow.GetDraggingAxesPosition(self)
       
   468                 if destBBox.width > 0 and destBBox.height > 0:
       
   469                     srcPanel = self.ParentWindow.DraggingAxesPanel
       
   470                     srcBBox = srcPanel.GetAxesBoundingBox()
       
   471                     
       
   472                     if destBBox.x == 0:
       
   473                         srcX = srcBBox.x - srcPos.x
       
   474                     else:
       
   475                         srcX = srcBBox.x
       
   476                     if destBBox.y == 0:
       
   477                         srcY = srcBBox.y - srcPos.y
       
   478                     else:
       
   479                         srcY = srcBBox.y
       
   480                     
       
   481                     srcBmp = _convert_agg_to_wx_bitmap(srcPanel.get_renderer(), None)
       
   482                     srcDC = wx.MemoryDC()
       
   483                     srcDC.SelectObject(srcBmp)
       
   484                     
       
   485                     dc.Blit(destBBox.x, destBBox.y, 
       
   486                             int(destBBox.width), int(destBBox.height), 
       
   487                             srcDC, srcX, srcY)
       
   488         
       
   489         def OnEnter(self, event):
       
   490             self.ShowButtons(True)
       
   491             event.Skip()
       
   492             
       
   493         def OnLeave(self, event):
       
   494             if self.Highlight != HIGHLIGHT_RESIZE or self.CanvasStartSize is None:
       
   495                 x, y = event.GetPosition()
       
   496                 width, height = self.GetSize()
       
   497                 if (x <= 0 or x >= width - 1 or
       
   498                     y <= 0 or y >= height - 1):
       
   499                     self.ShowButtons(False)
       
   500             event.Skip()
       
   501         
       
   502         def OnCloseButton(self):
       
   503             wx.CallAfter(self.ParentWindow.DeleteValue, self)
       
   504         
       
   505         def OnForceButton(self):
       
   506             wx.CallAfter(self.ForceValue, self.Items[0])
       
   507             
       
   508         def OnReleaseButton(self):
       
   509             wx.CallAfter(self.ReleaseValue, self.Items[0])
       
   510         
       
   511         def OnResizeWindow(self, event):
       
   512             wx.CallAfter(self.RefreshButtonsState, True)
       
   513             event.Skip()
       
   514         
       
   515         def OnMouseDragging(self, x, y):
       
   516             xw, yw = self.GetPosition()
       
   517             self.ParentWindow.RefreshHighlight(x + xw, y + yw)
       
   518         
       
   519         def OnDragging(self, x, y):
       
   520             width, height = self.GetSize()
       
   521             if y < height / 2:
       
   522                 if self.ParentWindow.IsViewerFirst(self):
       
   523                     self.SetHighlight(HIGHLIGHT_BEFORE)
       
   524                 else:
       
   525                     self.SetHighlight(HIGHLIGHT_NONE)
       
   526                     self.ParentWindow.HighlightPreviousViewer(self)
       
   527             else:
       
   528                 self.SetHighlight(HIGHLIGHT_AFTER)
       
   529         
       
   530         def OnEraseBackground(self, event):
       
   531             pass
       
   532         
       
   533         def OnResize(self, event):
       
   534             wx.CallAfter(self.RefreshButtonsState, True)
       
   535             event.Skip()
       
   536         
       
   537         def ForceValue(self, item):
       
   538             iec_path = item.GetVariable()
       
   539             iec_type = self.ParentWindow.GetDataType(iec_path)
       
   540             if iec_type is not None:
       
   541                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
   542                 if dialog.ShowModal() == wx.ID_OK:
       
   543                     self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue())
       
   544         
       
   545         def ReleaseValue(self, item):
       
   546             iec_path = item.GetVariable().upper()
       
   547             self.ParentWindow.ReleaseDataValue(iec_path)
       
   548     
       
   549     
       
   550     class DebugVariableText(DebugVariableViewer, wx.Panel):
       
   551         
       
   552         def __init__(self, parent, window, items=[]):
       
   553             DebugVariableViewer.__init__(self, window, items)
       
   554             
       
   555             wx.Panel.__init__(self, parent)
       
   556             self.SetBackgroundColour(wx.WHITE)
       
   557             self.SetDropTarget(DebugVariableDropTarget(window, self))
       
   558             self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
       
   559             self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
       
   560             self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
       
   561             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
       
   562             self.Bind(wx.EVT_SIZE, self.OnResize)
       
   563             self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
       
   564             self.Bind(wx.EVT_PAINT, self.OnPaint)
       
   565             
       
   566             self.SetMinSize(wx.Size(0, 25))
       
   567             
       
   568             self.Buttons.append(
       
   569                 GraphButton(0, 0, GetBitmap("force"), self.OnForceButton))
       
   570             self.Buttons.append(
       
   571                 GraphButton(0, 0, GetBitmap("release"), self.OnReleaseButton))
       
   572             self.Buttons.append(
       
   573                 GraphButton(0, 0, GetBitmap("delete_graph"), self.OnCloseButton))
       
   574             
       
   575             self.ShowButtons(False)
       
   576             
       
   577         def RefreshViewer(self):
       
   578             width, height = self.GetSize()
       
   579             bitmap = wx.EmptyBitmap(width, height)
       
   580             
       
   581             dc = wx.BufferedDC(wx.ClientDC(self), bitmap)
       
   582             dc.Clear()
       
   583             dc.BeginDrawing()
       
   584             
       
   585             gc = wx.GCDC(dc)
       
   586             
       
   587             item_name = self.Items[0].GetVariable(self.ParentWindow.GetVariableNameMask())
       
   588             w, h = gc.GetTextExtent(item_name)
       
   589             gc.DrawText(item_name, 20, (height - h) / 2)
       
   590             
       
   591             if self.Items[0].IsForced():
       
   592                 gc.SetTextForeground(wx.BLUE)
       
   593                 self.Buttons[0].Disable()
       
   594                 self.Buttons[1].Enable()
       
   595             else:
       
   596                 self.Buttons[1].Disable()
       
   597                 self.Buttons[0].Enable()
       
   598             self.RefreshButtonsState(True)
       
   599             
       
   600             item_value = self.Items[0].GetValue()
       
   601             w, h = gc.GetTextExtent(item_value)
       
   602             gc.DrawText(item_value, width - 40 - w, (height - h) / 2)
       
   603             
       
   604             self.DrawCommonElements(gc)
       
   605             
       
   606             gc.EndDrawing()
       
   607         
       
   608         def OnLeftDown(self, event):
       
   609             width, height = self.GetSize()
       
   610             item_name = self.Items[0].GetVariable(self.ParentWindow.GetVariableNameMask())
       
   611             w, h = self.GetTextExtent(item_name)
       
   612             x, y = event.GetPosition()
       
   613             rect = wx.Rect(20, (height - h) / 2, w, h)
       
   614             if rect.InsideXY(x, y):
       
   615                 data = wx.TextDataObject(str((self.Items[0].GetVariable(), "debug", "move")))
       
   616                 dragSource = wx.DropSource(self)
       
   617                 dragSource.SetData(data)
       
   618                 dragSource.DoDragDrop()
       
   619             else:
       
   620                 event.Skip()
       
   621         
       
   622         def OnLeftUp(self, event):
       
   623             x, y = event.GetPosition()
       
   624             wx.CallAfter(self.HandleButtons, x, y)
       
   625             event.Skip()
       
   626         
       
   627         def OnPaint(self, event):
       
   628             self.RefreshViewer()
       
   629             event.Skip()
       
   630     
       
   631     class DebugVariableGraphic(DebugVariableViewer, FigureCanvas):
       
   632         
       
   633         def __init__(self, parent, window, items, graph_type):
       
   634             DebugVariableViewer.__init__(self, window, items)
       
   635             
       
   636             self.CanvasSize = SIZE_MINI
       
   637             self.GraphType = graph_type
       
   638             self.CursorTick = None
       
   639             self.MouseStartPos = None
       
   640             self.StartCursorTick = None
       
   641             self.CanvasStartSize = None
       
   642             self.ContextualButtons = []
       
   643             self.ContextualButtonsItem = None
       
   644             
       
   645             self.Figure = matplotlib.figure.Figure(facecolor='w')
       
   646             self.Figure.subplotpars.update(top=0.95, left=0.1, bottom=0.1, right=0.95)
       
   647             
       
   648             FigureCanvas.__init__(self, parent, -1, self.Figure)
       
   649             self.SetWindowStyle(wx.WANTS_CHARS)
       
   650             self.SetBackgroundColour(wx.WHITE)
       
   651             self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
       
   652             self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
       
   653             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
       
   654             self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
       
   655             self.Bind(wx.EVT_SIZE, self.OnResize)
       
   656             
       
   657             canvas_size = self.GetCanvasMinSize()
       
   658             self.SetMinSize(canvas_size)
       
   659             self.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
       
   660             self.mpl_connect('button_press_event', self.OnCanvasButtonPressed)
       
   661             self.mpl_connect('motion_notify_event', self.OnCanvasMotion)
       
   662             self.mpl_connect('button_release_event', self.OnCanvasButtonReleased)
       
   663             self.mpl_connect('scroll_event', self.OnCanvasScroll)
       
   664             
       
   665             for size, bitmap in zip([SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI],
       
   666                                     ["minimize_graph", "middle_graph", "maximize_graph"]):
       
   667                 self.Buttons.append(GraphButton(0, 0, GetBitmap(bitmap), self.GetOnChangeSizeButton(size)))
       
   668             self.Buttons.append(
       
   669                 GraphButton(0, 0, GetBitmap("export_graph_mini"), self.OnExportGraphButton))
       
   670             self.Buttons.append(
       
   671                 GraphButton(0, 0, GetBitmap("delete_graph"), self.OnCloseButton))
       
   672             
       
   673             self.ResetGraphics()
       
   674             self.RefreshLabelsPosition(canvas_size.height)
       
   675             self.ShowButtons(False)
       
   676         
       
   677         def draw(self, drawDC=None):
       
   678             FigureCanvasAgg.draw(self)
       
   679     
       
   680             self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
       
   681             self.bitmap.UseAlpha() 
       
   682             width, height = self.GetSize()
       
   683             bbox = self.GetAxesBoundingBox()
       
   684             
       
   685             destDC = wx.MemoryDC()
       
   686             destDC.SelectObject(self.bitmap)
       
   687             
       
   688             destGC = wx.GCDC(destDC)
       
   689             
       
   690             destGC.BeginDrawing()
       
   691             if self.Highlight == HIGHLIGHT_RESIZE:
       
   692                 destGC.SetPen(HIGHLIGHT_RESIZE_PEN)
       
   693                 destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH)
       
   694                 destGC.DrawRectangle(0, height - 5, width, 5)
       
   695             else:
       
   696                 destGC.SetPen(HIGHLIGHT_DROP_PEN)
       
   697                 destGC.SetBrush(HIGHLIGHT_DROP_BRUSH)
       
   698                 if self.Highlight == HIGHLIGHT_LEFT:
       
   699                     destGC.DrawRectangle(bbox.x, bbox.y, 
       
   700                                          bbox.width / 2, bbox.height)
       
   701                 elif self.Highlight == HIGHLIGHT_RIGHT:
       
   702                     destGC.DrawRectangle(bbox.x + bbox.width / 2, bbox.y, 
       
   703                                          bbox.width / 2, bbox.height)
       
   704             
       
   705             self.DrawCommonElements(destGC, self.Buttons + self.ContextualButtons)
       
   706             
       
   707             destGC.EndDrawing()
       
   708             
       
   709             self._isDrawn = True
       
   710             self.gui_repaint(drawDC=drawDC)
       
   711         
       
   712         def GetButtons(self):
       
   713             return self.Buttons + self.ContextualButtons
       
   714         
       
   715         def PopupContextualButtons(self, item, rect, style=wx.RIGHT):
       
   716             if self.ContextualButtonsItem is not None and item != self.ContextualButtonsItem:
       
   717                 self.DismissContextualButtons()
       
   718             
       
   719             if self.ContextualButtonsItem is None:
       
   720                 self.ContextualButtonsItem = item
       
   721                 
       
   722                 if self.ContextualButtonsItem.IsForced():
       
   723                     self.ContextualButtons.append(
       
   724                         GraphButton(0, 0, GetBitmap("release"), self.OnReleaseButton))
       
   725                 self.ContextualButtons.append(
       
   726                     GraphButton(0, 0, GetBitmap("force"), self.OnForceButton))
       
   727                 self.ContextualButtons.append(
       
   728                     GraphButton(0, 0, GetBitmap("export_graph_mini"), self.OnExportItemGraphButton))
       
   729                 self.ContextualButtons.append(
       
   730                     GraphButton(0, 0, GetBitmap("delete_graph"), self.OnRemoveItemButton))
       
   731                 
       
   732                 offset = 0
       
   733                 buttons = self.ContextualButtons[:]
       
   734                 if style in [wx.TOP, wx.LEFT]:
       
   735                      buttons.reverse()
       
   736                 for button in buttons:
       
   737                     w, h = button.GetSize()
       
   738                     if style in [wx.LEFT, wx.RIGHT]:
       
   739                         if style == wx.LEFT:
       
   740                             x = rect.x - w - offset
       
   741                         else:
       
   742                             x = rect.x + rect.width + offset
       
   743                         y = rect.y + (rect.height - h) / 2
       
   744                         offset += w
       
   745                     else:
       
   746                         x = rect.x + (rect.width - w ) / 2
       
   747                         if style == wx.TOP:
       
   748                             y = rect.y - h - offset
       
   749                         else:
       
   750                             y = rect.y + rect.height + offset
       
   751                         offset += h
       
   752                     button.SetPosition(x, y)
       
   753                 self.ParentWindow.ForceRefresh()
       
   754         
       
   755         def DismissContextualButtons(self):
       
   756             if self.ContextualButtonsItem is not None:
       
   757                 self.ContextualButtonsItem = None
       
   758                 self.ContextualButtons = []
       
   759                 self.ParentWindow.ForceRefresh()
       
   760         
       
   761         def IsOverContextualButton(self, x, y):
       
   762             for button in self.ContextualButtons:
       
   763                 if button.HitTest(x, y):
       
   764                     return True
       
   765             return False
       
   766         
       
   767         def SetMinSize(self, size):
       
   768             wx.Window.SetMinSize(self, size)
       
   769             wx.CallAfter(self.RefreshButtonsState)
       
   770         
       
   771         def GetOnChangeSizeButton(self, size):
       
   772             def OnChangeSizeButton():
       
   773                 self.CanvasSize = size
       
   774                 self.SetCanvasSize(200, self.CanvasSize)
       
   775             return OnChangeSizeButton
       
   776         
       
   777         def OnExportGraphButton(self):
       
   778             self.ExportGraph()
       
   779         
       
   780         def OnForceButton(self):
       
   781             wx.CallAfter(self.ForceValue, 
       
   782                          self.ContextualButtonsItem)
       
   783             self.DismissContextualButtons()
       
   784             
       
   785         def OnReleaseButton(self):
       
   786             wx.CallAfter(self.ReleaseValue, 
       
   787                          self.ContextualButtonsItem)
       
   788             self.DismissContextualButtons()
       
   789         
       
   790         def OnExportItemGraphButton(self):
       
   791             wx.CallAfter(self.ExportGraph, 
       
   792                          self.ContextualButtonsItem)
       
   793             self.DismissContextualButtons()
       
   794             
       
   795         def OnRemoveItemButton(self):            
       
   796             wx.CallAfter(self.ParentWindow.DeleteValue, self, 
       
   797                          self.ContextualButtonsItem)
       
   798             self.DismissContextualButtons()
       
   799         
       
   800         def RefreshLabelsPosition(self, height):
       
   801             canvas_ratio = 1. / height
       
   802             graph_ratio = 1. / ((1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) * height)
       
   803             
       
   804             self.Figure.subplotpars.update(
       
   805                 top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, 
       
   806                 bottom= CANVAS_BORDER[0] * canvas_ratio)
       
   807             
       
   808             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
   809                 num_item = len(self.Items)
       
   810                 for idx in xrange(num_item):
       
   811                     if not self.Is3DCanvas():
       
   812                         self.AxesLabels[idx].set_position(
       
   813                             (0.05, 
       
   814                              1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio))
       
   815                     self.Labels[idx].set_position(
       
   816                         (0.95, 
       
   817                          CANVAS_PADDING * graph_ratio + 
       
   818                          (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
       
   819             else:
       
   820                 self.AxesLabels[0].set_position((0.1, CANVAS_PADDING * graph_ratio))
       
   821                 self.Labels[0].set_position((0.95, CANVAS_PADDING * graph_ratio))
       
   822                 self.AxesLabels[1].set_position((0.05, 2 * CANVAS_PADDING * graph_ratio))
       
   823                 self.Labels[1].set_position((0.05, 1.0 - CANVAS_PADDING * graph_ratio))
       
   824         
       
   825             self.Figure.subplots_adjust()
       
   826         
       
   827         def GetCanvasMinSize(self):
       
   828             return wx.Size(200, 
       
   829                            CANVAS_BORDER[0] + CANVAS_BORDER[1] + 
       
   830                            2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
       
   831         
       
   832         def SetCanvasSize(self, width, height):
       
   833             height = max(height, self.GetCanvasMinSize()[1])
       
   834             self.SetMinSize(wx.Size(width, height))
       
   835             self.RefreshLabelsPosition(height)
       
   836             self.RefreshButtonsState()
       
   837             self.ParentWindow.RefreshGraphicsSizer()
       
   838             
       
   839         def GetAxesBoundingBox(self, absolute=False):
       
   840             width, height = self.GetSize()
       
   841             ax, ay, aw, ah = self.figure.gca().get_position().bounds
       
   842             bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
       
   843                            aw * width + 2, ah * height + 1)
       
   844             if absolute:
       
   845                 xw, yw = self.GetPosition()
       
   846                 bbox.x += xw
       
   847                 bbox.y += yw
       
   848             return bbox
       
   849         
       
   850         def OnCanvasButtonPressed(self, event):
       
   851             width, height = self.GetSize()
       
   852             x, y = event.x, height - event.y
       
   853             if not self.IsOverButton(x, y):
       
   854                 if event.inaxes == self.Axes:
       
   855                     item_idx = None
       
   856                     for i, t in ([pair for pair in enumerate(self.AxesLabels)] + 
       
   857                                  [pair for pair in enumerate(self.Labels)]):
       
   858                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
       
   859                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
       
   860                         if rect.InsideXY(x, y):
       
   861                             item_idx = i
       
   862                             break
       
   863                     if item_idx is not None:
       
   864                         self.ShowButtons(False)
       
   865                         self.DismissContextualButtons()
       
   866                         xw, yw = self.GetPosition()
       
   867                         self.ParentWindow.StartDragNDrop(self, 
       
   868                             self.Items[item_idx], x + xw, y + yw, x + xw, y + yw)
       
   869                     elif not self.Is3DCanvas():
       
   870                         self.MouseStartPos = wx.Point(x, y)
       
   871                         if event.button == 1 and event.inaxes == self.Axes:
       
   872                             self.StartCursorTick = self.CursorTick
       
   873                             self.HandleCursorMove(event)
       
   874                         elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
       
   875                             width, height = self.GetSize()
       
   876                             start_tick, end_tick = self.ParentWindow.GetRange()
       
   877                             self.StartCursorTick = start_tick
       
   878                 
       
   879                 elif event.button == 1 and event.y <= 5:
       
   880                     self.MouseStartPos = wx.Point(x, y)
       
   881                     self.CanvasStartSize = self.GetSize()
       
   882         
       
   883         def OnCanvasButtonReleased(self, event):
       
   884             if self.ParentWindow.IsDragging():
       
   885                 width, height = self.GetSize()
       
   886                 xw, yw = self.GetPosition()
       
   887                 self.ParentWindow.StopDragNDrop(
       
   888                     self.ParentWindow.DraggingAxesPanel.Items[0].GetVariable(),
       
   889                     xw + event.x, 
       
   890                     yw + height - event.y)
       
   891             else:
       
   892                 self.MouseStartPos = None
       
   893                 self.StartCursorTick = None
       
   894                 self.CanvasStartSize = None
       
   895                 width, height = self.GetSize()
       
   896                 self.HandleButtons(event.x, height - event.y)
       
   897                 if event.y > 5 and self.SetHighlight(HIGHLIGHT_NONE):
       
   898                     self.SetCursor(wx.NullCursor)
       
   899                     self.ParentWindow.ForceRefresh()
       
   900         
       
   901         def OnCanvasMotion(self, event):
       
   902             width, height = self.GetSize()
       
   903             if self.ParentWindow.IsDragging():
       
   904                 xw, yw = self.GetPosition()
       
   905                 self.ParentWindow.MoveDragNDrop(
       
   906                     xw + event.x, 
       
   907                     yw + height - event.y)
       
   908             else:
       
   909                 if not self.Is3DCanvas():
       
   910                     if event.button == 1 and self.CanvasStartSize is None:
       
   911                         if event.inaxes == self.Axes:
       
   912                             if self.MouseStartPos is not None:
       
   913                                 self.HandleCursorMove(event)
       
   914                         elif self.MouseStartPos is not None and len(self.Items) == 1:
       
   915                             xw, yw = self.GetPosition()
       
   916                             self.ParentWindow.SetCursorTick(self.StartCursorTick)
       
   917                             self.ParentWindow.StartDragNDrop(self, 
       
   918                                 self.Items[0],
       
   919                                 event.x + xw, height - event.y + yw, 
       
   920                                 self.MouseStartPos.x + xw, self.MouseStartPos.y + yw)
       
   921                     elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
       
   922                         start_tick, end_tick = self.ParentWindow.GetRange()
       
   923                         rect = self.GetAxesBoundingBox()
       
   924                         self.ParentWindow.SetCanvasPosition(
       
   925                             self.StartCursorTick + (self.MouseStartPos.x - event.x) *
       
   926                             (end_tick - start_tick) / rect.width)    
       
   927                 
       
   928                 if event.button == 1 and self.CanvasStartSize is not None:
       
   929                     width, height = self.GetSize()
       
   930                     self.SetCanvasSize(width, 
       
   931                         self.CanvasStartSize.height + height - event.y - self.MouseStartPos.y)
       
   932                     
       
   933                 elif event.button in [None, "up", "down"]:
       
   934                     if self.GraphType == GRAPH_PARALLEL:
       
   935                         orientation = [wx.RIGHT] * len(self.AxesLabels) + [wx.LEFT] * len(self.Labels)
       
   936                     elif len(self.AxesLabels) > 0:
       
   937                         orientation = [wx.RIGHT, wx.TOP, wx.LEFT, wx.BOTTOM]
       
   938                     else:
       
   939                         orientation = [wx.LEFT] * len(self.Labels)
       
   940                     item_idx = None
       
   941                     item_style = None
       
   942                     for (i, t), style in zip([pair for pair in enumerate(self.AxesLabels)] + 
       
   943                                              [pair for pair in enumerate(self.Labels)], 
       
   944                                              orientation):
       
   945                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
       
   946                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
       
   947                         if rect.InsideXY(event.x, height - event.y):
       
   948                             item_idx = i
       
   949                             item_style = style
       
   950                             break
       
   951                     if item_idx is not None:
       
   952                         self.PopupContextualButtons(self.Items[item_idx], rect, item_style)
       
   953                         return 
       
   954                     if not self.IsOverContextualButton(event.x, height - event.y):
       
   955                         self.DismissContextualButtons()
       
   956                     
       
   957                     if event.y <= 5:
       
   958                         if self.SetHighlight(HIGHLIGHT_RESIZE):
       
   959                             self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS))
       
   960                             self.ParentWindow.ForceRefresh()
       
   961                     else:
       
   962                         if self.SetHighlight(HIGHLIGHT_NONE):
       
   963                             self.SetCursor(wx.NullCursor)
       
   964                             self.ParentWindow.ForceRefresh()
       
   965         
       
   966         def OnCanvasScroll(self, event):
       
   967             if event.inaxes is not None and event.guiEvent.ControlDown():
       
   968                 if self.GraphType == GRAPH_ORTHOGONAL:
       
   969                     start_tick, end_tick = self.ParentWindow.GetRange()
       
   970                     tick = (start_tick + end_tick) / 2.
       
   971                 else:
       
   972                     tick = event.xdata
       
   973                 self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
       
   974                 self.ParentWindow.VetoScrollEvent = True
       
   975         
       
   976         def OnDragging(self, x, y):
       
   977             width, height = self.GetSize()
       
   978             bbox = self.GetAxesBoundingBox()
       
   979             if bbox.InsideXY(x, y) and not self.Is3DCanvas():
       
   980                 rect = wx.Rect(bbox.x, bbox.y, bbox.width / 2, bbox.height)
       
   981                 if rect.InsideXY(x, y):
       
   982                     self.SetHighlight(HIGHLIGHT_LEFT)
       
   983                 else:
       
   984                     self.SetHighlight(HIGHLIGHT_RIGHT)
       
   985             elif y < height / 2:
       
   986                 if self.ParentWindow.IsViewerFirst(self):
       
   987                     self.SetHighlight(HIGHLIGHT_BEFORE)
       
   988                 else:
       
   989                     self.SetHighlight(HIGHLIGHT_NONE)
       
   990                     self.ParentWindow.HighlightPreviousViewer(self)
       
   991             else:
       
   992                 self.SetHighlight(HIGHLIGHT_AFTER)
       
   993         
       
   994         def OnLeave(self, event):
       
   995             if self.CanvasStartSize is None and self.SetHighlight(HIGHLIGHT_NONE):
       
   996                 self.SetCursor(wx.NullCursor)
       
   997                 self.ParentWindow.ForceRefresh()
       
   998             DebugVariableViewer.OnLeave(self, event)
       
   999         
       
  1000         KEY_CURSOR_INCREMENT = {
       
  1001             wx.WXK_LEFT: -1,
       
  1002             wx.WXK_RIGHT: 1,
       
  1003             wx.WXK_UP: -10,
       
  1004             wx.WXK_DOWN: 10}
       
  1005         def OnKeyDown(self, event):
       
  1006             if self.CursorTick is not None:
       
  1007                 self.ParentWindow.MoveCursorTick(
       
  1008                     self.KEY_CURSOR_INCREMENT.get(
       
  1009                       event.GetKeyCode(), 0))
       
  1010             event.Skip()
       
  1011         
       
  1012         def HandleCursorMove(self, event):
       
  1013             start_tick, end_tick = self.ParentWindow.GetRange()
       
  1014             cursor_tick = None
       
  1015             if self.GraphType == GRAPH_ORTHOGONAL:
       
  1016                 x_data = self.Items[0].GetData(start_tick, end_tick)
       
  1017                 y_data = self.Items[1].GetData(start_tick, end_tick)
       
  1018                 if len(x_data) > 0 and len(y_data) > 0:
       
  1019                     length = min(len(x_data), len(y_data))
       
  1020                     d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + (y_data[:length,1]-event.ydata) ** 2)
       
  1021                     cursor_tick = x_data[numpy.argmin(d), 0]
       
  1022             else:
       
  1023                 data = self.Items[0].GetData(start_tick, end_tick)
       
  1024                 if len(data) > 0:
       
  1025                     cursor_tick = data[numpy.argmin(numpy.abs(data[:,0] - event.xdata)), 0]
       
  1026             if cursor_tick is not None:
       
  1027                 self.ParentWindow.SetCursorTick(cursor_tick)
       
  1028         
       
  1029         def OnAxesMotion(self, event):
       
  1030             if self.Is3DCanvas():
       
  1031                 current_time = gettime()
       
  1032                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
  1033                     self.LastMotionTime = current_time
       
  1034                     Axes3D._on_move(self.Axes, event)
       
  1035         
       
  1036         def ResetGraphics(self):
       
  1037             self.Figure.clear()
       
  1038             if self.Is3DCanvas():
       
  1039                 self.Axes = self.Figure.gca(projection='3d')
       
  1040                 self.Axes.set_color_cycle(['b'])
       
  1041                 self.LastMotionTime = gettime()
       
  1042                 setattr(self.Axes, "_on_move", self.OnAxesMotion)
       
  1043                 self.Axes.mouse_init()
       
  1044                 self.Axes.tick_params(axis='z', labelsize='small')
       
  1045             else:
       
  1046                 self.Axes = self.Figure.gca()
       
  1047                 self.Axes.set_color_cycle(color_cycle)
       
  1048             self.Axes.tick_params(axis='x', labelsize='small')
       
  1049             self.Axes.tick_params(axis='y', labelsize='small')
       
  1050             self.Plots = []
       
  1051             self.VLine = None
       
  1052             self.HLine = None
       
  1053             self.Labels = []
       
  1054             self.AxesLabels = []
       
  1055             if not self.Is3DCanvas():
       
  1056                 text_func = self.Axes.text
       
  1057             else:
       
  1058                 text_func = self.Axes.text2D
       
  1059             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
  1060                 num_item = len(self.Items)
       
  1061                 for idx in xrange(num_item):
       
  1062                     if num_item == 1:
       
  1063                         color = 'k'
       
  1064                     else:
       
  1065                         color = color_cycle[idx % len(color_cycle)]
       
  1066                     if not self.Is3DCanvas():
       
  1067                         self.AxesLabels.append(
       
  1068                             text_func(0, 0, "", size='small',
       
  1069                                       verticalalignment='top', 
       
  1070                                       color=color,
       
  1071                                       transform=self.Axes.transAxes))
       
  1072                     self.Labels.append(
       
  1073                         text_func(0, 0, "", size='large', 
       
  1074                                   horizontalalignment='right',
       
  1075                                   color=color,
       
  1076                                   transform=self.Axes.transAxes))
       
  1077             else:
       
  1078                 self.AxesLabels.append(
       
  1079                     self.Axes.text(0, 0, "", size='small',
       
  1080                                    transform=self.Axes.transAxes))
       
  1081                 self.Labels.append(
       
  1082                     self.Axes.text(0, 0, "", size='large',
       
  1083                                    horizontalalignment='right',
       
  1084                                    transform=self.Axes.transAxes))
       
  1085                 self.AxesLabels.append(
       
  1086                     self.Axes.text(0, 0, "", size='small',
       
  1087                                    rotation='vertical',
       
  1088                                    verticalalignment='bottom',
       
  1089                                    transform=self.Axes.transAxes))
       
  1090                 self.Labels.append(
       
  1091                     self.Axes.text(0, 0, "", size='large',
       
  1092                                    rotation='vertical',
       
  1093                                    verticalalignment='top',
       
  1094                                    transform=self.Axes.transAxes))
       
  1095             width, height = self.GetSize()
       
  1096             self.RefreshLabelsPosition(height)
       
  1097             
       
  1098         def AddItem(self, item):
       
  1099             DebugVariableViewer.AddItem(self, item)
       
  1100             self.ResetGraphics()
       
  1101             
       
  1102         def RemoveItem(self, item):
       
  1103             DebugVariableViewer.RemoveItem(self, item)
       
  1104             if not self.IsEmpty():
       
  1105                 if len(self.Items) == 1:
       
  1106                     self.GraphType = GRAPH_PARALLEL
       
  1107                 self.ResetGraphics()
       
  1108         
       
  1109         def UnregisterObsoleteData(self):
       
  1110             DebugVariableViewer.UnregisterObsoleteData(self)
       
  1111             if not self.IsEmpty():
       
  1112                 self.ResetGraphics()
       
  1113         
       
  1114         def Is3DCanvas(self):
       
  1115             return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
       
  1116         
       
  1117         def SetCursorTick(self, cursor_tick):
       
  1118             self.CursorTick = cursor_tick
       
  1119             
       
  1120         def RefreshViewer(self, refresh_graphics=True):
       
  1121             
       
  1122             if refresh_graphics:
       
  1123                 start_tick, end_tick = self.ParentWindow.GetRange()
       
  1124                 
       
  1125                 if self.GraphType == GRAPH_PARALLEL:    
       
  1126                     min_value = max_value = None
       
  1127                     
       
  1128                     for idx, item in enumerate(self.Items):
       
  1129                         data = item.GetData(start_tick, end_tick)
       
  1130                         if data is not None:
       
  1131                             item_min_value, item_max_value = item.GetValueRange()
       
  1132                             if min_value is None:
       
  1133                                 min_value = item_min_value
       
  1134                             elif item_min_value is not None:
       
  1135                                 min_value = min(min_value, item_min_value)
       
  1136                             if max_value is None:
       
  1137                                 max_value = item_max_value
       
  1138                             elif item_max_value is not None:
       
  1139                                 max_value = max(max_value, item_max_value)
       
  1140                             
       
  1141                             if len(self.Plots) <= idx:
       
  1142                                 self.Plots.append(
       
  1143                                     self.Axes.plot(data[:, 0], data[:, 1])[0])
       
  1144                             else:
       
  1145                                 self.Plots[idx].set_data(data[:, 0], data[:, 1])
       
  1146                         
       
  1147                     if min_value is not None and max_value is not None:
       
  1148                         y_center = (min_value + max_value) / 2.
       
  1149                         y_range = max(1.0, max_value - min_value)
       
  1150                     else:
       
  1151                         y_center = 0.5
       
  1152                         y_range = 1.0
       
  1153                     x_min, x_max = start_tick, end_tick
       
  1154                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
       
  1155                     
       
  1156                     if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1157                         if self.VLine is None:
       
  1158                             self.VLine = self.Axes.axvline(self.CursorTick, color=cursor_color)
       
  1159                         else:
       
  1160                             self.VLine.set_xdata((self.CursorTick, self.CursorTick))
       
  1161                         self.VLine.set_visible(True)
       
  1162                     else:
       
  1163                         if self.VLine is not None:
       
  1164                             self.VLine.set_visible(False)
       
  1165                 else:
       
  1166                     min_start_tick = reduce(max, [item.GetData()[0, 0] 
       
  1167                                                   for item in self.Items
       
  1168                                                   if len(item.GetData()) > 0], 0)
       
  1169                     start_tick = max(start_tick, min_start_tick)
       
  1170                     end_tick = max(end_tick, min_start_tick)
       
  1171                     x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick)
       
  1172                     y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick)
       
  1173                     if self.CursorTick is not None:
       
  1174                         x_cursor, x_forced = self.Items[0].GetValue(self.CursorTick, raw=True)
       
  1175                         y_cursor, y_forced = self.Items[1].GetValue(self.CursorTick, raw=True)
       
  1176                     length = 0
       
  1177                     if x_data is not None and y_data is not None:  
       
  1178                         length = min(len(x_data), len(y_data))
       
  1179                     if len(self.Items) < 3:
       
  1180                         if x_data is not None and y_data is not None:
       
  1181                             if len(self.Plots) == 0:
       
  1182                                 self.Plots.append(
       
  1183                                     self.Axes.plot(x_data[:, 1][:length], 
       
  1184                                                    y_data[:, 1][:length])[0])
       
  1185                             else:
       
  1186                                 self.Plots[0].set_data(
       
  1187                                     x_data[:, 1][:length], 
       
  1188                                     y_data[:, 1][:length])
       
  1189                         
       
  1190                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1191                             if self.VLine is None:
       
  1192                                 self.VLine = self.Axes.axvline(x_cursor, color=cursor_color)
       
  1193                             else:
       
  1194                                 self.VLine.set_xdata((x_cursor, x_cursor))
       
  1195                             if self.HLine is None:
       
  1196                                 self.HLine = self.Axes.axhline(y_cursor, color=cursor_color)
       
  1197                             else:
       
  1198                                 self.HLine.set_ydata((y_cursor, y_cursor))
       
  1199                             self.VLine.set_visible(True)
       
  1200                             self.HLine.set_visible(True)
       
  1201                         else:
       
  1202                             if self.VLine is not None:
       
  1203                                 self.VLine.set_visible(False)
       
  1204                             if self.HLine is not None:
       
  1205                                 self.HLine.set_visible(False)
       
  1206                     else:
       
  1207                         while len(self.Axes.lines) > 0:
       
  1208                             self.Axes.lines.pop()
       
  1209                         z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick)
       
  1210                         if self.CursorTick is not None:
       
  1211                             z_cursor, z_forced = self.Items[2].GetValue(self.CursorTick, raw=True)
       
  1212                         if x_data is not None and y_data is not None and z_data is not None:
       
  1213                             length = min(length, len(z_data))
       
  1214                             self.Axes.plot(x_data[:, 1][:length],
       
  1215                                            y_data[:, 1][:length],
       
  1216                                            zs = z_data[:, 1][:length])
       
  1217                         self.Axes.set_zlim(z_min, z_max)
       
  1218                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1219                             for kwargs in [{"xs": numpy.array([x_min, x_max])},
       
  1220                                            {"ys": numpy.array([y_min, y_max])},
       
  1221                                            {"zs": numpy.array([z_min, z_max])}]:
       
  1222                                 for param, value in [("xs", numpy.array([x_cursor, x_cursor])),
       
  1223                                                      ("ys", numpy.array([y_cursor, y_cursor])),
       
  1224                                                      ("zs", numpy.array([z_cursor, z_cursor]))]:
       
  1225                                     kwargs.setdefault(param, value)
       
  1226                                 kwargs["color"] = cursor_color
       
  1227                                 self.Axes.plot(**kwargs)
       
  1228                     
       
  1229                 self.Axes.set_xlim(x_min, x_max)
       
  1230                 self.Axes.set_ylim(y_min, y_max)
       
  1231             
       
  1232             variable_name_mask = self.ParentWindow.GetVariableNameMask()
       
  1233             if self.CursorTick is not None:
       
  1234                 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items])
       
  1235             else:
       
  1236                 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items])
       
  1237             labels = [item.GetVariable(variable_name_mask) for item in self.Items]
       
  1238             styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced)
       
  1239             if self.Is3DCanvas():
       
  1240                 for idx, label_func in enumerate([self.Axes.set_xlabel, 
       
  1241                                                   self.Axes.set_ylabel,
       
  1242                                                   self.Axes.set_zlabel]):
       
  1243                     label_func(labels[idx], fontdict={'size': 'small','color': color_cycle[idx]})
       
  1244             else:
       
  1245                 for label, text in zip(self.AxesLabels, labels):
       
  1246                     label.set_text(text)
       
  1247             for label, value, style in zip(self.Labels, values, styles):
       
  1248                 label.set_text(value)
       
  1249                 label.set_style(style)
       
  1250             
       
  1251             self.draw()
       
  1252     
       
  1253         def ExportGraph(self, item=None):
       
  1254             if item is not None:
       
  1255                 variables = [(item, [entry for entry in item.GetData()])]
       
  1256             else:
       
  1257                 variables = [(item, [entry for entry in item.GetData()])
       
  1258                              for item in self.Items]
       
  1259             self.ParentWindow.CopyDataToClipboard(variables)
       
  1260     
       
  1261 class DebugVariablePanel(wx.Panel, DebugViewer):
       
  1262     
       
  1263     def __init__(self, parent, producer, window):
       
  1264         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
       
  1265         
       
  1266         self.ParentWindow = window
       
  1267         
       
  1268         self.HasNewData = False
       
  1269         self.Force = False
       
  1270         
       
  1271         if USE_MPL:
       
  1272             self.SetBackgroundColour(wx.WHITE)
       
  1273             
       
  1274             main_sizer = wx.BoxSizer(wx.VERTICAL)
       
  1275             
       
  1276             self.Ticks = numpy.array([])
       
  1277             self.StartTick = 0
       
  1278             self.Fixed = False
       
  1279             self.CursorTick = None
       
  1280             self.DraggingAxesPanel = None
       
  1281             self.DraggingAxesBoundingBox = None
       
  1282             self.DraggingAxesMousePos = None
       
  1283             self.VetoScrollEvent = False
       
  1284             self.VariableNameMask = []
       
  1285             
       
  1286             self.GraphicPanels = []
       
  1287             
       
  1288             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1289             main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
       
  1290             
       
  1291             range_label = wx.StaticText(self, label=_('Range:'))
       
  1292             graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
       
  1293             
       
  1294             self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
       
  1295             self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
       
  1296             graphics_button_sizer.AddWindow(self.CanvasRange, 1, 
       
  1297                   border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
       
  1298             
       
  1299             self.CanvasRange.Clear()
       
  1300             default_range_idx = 0
       
  1301             for idx, (text, value) in enumerate(RANGE_VALUES):
       
  1302                 self.CanvasRange.Append(text)
       
  1303                 if text == "1s":
       
  1304                     default_range_idx = idx
       
  1305             self.CanvasRange.SetSelection(default_range_idx)
       
  1306             
       
  1307             for name, bitmap, help in [
       
  1308                 ("CurrentButton", "current", _("Go to current value")),
       
  1309                 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
       
  1310                 button = wx.lib.buttons.GenBitmapButton(self, 
       
  1311                       bitmap=GetBitmap(bitmap), 
       
  1312                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
  1313                 button.SetToolTipString(help)
       
  1314                 setattr(self, name, button)
       
  1315                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
  1316                 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
  1317             
       
  1318             self.CanvasPosition = wx.ScrollBar(self, 
       
  1319                   size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
       
  1320             self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
       
  1321                   self.OnPositionChanging, self.CanvasPosition)
       
  1322             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
       
  1323                   self.OnPositionChanging, self.CanvasPosition)
       
  1324             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, 
       
  1325                   self.OnPositionChanging, self.CanvasPosition)
       
  1326             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
       
  1327                   self.OnPositionChanging, self.CanvasPosition)
       
  1328             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
       
  1329                   self.OnPositionChanging, self.CanvasPosition)
       
  1330             main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
       
  1331             
       
  1332             self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1333             main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
       
  1334             
       
  1335             self.TickLabel = wx.StaticText(self)
       
  1336             self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
       
  1337             
       
  1338             self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
       
  1339             self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
       
  1340             
       
  1341             self.TickTimeLabel = wx.StaticText(self)
       
  1342             self.TickSizer.AddWindow(self.TickTimeLabel)
       
  1343             
       
  1344             self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
       
  1345             self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
       
  1346             self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
       
  1347             self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground)
       
  1348             self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint)
       
  1349             self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
       
  1350             self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel)
       
  1351             
       
  1352             main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
       
  1353             
       
  1354             self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
       
  1355             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
       
  1356             
       
  1357         else:
       
  1358             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
  1359             main_sizer.AddGrowableCol(0)
       
  1360             main_sizer.AddGrowableRow(1)
       
  1361             
       
  1362             button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1363             main_sizer.AddSizer(button_sizer, border=5, 
       
  1364                   flag=wx.ALIGN_RIGHT|wx.ALL)
       
  1365             
       
  1366             for name, bitmap, help in [
       
  1367                     ("DeleteButton", "remove_element", _("Remove debug variable")),
       
  1368                     ("UpButton", "up", _("Move debug variable up")),
       
  1369                     ("DownButton", "down", _("Move debug variable down"))]:
       
  1370                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
  1371                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
  1372                 button.SetToolTipString(help)
       
  1373                 setattr(self, name, button)
       
  1374                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
  1375             
       
  1376             self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL)
       
  1377             self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
       
  1378             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
       
  1379                   self.OnVariablesGridCellRightClick)
       
  1380             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
       
  1381                   self.OnVariablesGridCellLeftClick)
       
  1382             main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
  1383         
       
  1384             self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
       
  1385             self.VariablesGrid.SetTable(self.Table)
       
  1386             self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
       
  1387                                            "Up": self.UpButton,
       
  1388                                            "Down": self.DownButton})
       
  1389         
       
  1390             def _AddVariable(new_row):
       
  1391                 return self.VariablesGrid.GetGridCursorRow()
       
  1392             setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
  1393         
       
  1394             def _DeleteVariable(row):
       
  1395                 item = self.Table.GetItem(row)
       
  1396                 self.RemoveDataConsumer(item)
       
  1397                 self.Table.RemoveItem(row)
       
  1398                 self.RefreshView()
       
  1399             setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
  1400             
       
  1401             def _MoveVariable(row, move):
       
  1402                 new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
       
  1403                 if new_row != row:
       
  1404                     self.Table.MoveItem(row, new_row)
       
  1405                     self.RefreshView()
       
  1406                 return new_row
       
  1407             setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
  1408             
       
  1409             self.VariablesGrid.SetRowLabelSize(0)
       
  1410             
       
  1411             self.GridColSizes = [200, 100]
       
  1412             
       
  1413             for col in range(self.Table.GetNumberCols()):
       
  1414                 attr = wx.grid.GridCellAttr()
       
  1415                 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
  1416                 self.VariablesGrid.SetColAttr(col, attr)
       
  1417                 self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
       
  1418             
       
  1419             self.Table.ResetView(self.VariablesGrid)
       
  1420             self.VariablesGrid.RefreshButtons()
       
  1421         
       
  1422         DebugViewer.__init__(self, producer, True)
       
  1423         
       
  1424         self.SetSizer(main_sizer)
       
  1425     
       
  1426     def SetTickTime(self, ticktime=0):
       
  1427         if USE_MPL:
       
  1428             self.Ticktime = ticktime
       
  1429             if self.Ticktime == 0:
       
  1430                 self.Ticktime = MILLISECOND
       
  1431             self.CurrentRange = RANGE_VALUES[
       
  1432                 self.CanvasRange.GetSelection()][1] / self.Ticktime
       
  1433     
       
  1434     def SetDataProducer(self, producer):
       
  1435         DebugViewer.SetDataProducer(self, producer)
       
  1436         
       
  1437         if USE_MPL:
       
  1438             if self.DataProducer is not None:
       
  1439                 self.SetTickTime(self.DataProducer.GetTicktime())
       
  1440     
       
  1441     def RefreshNewData(self, *args, **kwargs):
       
  1442         if self.HasNewData or self.Force:
       
  1443             self.HasNewData = False
       
  1444             self.RefreshView(only_values=True)
       
  1445         DebugViewer.RefreshNewData(self, *args, **kwargs)
       
  1446     
       
  1447     def NewDataAvailable(self, tick, *args, **kwargs):
       
  1448         if USE_MPL and tick is not None:
       
  1449             if len(self.Ticks) == 0:
       
  1450                 self.StartTick = tick 
       
  1451             self.Ticks = numpy.append(self.Ticks, [tick])
       
  1452             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
       
  1453                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
       
  1454             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
       
  1455                 self.Force = True
       
  1456         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
       
  1457     
       
  1458     def ForceRefresh(self):
       
  1459         self.Force = True
       
  1460         wx.CallAfter(self.NewDataAvailable, None, True)
       
  1461     
       
  1462     def RefreshGraphicsSizer(self):
       
  1463         self.GraphicsSizer.Clear()
       
  1464         
       
  1465         for panel in self.GraphicPanels:
       
  1466             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
       
  1467             
       
  1468         self.GraphicsSizer.Layout()
       
  1469         self.RefreshGraphicsWindowScrollbars()
       
  1470     
       
  1471     def SetCanvasPosition(self, tick):
       
  1472         tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
       
  1473         self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
       
  1474         self.Fixed = True
       
  1475         self.RefreshCanvasPosition()
       
  1476         self.ForceRefresh()
       
  1477     
       
  1478     def SetCursorTick(self, cursor_tick):
       
  1479         self.CursorTick = cursor_tick
       
  1480         self.Fixed = True
       
  1481         self.UpdateCursorTick() 
       
  1482     
       
  1483     def MoveCursorTick(self, move):
       
  1484         if self.CursorTick is not None:
       
  1485             cursor_tick = max(self.Ticks[0], 
       
  1486                           min(self.CursorTick + move, 
       
  1487                               self.Ticks[-1]))
       
  1488             cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
       
  1489             if self.Ticks[cursor_tick_idx] == self.CursorTick:
       
  1490                 cursor_tick_idx = max(0, 
       
  1491                                   min(cursor_tick_idx + abs(move) / move, 
       
  1492                                       len(self.Ticks) - 1))
       
  1493             self.CursorTick = self.Ticks[cursor_tick_idx]
       
  1494             self.StartTick = max(self.Ticks[
       
  1495                                 numpy.argmin(numpy.abs(self.Ticks - 
       
  1496                                         self.CursorTick + self.CurrentRange))],
       
  1497                              min(self.StartTick, self.CursorTick))
       
  1498             self.RefreshCanvasPosition()
       
  1499             self.UpdateCursorTick() 
       
  1500             
       
  1501     def ResetCursorTick(self):
       
  1502         self.CursorTick = None
       
  1503         self.UpdateCursorTick()
       
  1504     
       
  1505     def UpdateCursorTick(self):
       
  1506         for panel in self.GraphicPanels:
       
  1507             if isinstance(panel, DebugVariableGraphic):
       
  1508                 panel.SetCursorTick(self.CursorTick)
       
  1509         self.ForceRefresh()
       
  1510     
       
  1511     def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
       
  1512         if len(panel.GetItems()) > 1:
       
  1513             self.DraggingAxesPanel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1514             self.DraggingAxesPanel.SetCursorTick(self.CursorTick)
       
  1515             width, height = panel.GetSize()
       
  1516             self.DraggingAxesPanel.SetSize(wx.Size(width, height))
       
  1517             self.DraggingAxesPanel.ResetGraphics()
       
  1518             self.DraggingAxesPanel.SetPosition(wx.Point(0, -height))
       
  1519         else:
       
  1520             self.DraggingAxesPanel = panel
       
  1521         self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(absolute=True)
       
  1522         self.DraggingAxesMousePos = wx.Point(
       
  1523             x_mouse_start - self.DraggingAxesBoundingBox.x, 
       
  1524             y_mouse_start - self.DraggingAxesBoundingBox.y)
       
  1525         self.MoveDragNDrop(x_mouse, y_mouse)
       
  1526         
       
  1527     def MoveDragNDrop(self, x_mouse, y_mouse):
       
  1528         self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
       
  1529         self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
       
  1530         self.RefreshHighlight(x_mouse, y_mouse)
       
  1531     
       
  1532     def RefreshHighlight(self, x_mouse, y_mouse):
       
  1533         for idx, panel in enumerate(self.GraphicPanels):
       
  1534             x, y = panel.GetPosition()
       
  1535             width, height = panel.GetSize()
       
  1536             rect = wx.Rect(x, y, width, height)
       
  1537             if (rect.InsideXY(x_mouse, y_mouse) or 
       
  1538                 idx == 0 and y_mouse < 0 or
       
  1539                 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
       
  1540                 panel.OnDragging(x_mouse - x, y_mouse - y)
       
  1541             else:
       
  1542                 panel.SetHighlight(HIGHLIGHT_NONE)
       
  1543         if wx.Platform == "__WXMSW__":
       
  1544             self.RefreshView()
       
  1545         else:
       
  1546             self.ForceRefresh()
       
  1547     
       
  1548     def ResetHighlight(self):
       
  1549         for panel in self.GraphicPanels:
       
  1550             panel.SetHighlight(HIGHLIGHT_NONE)
       
  1551         if wx.Platform == "__WXMSW__":
       
  1552             self.RefreshView()
       
  1553         else:
       
  1554             self.ForceRefresh()
       
  1555     
       
  1556     def IsDragging(self):
       
  1557         return self.DraggingAxesPanel is not None
       
  1558     
       
  1559     def GetDraggingAxesClippingRegion(self, panel):
       
  1560         x, y = panel.GetPosition()
       
  1561         width, height = panel.GetSize()
       
  1562         bbox = wx.Rect(x, y, width, height)
       
  1563         bbox = bbox.Intersect(self.DraggingAxesBoundingBox)
       
  1564         bbox.x -= x
       
  1565         bbox.y -= y
       
  1566         return bbox
       
  1567     
       
  1568     def GetDraggingAxesPosition(self, panel):
       
  1569         x, y = panel.GetPosition()
       
  1570         return wx.Point(self.DraggingAxesBoundingBox.x - x,
       
  1571                         self.DraggingAxesBoundingBox.y - y)
       
  1572     
       
  1573     def StopDragNDrop(self, variable, x_mouse, y_mouse):
       
  1574         if self.DraggingAxesPanel not in self.GraphicPanels:
       
  1575             self.DraggingAxesPanel.Destroy()
       
  1576         self.DraggingAxesPanel = None
       
  1577         self.DraggingAxesBoundingBox = None
       
  1578         self.DraggingAxesMousePos = None
       
  1579         for idx, panel in enumerate(self.GraphicPanels):
       
  1580             panel.SetHighlight(HIGHLIGHT_NONE)
       
  1581             xw, yw = panel.GetPosition()
       
  1582             width, height = panel.GetSize()
       
  1583             bbox = wx.Rect(xw, yw, width, height)
       
  1584             if bbox.InsideXY(x_mouse, y_mouse):
       
  1585                 panel.ShowButtons(True)
       
  1586                 merge_type = GRAPH_PARALLEL
       
  1587                 if isinstance(panel, DebugVariableText) or panel.Is3DCanvas():
       
  1588                     if y_mouse > yw + height / 2:
       
  1589                         idx += 1
       
  1590                     wx.CallAfter(self.MoveValue, variable, idx)
       
  1591                 else:
       
  1592                     rect = panel.GetAxesBoundingBox(True)
       
  1593                     if rect.InsideXY(x_mouse, y_mouse):
       
  1594                         merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
       
  1595                         if merge_rect.InsideXY(x_mouse, y_mouse):
       
  1596                             merge_type = GRAPH_ORTHOGONAL
       
  1597                         wx.CallAfter(self.MergeGraphs, variable, idx, merge_type, force=True)
       
  1598                     else:
       
  1599                         if y_mouse > yw + height / 2:
       
  1600                             idx += 1
       
  1601                         wx.CallAfter(self.MoveValue, variable, idx)
       
  1602                 self.ForceRefresh()
       
  1603                 return 
       
  1604         width, height = self.GraphicsWindow.GetVirtualSize()
       
  1605         rect = wx.Rect(0, 0, width, height)
       
  1606         if rect.InsideXY(x_mouse, y_mouse):
       
  1607             wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels))
       
  1608         self.ForceRefresh()
       
  1609     
       
  1610     def RefreshView(self, only_values=False):
       
  1611         if USE_MPL:
       
  1612             self.RefreshCanvasPosition()
       
  1613             
       
  1614             width, height = self.GraphicsWindow.GetVirtualSize()
       
  1615             bitmap = wx.EmptyBitmap(width, height)
       
  1616             dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
       
  1617             dc.Clear()
       
  1618             dc.BeginDrawing()
       
  1619             if self.DraggingAxesPanel is not None:
       
  1620                 destBBox = self.DraggingAxesBoundingBox
       
  1621                 srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
       
  1622                 
       
  1623                 srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
       
  1624                 srcDC = wx.MemoryDC()
       
  1625                 srcDC.SelectObject(srcBmp)
       
  1626                     
       
  1627                 dc.Blit(destBBox.x, destBBox.y, 
       
  1628                         int(destBBox.width), int(destBBox.height), 
       
  1629                         srcDC, srcBBox.x, srcBBox.y)
       
  1630             dc.EndDrawing()
       
  1631             
       
  1632             if not self.Fixed or self.Force:
       
  1633                 self.Force = False
       
  1634                 refresh_graphics = True
       
  1635             else:
       
  1636                 refresh_graphics = False
       
  1637             
       
  1638             if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
       
  1639                 self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
       
  1640             for panel in self.GraphicPanels:
       
  1641                 if isinstance(panel, DebugVariableGraphic):
       
  1642                     panel.RefreshViewer(refresh_graphics)
       
  1643                 else:
       
  1644                     panel.RefreshViewer()
       
  1645             
       
  1646             if self.CursorTick is not None:
       
  1647                 tick = self.CursorTick
       
  1648             elif len(self.Ticks) > 0:
       
  1649                 tick = self.Ticks[-1]
       
  1650             else:
       
  1651                 tick = None
       
  1652             if tick is not None:
       
  1653                 self.TickLabel.SetLabel("Tick: %d" % tick)
       
  1654                 tick_duration = int(tick * self.Ticktime)
       
  1655                 not_null = False
       
  1656                 duration = ""
       
  1657                 for value, format in [(tick_duration / DAY, "%dd"),
       
  1658                                       ((tick_duration % DAY) / HOUR, "%dh"),
       
  1659                                       ((tick_duration % HOUR) / MINUTE, "%dm"),
       
  1660                                       ((tick_duration % MINUTE) / SECOND, "%ds")]:
       
  1661                     
       
  1662                     if value > 0 or not_null:
       
  1663                         duration += format % value
       
  1664                         not_null = True
       
  1665                 
       
  1666                 duration += "%gms" % (float(tick_duration % SECOND) / MILLISECOND) 
       
  1667                 self.TickTimeLabel.SetLabel("t: %s" % duration)
       
  1668             else:
       
  1669                 self.TickLabel.SetLabel("")
       
  1670                 self.TickTimeLabel.SetLabel("")
       
  1671             self.TickSizer.Layout()
       
  1672         else:
       
  1673             self.Freeze()
       
  1674             
       
  1675             if only_values:
       
  1676                 for col in xrange(self.Table.GetNumberCols()):
       
  1677                     if self.Table.GetColLabelValue(col, False) == "Value":
       
  1678                         for row in xrange(self.Table.GetNumberRows()):
       
  1679                             self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
       
  1680             else:
       
  1681                 self.Table.ResetView(self.VariablesGrid)
       
  1682             self.VariablesGrid.RefreshButtons()
       
  1683             
       
  1684             self.Thaw()
       
  1685         
       
  1686     def UnregisterObsoleteData(self):
       
  1687         self.SubscribeAllDataConsumers()
       
  1688         if USE_MPL:
       
  1689             if self.DataProducer is not None:
       
  1690                 if self.DataProducer is not None:
       
  1691                     self.SetTickTime(self.DataProducer.GetTicktime())
       
  1692             
       
  1693             for panel in self.GraphicPanels:
       
  1694                 panel.UnregisterObsoleteData()
       
  1695                 if panel.IsEmpty():
       
  1696                     if panel.HasCapture():
       
  1697                         panel.ReleaseMouse()
       
  1698                     self.GraphicPanels.remove(panel)
       
  1699                     panel.Destroy()
       
  1700             
       
  1701             self.ResetVariableNameMask()
       
  1702             self.RefreshGraphicsSizer()
       
  1703             self.ForceRefresh()
       
  1704             
       
  1705         else:
       
  1706             items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
       
  1707             items.reverse()
       
  1708             for idx, item in items:
       
  1709                 iec_path = item.GetVariable()
       
  1710                 if self.GetDataType(iec_path) is None:
       
  1711                     self.RemoveDataConsumer(item)
       
  1712                     self.Table.RemoveItem(idx)
       
  1713                 else:
       
  1714                     self.AddDataConsumer(iec_path.upper(), item)
       
  1715                     item.RefreshVariableType()
       
  1716             self.Freeze()
       
  1717             self.Table.ResetView(self.VariablesGrid)
       
  1718             self.VariablesGrid.RefreshButtons()
       
  1719             self.Thaw()
       
  1720     
       
  1721     def ResetView(self):
       
  1722         self.UnsubscribeAllDataConsumers()
       
  1723         if USE_MPL:
       
  1724             self.Fixed = False
       
  1725             for panel in self.GraphicPanels:
       
  1726                 panel.Destroy()
       
  1727             self.GraphicPanels = []
       
  1728             self.ResetVariableNameMask()
       
  1729             self.RefreshGraphicsSizer()
       
  1730         else:
       
  1731             self.Table.Empty()
       
  1732             self.Freeze()
       
  1733             self.Table.ResetView(self.VariablesGrid)
       
  1734             self.VariablesGrid.RefreshButtons()
       
  1735             self.Thaw()
       
  1736     
       
  1737     def RefreshCanvasPosition(self):
       
  1738         if len(self.Ticks) > 0:
       
  1739             pos = int(self.StartTick - self.Ticks[0])
       
  1740             range = int(self.Ticks[-1] - self.Ticks[0])
       
  1741         else:
       
  1742             pos = 0
       
  1743             range = 0
       
  1744         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
       
  1745     
       
  1746     def GetForceVariableMenuFunction(self, iec_path, item):
       
  1747         iec_type = self.GetDataType(iec_path)
       
  1748         def ForceVariableFunction(event):
       
  1749             if iec_type is not None:
       
  1750                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
  1751                 if dialog.ShowModal() == wx.ID_OK:
       
  1752                     self.ForceDataValue(iec_path.upper(), dialog.GetValue())
       
  1753         return ForceVariableFunction
       
  1754 
       
  1755     def GetReleaseVariableMenuFunction(self, iec_path):
       
  1756         def ReleaseVariableFunction(event):
       
  1757             self.ReleaseDataValue(iec_path)
       
  1758         return ReleaseVariableFunction
       
  1759     
       
  1760     def OnVariablesGridCellLeftClick(self, event):
       
  1761         if event.GetCol() == 0:
       
  1762             row = event.GetRow()
       
  1763             data = wx.TextDataObject(str((self.Table.GetValueByName(row, "Variable"), "debug")))
       
  1764             dragSource = wx.DropSource(self.VariablesGrid)
       
  1765             dragSource.SetData(data)
       
  1766             dragSource.DoDragDrop()
       
  1767         event.Skip()
       
  1768     
       
  1769     def OnVariablesGridCellRightClick(self, event):
       
  1770         row, col = event.GetRow(), event.GetCol()
       
  1771         if self.Table.GetColLabelValue(col, False) == "Value":
       
  1772             iec_path = self.Table.GetValueByName(row, "Variable").upper()
       
  1773 
       
  1774             menu = wx.Menu(title='')
       
  1775             
       
  1776             new_id = wx.NewId()
       
  1777             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
       
  1778             self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
       
  1779             
       
  1780             new_id = wx.NewId()
       
  1781             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
       
  1782             self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
       
  1783             
       
  1784             if self.Table.IsForced(row):
       
  1785                 menu.Enable(new_id, True)
       
  1786             else:
       
  1787                 menu.Enable(new_id, False)
       
  1788             
       
  1789             self.PopupMenu(menu)
       
  1790             
       
  1791             menu.Destroy()
       
  1792         event.Skip()
       
  1793     
       
  1794     def ChangeRange(self, dir, tick=None):
       
  1795         current_range = self.CurrentRange
       
  1796         current_range_idx = self.CanvasRange.GetSelection()
       
  1797         new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1))
       
  1798         if new_range_idx != current_range_idx:
       
  1799             self.CanvasRange.SetSelection(new_range_idx)
       
  1800             self.CurrentRange = RANGE_VALUES[new_range_idx][1] / self.Ticktime
       
  1801             if len(self.Ticks) > 0:
       
  1802                 if tick is None:
       
  1803                     tick = self.StartTick + self.CurrentRange / 2.
       
  1804                 new_start_tick = tick - (tick - self.StartTick) * self.CurrentRange / current_range 
       
  1805                 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
       
  1806                 self.Fixed = self.StartTick < self.Ticks[-1] - self.CurrentRange
       
  1807             self.ForceRefresh()
       
  1808     
       
  1809     def RefreshRange(self):
       
  1810         if len(self.Ticks) > 0:
       
  1811             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
       
  1812                 self.Fixed = False
       
  1813             if self.Fixed:
       
  1814                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
       
  1815             else:
       
  1816                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
  1817         self.ForceRefresh()
       
  1818     
       
  1819     def OnRangeChanged(self, event):
       
  1820         try:
       
  1821             self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime
       
  1822         except ValueError, e:
       
  1823             self.CanvasRange.SetValue(str(self.CurrentRange))
       
  1824         wx.CallAfter(self.RefreshRange)
       
  1825         event.Skip()
       
  1826     
       
  1827     def OnCurrentButton(self, event):
       
  1828         if len(self.Ticks) > 0:
       
  1829             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
  1830             self.Fixed = False
       
  1831             self.ResetCursorTick()
       
  1832         event.Skip()
       
  1833     
       
  1834     def CopyDataToClipboard(self, variables):
       
  1835         text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
       
  1836         next_tick = NextTick(variables)
       
  1837         while next_tick is not None:
       
  1838             values = []
       
  1839             for item, data in variables:
       
  1840                 if len(data) > 0:
       
  1841                     if next_tick == data[0][0]:
       
  1842                         var_type = item.GetVariableType()
       
  1843                         if var_type in ["STRING", "WSTRING"]:
       
  1844                             value = item.GetRawValue(int(data.pop(0)[2]))
       
  1845                             if var_type == "STRING":
       
  1846                                 values.append("'%s'" % value)
       
  1847                             else:
       
  1848                                 values.append('"%s"' % value)
       
  1849                         else:
       
  1850                             values.append("%.3f" % data.pop(0)[1])
       
  1851                     else:
       
  1852                         values.append("")
       
  1853                 else:
       
  1854                     values.append("")
       
  1855             text += "%d;%s;\n" % (next_tick, ";".join(values))
       
  1856             next_tick = NextTick(variables)
       
  1857         self.ParentWindow.SetCopyBuffer(text)
       
  1858     
       
  1859     def OnExportGraphButton(self, event):
       
  1860         variables = []
       
  1861         if USE_MPL:
       
  1862             items = []
       
  1863             for panel in self.GraphicPanels:
       
  1864                 items.extend(panel.GetItems())
       
  1865         else:
       
  1866             items = self.Table.GetData()
       
  1867         for item in items:
       
  1868             if item.IsNumVariable():
       
  1869                 variables.append((item, [entry for entry in item.GetData()]))
       
  1870         wx.CallAfter(self.CopyDataToClipboard, variables)
       
  1871         event.Skip()
       
  1872     
       
  1873     def OnPositionChanging(self, event):
       
  1874         if len(self.Ticks) > 0:
       
  1875             self.StartTick = self.Ticks[0] + event.GetPosition()
       
  1876             self.Fixed = True
       
  1877             self.ForceRefresh()
       
  1878         event.Skip()
       
  1879     
       
  1880     def GetRange(self):
       
  1881         return self.StartTick, self.StartTick + self.CurrentRange
       
  1882     
       
  1883     def GetViewerIndex(self, viewer):
       
  1884         if viewer in self.GraphicPanels:
       
  1885             return self.GraphicPanels.index(viewer)
       
  1886         return None
       
  1887     
       
  1888     def IsViewerFirst(self, viewer):
       
  1889         return viewer == self.GraphicPanels[0]
       
  1890     
       
  1891     def IsViewerLast(self, viewer):
       
  1892         return viewer == self.GraphicPanels[-1]
       
  1893     
       
  1894     def HighlightPreviousViewer(self, viewer):
       
  1895         if self.IsViewerFirst(viewer):
       
  1896             return
       
  1897         idx = self.GetViewerIndex(viewer)
       
  1898         if idx is None:
       
  1899             return
       
  1900         self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
       
  1901     
       
  1902     def ResetVariableNameMask(self):
       
  1903         items = []
       
  1904         for panel in self.GraphicPanels:
       
  1905             items.extend(panel.GetItems())
       
  1906         if len(items) > 1:
       
  1907             self.VariableNameMask = reduce(compute_mask,
       
  1908                 [item.GetVariable().split('.') for item in items])
       
  1909         elif len(items) > 0:
       
  1910             self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*']
       
  1911         else:
       
  1912             self.VariableNameMask = []
       
  1913         self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
       
  1914         self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
       
  1915             
       
  1916     def GetVariableNameMask(self):
       
  1917         return self.VariableNameMask
       
  1918     
       
  1919     def InsertValue(self, iec_path, idx = None, force=False):
       
  1920         if USE_MPL:
       
  1921             for panel in self.GraphicPanels:
       
  1922                 if panel.GetItem(iec_path) is not None:
       
  1923                     return
       
  1924             if idx is None:
       
  1925                 idx = len(self.GraphicPanels)
       
  1926         else:
       
  1927             for item in self.Table.GetData():
       
  1928                 if iec_path == item.GetVariable():
       
  1929                     return
       
  1930             if idx is None:
       
  1931                 idx = self.Table.GetNumberRows()
       
  1932         item = DebugVariableItem(self, iec_path, USE_MPL)
       
  1933         result = self.AddDataConsumer(iec_path.upper(), item)
       
  1934         if result is not None or force:
       
  1935             
       
  1936             if USE_MPL:
       
  1937                 if item.IsNumVariable():
       
  1938                     panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1939                     if self.CursorTick is not None:
       
  1940                         panel.SetCursorTick(self.CursorTick)
       
  1941                 else:
       
  1942                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  1943                 if idx is not None:
       
  1944                     self.GraphicPanels.insert(idx, panel)
       
  1945                 else:
       
  1946                     self.GraphicPanels.append(panel)
       
  1947                 self.ResetVariableNameMask()
       
  1948                 self.RefreshGraphicsSizer()
       
  1949                 self.ForceRefresh()
       
  1950             else:
       
  1951                 self.Table.InsertItem(idx, item)
       
  1952                 self.RefreshView()
       
  1953     
       
  1954     def MoveValue(self, iec_path, idx = None):
       
  1955         if idx is None:
       
  1956             idx = len(self.GraphicPanels)
       
  1957         source_panel = None
       
  1958         item = None
       
  1959         for panel in self.GraphicPanels:
       
  1960             item = panel.GetItem(iec_path)
       
  1961             if item is not None:
       
  1962                 source_panel = panel
       
  1963                 break
       
  1964         if source_panel is not None:
       
  1965             source_panel.RemoveItem(item)
       
  1966             source_size = source_panel.GetSize()
       
  1967             if source_panel.IsEmpty():
       
  1968                 if source_panel.HasCapture():
       
  1969                     source_panel.ReleaseMouse()
       
  1970                 self.GraphicPanels.remove(source_panel)
       
  1971                 source_panel.Destroy()
       
  1972             
       
  1973             if item.IsNumVariable():
       
  1974                 panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1975                 panel.SetCanvasSize(source_size.width, source_size.height)
       
  1976                 if self.CursorTick is not None:
       
  1977                     panel.SetCursorTick(self.CursorTick)
       
  1978             else:
       
  1979                 panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  1980             self.GraphicPanels.insert(idx, panel)
       
  1981             self.ResetVariableNameMask()
       
  1982             self.RefreshGraphicsSizer()
       
  1983             self.ForceRefresh()
       
  1984     
       
  1985     def MergeGraphs(self, source, target_idx, merge_type, force=False):
       
  1986         source_item = None
       
  1987         source_panel = None
       
  1988         for panel in self.GraphicPanels:
       
  1989             source_item = panel.GetItem(source)
       
  1990             if source_item is not None:
       
  1991                 source_panel = panel
       
  1992                 break
       
  1993         if source_item is None:
       
  1994             item = DebugVariableItem(self, source, USE_MPL)
       
  1995             if item.IsNumVariable():
       
  1996                 result = self.AddDataConsumer(source.upper(), item)
       
  1997                 if result is not None or force:
       
  1998                     source_item = item
       
  1999         if source_item is not None and source_item.IsNumVariable():
       
  2000             if source_panel is not None:
       
  2001                 source_size = source_panel.GetSize()
       
  2002             else:
       
  2003                 source_size = None
       
  2004             target_panel = self.GraphicPanels[target_idx]
       
  2005             graph_type = target_panel.GraphType
       
  2006             if target_panel != source_panel:
       
  2007                 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
       
  2008                     merge_type == GRAPH_ORTHOGONAL and 
       
  2009                     (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
       
  2010                      graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
       
  2011                     return
       
  2012                 
       
  2013                 if source_panel is not None:
       
  2014                     source_panel.RemoveItem(source_item)
       
  2015                     if source_panel.IsEmpty():
       
  2016                         if source_panel.HasCapture():
       
  2017                             source_panel.ReleaseMouse()
       
  2018                         self.GraphicPanels.remove(source_panel)
       
  2019                         source_panel.Destroy()
       
  2020             elif (merge_type != graph_type and len(target_panel.Items) == 2):
       
  2021                 target_panel.RemoveItem(source_item)
       
  2022             else:
       
  2023                 target_panel = None
       
  2024                 
       
  2025             if target_panel is not None:
       
  2026                 target_panel.AddItem(source_item)
       
  2027                 target_panel.GraphType = merge_type
       
  2028                 size = target_panel.GetSize()
       
  2029                 if merge_type == GRAPH_ORTHOGONAL:
       
  2030                     target_panel.SetCanvasSize(size.width, size.width)
       
  2031                 elif source_size is not None:
       
  2032                     target_panel.SetCanvasSize(size.width, size.height + source_size.height)
       
  2033                 else:
       
  2034                     target_panel.SetCanvasSize(size.width, size.height)
       
  2035                 target_panel.ResetGraphics()
       
  2036                 
       
  2037                 self.ResetVariableNameMask()
       
  2038                 self.RefreshGraphicsSizer()
       
  2039                 self.ForceRefresh()
       
  2040     
       
  2041     def DeleteValue(self, source_panel, item=None):
       
  2042         source_idx = self.GetViewerIndex(source_panel)
       
  2043         if source_idx is not None:
       
  2044             
       
  2045             if item is None:
       
  2046                 source_panel.Clear()
       
  2047                 source_panel.Destroy()
       
  2048                 self.GraphicPanels.remove(source_panel)
       
  2049                 self.ResetVariableNameMask()
       
  2050                 self.RefreshGraphicsSizer()
       
  2051             else:
       
  2052                 source_panel.RemoveItem(item)
       
  2053                 if source_panel.IsEmpty():
       
  2054                     source_panel.Destroy()
       
  2055                     self.GraphicPanels.remove(source_panel)
       
  2056                     self.ResetVariableNameMask()
       
  2057                     self.RefreshGraphicsSizer()
       
  2058             self.ForceRefresh()
       
  2059     
       
  2060     def ResetGraphicsValues(self):
       
  2061         if USE_MPL:
       
  2062             self.Ticks = numpy.array([])
       
  2063             self.StartTick = 0
       
  2064             self.Fixed = False
       
  2065             for panel in self.GraphicPanels:
       
  2066                 panel.ResetData()
       
  2067             self.ResetCursorTick()
       
  2068 
       
  2069     def RefreshGraphicsWindowScrollbars(self):
       
  2070         xstart, ystart = self.GraphicsWindow.GetViewStart()
       
  2071         window_size = self.GraphicsWindow.GetClientSize()
       
  2072         vwidth, vheight = self.GraphicsSizer.GetMinSize()
       
  2073         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
       
  2074         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
       
  2075         self.GraphicsWindow.Scroll(posx, posy)
       
  2076         self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
       
  2077                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
       
  2078     
       
  2079     def OnGraphicsWindowEraseBackground(self, event):
       
  2080         pass
       
  2081     
       
  2082     def OnGraphicsWindowPaint(self, event):
       
  2083         self.RefreshView()
       
  2084         event.Skip()
       
  2085     
       
  2086     def OnGraphicsWindowResize(self, event):
       
  2087         size = self.GetSize()
       
  2088         for panel in self.GraphicPanels:
       
  2089             panel_size = panel.GetSize()
       
  2090             if (isinstance(panel, DebugVariableGraphic) and 
       
  2091                 panel.GraphType == GRAPH_ORTHOGONAL and 
       
  2092                 panel_size.width == panel_size.height):
       
  2093                 panel.SetCanvasSize(size.width, size.width)
       
  2094         self.RefreshGraphicsWindowScrollbars()
       
  2095         event.Skip()
       
  2096 
       
  2097     def OnGraphicsWindowMouseWheel(self, event):
       
  2098         if self.VetoScrollEvent:
       
  2099             self.VetoScrollEvent = False
       
  2100         else:
       
  2101             event.Skip()