controls/DebugVariablePanel.py
changeset 924 5f2cc382be8c
parent 919 4a36e38e51d3
child 925 5f9dd88a605b
equal deleted inserted replaced
920:1499a4d225db 924:5f2cc382be8c
    24 
    24 
    25 from types import TupleType, ListType, FloatType
    25 from types import TupleType, ListType, FloatType
    26 from time import time as gettime
    26 from time import time as gettime
    27 import math
    27 import math
    28 import numpy
    28 import numpy
       
    29 import binascii
    29 
    30 
    30 import wx
    31 import wx
    31 import wx.lib.buttons
    32 import wx.lib.buttons
    32 
    33 
    33 try:
    34 try:
    49     parent.Append(help=help, id=id, kind=kind, text=text)
    50     parent.Append(help=help, id=id, kind=kind, text=text)
    50 
    51 
    51 def GetDebugVariablesTableColnames():
    52 def GetDebugVariablesTableColnames():
    52     _ = lambda x : x
    53     _ = lambda x : x
    53     return [_("Variable"), _("Value")]
    54     return [_("Variable"), _("Value")]
    54     
    55 
       
    56 CRC_SIZE = 8
       
    57 CRC_MASK = 2 ** CRC_SIZE - 1
       
    58 
    55 class VariableTableItem(DebugDataConsumer):
    59 class VariableTableItem(DebugDataConsumer):
    56     
    60     
    57     def __init__(self, parent, variable):
    61     def __init__(self, parent, variable):
    58         DebugDataConsumer.__init__(self)
    62         DebugDataConsumer.__init__(self)
    59         self.Parent = parent
    63         self.Parent = parent
    68         if self.Parent and self.Variable != variable:
    72         if self.Parent and self.Variable != variable:
    69             self.Variable = variable
    73             self.Variable = variable
    70             self.RefreshVariableType()
    74             self.RefreshVariableType()
    71             self.Parent.RefreshView()
    75             self.Parent.RefreshView()
    72     
    76     
    73     def GetVariable(self, max_size=None):
    77     def GetVariable(self, mask=None):
    74         variable = self.Variable
    78         variable = self.Variable
    75         if max_size is not None:
    79         if mask is not None:
    76             max_size = max(max_size, 10)
    80             parts = variable.split('.')
    77             if len(variable) > max_size:
    81             mask = mask + ['*'] * max(0, len(parts) - len(mask))
    78                 variable = "..." + variable[-(max_size - 3):]
    82             last = None
       
    83             variable = ""
       
    84             for m, v in zip(mask, parts):
       
    85                 if m == '*':
       
    86                     if last == '*':
       
    87                         variable += '.'
       
    88                     variable += v
       
    89                 elif last is None or last == '*':
       
    90                     variable += '..'
       
    91                 last = m
    79         return variable
    92         return variable
    80     
    93     
    81     def RefreshVariableType(self):
    94     def RefreshVariableType(self):
    82         self.VariableType = self.Parent.GetDataType(self.Variable)
    95         self.VariableType = self.Parent.GetDataType(self.Variable)
    83         if USE_MPL:
    96         if USE_MPL:
   103             else:
   116             else:
   104                 return self.Data[start_idx:]
   117                 return self.Data[start_idx:]
   105             
   118             
   106         return None
   119         return None
   107     
   120     
       
   121     def GetRawValue(self, idx):
       
   122         if self.VariableType in ["STRING", "WSTRING"] and idx < len(self.RawData):
       
   123             return self.RawData[idx][0]
       
   124         return ""
       
   125     
   108     def GetRange(self):
   126     def GetRange(self):
   109         return self.MinValue, self.MaxValue
   127         return self.MinValue, self.MaxValue
   110     
   128     
   111     def ResetData(self):
   129     def ResetData(self):
   112         if self.IsNumVariable():
   130         if self.IsNumVariable():
   113             self.Data = numpy.array([]).reshape(0, 2)
   131             self.Data = numpy.array([]).reshape(0, 3)
       
   132             if self.VariableType in ["STRING", "WSTRING"]:
       
   133                 self.RawData = []
   114             self.MinValue = None
   134             self.MinValue = None
   115             self.MaxValue = None
   135             self.MaxValue = None
   116         else:
   136         else:
   117             self.Data = None
   137             self.Data = None
   118     
   138     
   119     def IsNumVariable(self):
   139     def IsNumVariable(self):
   120         return self.Parent.IsNumType(self.VariableType)
   140         return (self.Parent.IsNumType(self.VariableType) or 
       
   141                 self.VariableType in ["STRING", "WSTRING"])
   121     
   142     
   122     def NewValue(self, tick, value, forced=False):
   143     def NewValue(self, tick, value, forced=False):
   123         if USE_MPL and self.IsNumVariable():
   144         if USE_MPL and self.IsNumVariable():
   124             num_value = {True:1., False:0.}.get(value, float(value))
   145             if self.VariableType in ["STRING", "WSTRING"]:
       
   146                 num_value = binascii.crc32(value) & CRC_MASK
       
   147             else:
       
   148                 num_value = float(value)
   125             if self.MinValue is None:
   149             if self.MinValue is None:
   126                 self.MinValue = num_value
   150                 self.MinValue = num_value
   127             else:
   151             else:
   128                 self.MinValue = min(self.MinValue, num_value)
   152                 self.MinValue = min(self.MinValue, num_value)
   129             if self.MaxValue is None:
   153             if self.MaxValue is None:
   130                 self.MaxValue = num_value
   154                 self.MaxValue = num_value
   131             else:
   155             else:
   132                 self.MaxValue = max(self.MaxValue, num_value)
   156                 self.MaxValue = max(self.MaxValue, num_value)
   133             self.Data = numpy.append(self.Data, [[float(tick), num_value]], axis=0)
   157             forced_value = float(forced)
       
   158             if self.VariableType in ["STRING", "WSTRING"]:
       
   159                 raw_data = (value, forced_value)
       
   160                 if len(self.RawData) == 0 or self.RawData[-1] != raw_data:
       
   161                     extra_value = len(self.RawData)
       
   162                     self.RawData.append(raw_data)
       
   163                 else:
       
   164                     extra_value = len(self.RawData) - 1
       
   165             else:
       
   166                 extra_value = forced_value
       
   167             self.Data = numpy.append(self.Data, [[float(tick), num_value, extra_value]], axis=0)
   134             self.Parent.HasNewData = True
   168             self.Parent.HasNewData = True
   135         DebugDataConsumer.NewValue(self, tick, value, forced)
   169         DebugDataConsumer.NewValue(self, tick, value, forced)
   136     
   170     
   137     def SetForced(self, forced):
   171     def SetForced(self, forced):
   138         if self.Forced != forced:
   172         if self.Forced != forced:
   145             value = value[1:-1]
   179             value = value[1:-1]
   146         if self.Value != value:
   180         if self.Value != value:
   147             self.Value = value
   181             self.Value = value
   148             self.Parent.HasNewData = True
   182             self.Parent.HasNewData = True
   149             
   183             
   150     def GetValue(self):
   184     def GetValue(self, tick=None, raw=False):
   151         if self.VariableType == "STRING":
   185         if tick is not None and self.IsNumVariable() and len(self.Data) > 0:
   152             return "'%s'" % self.Value
   186             idx = numpy.argmin(abs(self.Data[:, 0] - tick))
   153         elif self.VariableType == "WSTRING":
   187             if self.VariableType in ["STRING", "WSTRING"]:
   154             return "\"%s\"" % self.Value
   188                 value, forced = self.RawData[int(self.Data[idx, 2])]
   155         elif isinstance(self.Value, FloatType):
   189                 if not raw:
   156             return "%.6g" % self.Value
   190                     if self.VariableType == "STRING":
       
   191                         value = "'%s'" % value
       
   192                     else:
       
   193                         value = '"%s"' % value
       
   194                 return value, forced
       
   195             else:
       
   196                 value = self.Data[idx, 1]
       
   197                 if not raw and isinstance(value, FloatType):
       
   198                     value = "%.6g" % value
       
   199                 return value, self.IsForced()
       
   200         elif not raw:
       
   201             if self.VariableType == "STRING":
       
   202                 return "'%s'" % self.Value
       
   203             elif self.VariableType == "WSTRING":
       
   204                 return '"%s"' % self.Value
       
   205             elif isinstance(self.Value, FloatType):
       
   206                 return "%.6g" % self.Value
   157         return self.Value
   207         return self.Value
   158 
   208 
   159     def GetNearestData(self, tick, adjust):
   209     def GetNearestData(self, tick, adjust):
   160         if USE_MPL and self.IsNumVariable():
   210         if USE_MPL and self.IsNumVariable():
   161             ticks = self.Data[:, 0]
   211             ticks = self.Data[:, 0]
   305         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
   355         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
   306         dialog.ShowModal()
   356         dialog.ShowModal()
   307         dialog.Destroy()
   357         dialog.Destroy()
   308 
   358 
   309 if USE_MPL:
   359 if USE_MPL:
   310     SECOND = 1000000000
   360     MILLISECOND = 1000000
       
   361     SECOND = 1000 * MILLISECOND
   311     MINUTE = 60 * SECOND
   362     MINUTE = 60 * SECOND
   312     HOUR = 60 * MINUTE
   363     HOUR = 60 * MINUTE
       
   364     DAY = 24 * HOUR
   313     
   365     
   314     ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
   366     ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
   315     RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
   367     RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
   316     TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
   368     TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
   317                         [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
   369                         [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
   321     
   373     
   322     SCROLLBAR_UNIT = 10
   374     SCROLLBAR_UNIT = 10
   323     
   375     
   324     def NextTick(variables):
   376     def NextTick(variables):
   325         next_tick = None
   377         next_tick = None
   326         for var_name, data in variables:
   378         for item, data in variables:
   327             if len(data) > 0:
   379             if len(data) > 0:
   328                 if next_tick is None:
   380                 if next_tick is None:
   329                     next_tick = data[0][0]
   381                     next_tick = data[0][0]
   330                 else:
   382                 else:
   331                     next_tick = min(next_tick, data[0][0])
   383                     next_tick = min(next_tick, data[0][0])
   355             self.SetBackgroundColour(wx.WHITE)
   407             self.SetBackgroundColour(wx.WHITE)
   356             self.SetDropTarget(DebugVariableDropTarget(window, self))
   408             self.SetDropTarget(DebugVariableDropTarget(window, self))
   357             
   409             
   358             self.ParentWindow = window
   410             self.ParentWindow = window
   359             self.Items = items
   411             self.Items = items
       
   412             self.ResetVariableNameMask()
   360             
   413             
   361             self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
   414             self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
   362             self.AddViewer()
   415             self.AddViewer()
   363             self.AddButtons()
   416             self.AddButtons()
   364             self.MainSizer.AddGrowableCol(0)
   417             self.MainSizer.AddGrowableCol(0)
   386                 if self.GraphType == GRAPH_ORTHOGONAL:
   439                 if self.GraphType == GRAPH_ORTHOGONAL:
   387                     return tuple(variables)
   440                     return tuple(variables)
   388                 return variables
   441                 return variables
   389             return self.Items[0].GetVariable()
   442             return self.Items[0].GetVariable()
   390         
   443         
       
   444         def ResetVariableNameMask(self):
       
   445             if len(self.Items) > 1:
       
   446                 self.VariableNameMask = reduce(compute_mask,
       
   447                     [item.GetVariable().split('.') for item in self.Items])
       
   448             elif len(self.Items) > 0:
       
   449                 self.VariableNameMask = self.Items[0].GetVariable().split('.')[:-1] + ['*']
       
   450             else:
       
   451                 self.VariableNameMask = []
       
   452         
   391         def AddItem(self, item):
   453         def AddItem(self, item):
   392             self.Items.append(item)
   454             self.Items.append(item)
       
   455             self.ResetVariableNameMask()
   393     
   456     
   394         def RemoveItem(self, item):
   457         def RemoveItem(self, item):
   395             if item in self.Items:
   458             if item in self.Items:
   396                 self.Items.remove(item)
   459                 self.Items.remove(item)
       
   460             self.ResetVariableNameMask()
   397             
   461             
   398         def Clear(self):
   462         def Clear(self):
   399             for item in self.Items:
   463             for item in self.Items:
   400                 self.ParentWindow.RemoveDataConsumer(item)
   464                 self.ParentWindow.RemoveDataConsumer(item)
   401             self.Items = []
   465             self.Items = []
       
   466             self.ResetVariableNameMask()
   402         
   467         
   403         def IsEmpty(self):
   468         def IsEmpty(self):
   404             return len(self.Items) == 0
   469             return len(self.Items) == 0
   405         
   470         
   406         def UnregisterObsoleteData(self):
   471         def UnregisterObsoleteData(self):
   410                     self.ParentWindow.RemoveDataConsumer(item)
   475                     self.ParentWindow.RemoveDataConsumer(item)
   411                     self.RemoveItem(item)
   476                     self.RemoveItem(item)
   412                 else:
   477                 else:
   413                     self.ParentWindow.AddDataConsumer(iec_path, item)
   478                     self.ParentWindow.AddDataConsumer(iec_path, item)
   414                     item.RefreshVariableType()
   479                     item.RefreshVariableType()
       
   480             self.ResetVariableNameMask()
   415         
   481         
   416         def ResetData(self):
   482         def ResetData(self):
   417             for item in self.Items:
   483             for item in self.Items:
   418                 item.ResetData()
   484                 item.ResetData()
   419         
   485         
   426             else:
   492             else:
   427                 menu = wx.Menu(title='')
   493                 menu = wx.Menu(title='')
   428                 for item in self.Items:
   494                 for item in self.Items:
   429                     new_id = wx.NewId()
   495                     new_id = wx.NewId()
   430                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   496                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   431                         text=item.GetVariable(20))
   497                         text=item.GetVariable(self.VariableNameMask))
   432                     self.Bind(wx.EVT_MENU, 
   498                     self.Bind(wx.EVT_MENU, 
   433                         self.GetForceVariableMenuFunction(item),
   499                         self.GetForceVariableMenuFunction(item),
   434                         id=new_id)
   500                         id=new_id)
   435                 self.PopupMenu(menu)
   501                 self.PopupMenu(menu)
   436             event.Skip()
   502             event.Skip()
   441             else:
   507             else:
   442                 menu = wx.Menu(title='')
   508                 menu = wx.Menu(title='')
   443                 for item in self.Items:
   509                 for item in self.Items:
   444                     new_id = wx.NewId()
   510                     new_id = wx.NewId()
   445                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   511                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   446                         text=item.GetVariable(20))
   512                         text=item.GetVariable(self.VariableNameMask))
   447                     self.Bind(wx.EVT_MENU, 
   513                     self.Bind(wx.EVT_MENU, 
   448                         self.GetReleaseVariableMenuFunction(item),
   514                         self.GetReleaseVariableMenuFunction(item),
   449                         id=new_id)
   515                         id=new_id)
   450                 
   516                 
   451                 self.PopupMenu(menu)
   517                 self.PopupMenu(menu)
   457             else:
   523             else:
   458                 menu = wx.Menu(title='')
   524                 menu = wx.Menu(title='')
   459                 for item in self.Items:
   525                 for item in self.Items:
   460                     new_id = wx.NewId()
   526                     new_id = wx.NewId()
   461                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   527                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   462                         text=item.GetVariable(20))
   528                         text=item.GetVariable(self.VariableNameMask))
   463                     self.Bind(wx.EVT_MENU, 
   529                     self.Bind(wx.EVT_MENU, 
   464                         self.GetDeleteValueMenuFunction(item),
   530                         self.GetDeleteValueMenuFunction(item),
   465                         id=new_id)
   531                         id=new_id)
   466                 
   532                 
   467                 new_id = wx.NewId()
   533                 new_id = wx.NewId()
   543                 self.ValueLabel.SetForegroundColour(wx.BLUE)
   609                 self.ValueLabel.SetForegroundColour(wx.BLUE)
   544             else:
   610             else:
   545                 self.ValueLabel.SetForegroundColour(wx.BLACK)
   611                 self.ValueLabel.SetForegroundColour(wx.BLACK)
   546             self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1)
   612             self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1)
   547     
   613     
       
   614     def compute_mask(x, y):
       
   615         mask = []
       
   616         for xp, yp in zip(x, y):
       
   617             if xp == yp:
       
   618                 mask.append(xp)
       
   619             else:
       
   620                 mask.append("*")
       
   621         return mask
       
   622         
   548     class DebugVariableGraphic(DebugVariableViewer):
   623     class DebugVariableGraphic(DebugVariableViewer):
   549         
   624         
   550         def __init__(self, parent, window, items, graph_type):
   625         def __init__(self, parent, window, items, graph_type):
   551             DebugVariableViewer.__init__(self, parent, window, items)
   626             DebugVariableViewer.__init__(self, parent, window, items)
   552         
   627         
   553             self.GraphType = graph_type
   628             self.GraphType = graph_type
       
   629             self.CursorTick = None
   554             
   630             
   555             self.ResetGraphics()
   631             self.ResetGraphics()
   556         
   632         
   557         def AddViewer(self):
   633         def AddViewer(self):
   558             self.Figure = matplotlib.figure.Figure(facecolor='w')
   634             self.Figure = matplotlib.figure.Figure(facecolor='w')
   559             
   635             
   560             self.Canvas = FigureCanvas(self, -1, self.Figure)
   636             self.Canvas = FigureCanvas(self, -1, self.Figure)
   561             self.Canvas.SetMinSize(wx.Size(200, 200))
   637             self.Canvas.SetMinSize(wx.Size(200, 200))
   562             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
   638             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
       
   639             self.Canvas.mpl_connect('motion_notify_event', self.OnCanvasMotion)
   563             self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick)
   640             self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick)
   564             
   641             
   565             self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW)
   642             self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW)
   566         
   643         
   567         def AddButtons(self):
   644         def AddButtons(self):
   605                     self.DoDragDrop(item_idx)
   682                     self.DoDragDrop(item_idx)
   606                     return
   683                     return
   607             event.Skip()
   684             event.Skip()
   608         
   685         
   609         def DoDragDrop(self, item_idx):
   686         def DoDragDrop(self, item_idx):
       
   687             self.ParentWindow.ResetCursorTickRatio()
   610             data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move")))
   688             data = wx.TextDataObject(str((self.Items[item_idx].GetVariable(), "debug", "move")))
   611             dragSource = wx.DropSource(self.Canvas)
   689             dragSource = wx.DropSource(self.Canvas)
   612             dragSource.SetData(data)
   690             dragSource.SetData(data)
   613             dragSource.DoDragDrop()
   691             dragSource.DoDragDrop()
   614             
   692         
   615         def OnMotion(self, event):
   693         def OnAxesMotion(self, event):
   616             if self.Is3DCanvas():
   694             if self.Is3DCanvas():
   617                 current_time = gettime()
   695                 current_time = gettime()
   618                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
   696                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
   619                     self.LastMotionTime = current_time
   697                     self.LastMotionTime = current_time
   620                     Axes3D._on_move(self.Axes, event)
   698                     Axes3D._on_move(self.Axes, event)
   621         
   699         
       
   700         def OnCanvasMotion(self, event):
       
   701             if not self.Is3DCanvas():
       
   702                 if event.inaxes == self.Axes:
       
   703                     start_tick, end_tick = self.ParentWindow.GetRange()
       
   704                     cursor_tick_ratio = None
       
   705                     if self.GraphType == GRAPH_ORTHOGONAL:
       
   706                         x_data = self.Items[0].GetData(start_tick, end_tick)
       
   707                         y_data = self.Items[1].GetData(start_tick, end_tick)
       
   708                         if len(x_data) > 0 and len(y_data) > 0:
       
   709                             length = min(len(x_data), len(y_data))
       
   710                             d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + (y_data[:length,1]-event.ydata) ** 2)
       
   711                             cursor_tick_ratio = float(x_data[numpy.argmin(d), 0] - start_tick) / (end_tick - start_tick)
       
   712                     else:
       
   713                         data = self.Items[0].GetData(start_tick, end_tick)
       
   714                         if len(data) > 0:
       
   715                             x_min, x_max = self.Axes.get_xlim()
       
   716                             cursor_tick_ratio = float(event.xdata - x_min) / (x_max - x_min)
       
   717                     if cursor_tick_ratio is not None:
       
   718                         self.ParentWindow.SetCursorTickRatio(cursor_tick_ratio)
       
   719                 else:
       
   720                     self.ParentWindow.ResetCursorTickRatio()
       
   721         
   622         def OnSplitButton(self, event):
   722         def OnSplitButton(self, event):
   623             if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL:
   723             if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL:
   624                 wx.CallAfter(self.ParentWindow.SplitGraphs, self)
   724                 wx.CallAfter(self.ParentWindow.SplitGraphs, self)
   625             else:
   725             else:
   626                 menu = wx.Menu(title='')
   726                 menu = wx.Menu(title='')
   627                 for item in self.Items:
   727                 for item in self.Items:
   628                     new_id = wx.NewId()
   728                     new_id = wx.NewId()
   629                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   729                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
   630                         text=item.GetVariable(20))
   730                         text=item.GetVariable(self.VariableNameMask))
   631                     self.Bind(wx.EVT_MENU, 
   731                     self.Bind(wx.EVT_MENU, 
   632                         self.GetSplitGraphMenuFunction(item),
   732                         self.GetSplitGraphMenuFunction(item),
   633                         id=new_id)
   733                         id=new_id)
   634                 
   734                 
   635                 new_id = wx.NewId()
   735                 new_id = wx.NewId()
   643             self.Figure.clear()
   743             self.Figure.clear()
   644             if self.Is3DCanvas():
   744             if self.Is3DCanvas():
   645                 self.Axes = self.Figure.gca(projection='3d')
   745                 self.Axes = self.Figure.gca(projection='3d')
   646                 self.Axes.set_color_cycle(['b'])
   746                 self.Axes.set_color_cycle(['b'])
   647                 self.LastMotionTime = gettime()
   747                 self.LastMotionTime = gettime()
   648                 setattr(self.Axes, "_on_move", self.OnMotion)
   748                 setattr(self.Axes, "_on_move", self.OnAxesMotion)
   649                 self.Axes.mouse_init()
   749                 self.Axes.mouse_init()
   650             else:
   750             else:
   651                 self.Axes = self.Figure.gca()
   751                 self.Axes = self.Figure.gca()
   652                 if self.GraphType == GRAPH_ORTHOGONAL:
   752                 if self.GraphType == GRAPH_ORTHOGONAL:
   653                     self.Figure.subplotpars.update(bottom=0.15)
   753                     self.Figure.subplotpars.update(bottom=0.15)
       
   754             self.Axes.set_title('.'.join(self.VariableNameMask))
   654             self.Plots = []
   755             self.Plots = []
       
   756             self.VLine = None
       
   757             self.HLine = None
       
   758             self.TickLabel = None
   655             self.SplitButton.Enable(len(self.Items) > 1)
   759             self.SplitButton.Enable(len(self.Items) > 1)
   656             
   760         
   657         def AddItem(self, item):
   761         def AddItem(self, item):
   658             DebugVariableViewer.AddItem(self, item)
   762             DebugVariableViewer.AddItem(self, item)
   659             self.ResetGraphics()
   763             self.ResetGraphics()
   660             
   764             
   661         def RemoveItem(self, item):
   765         def RemoveItem(self, item):
   662             DebugVariableViewer.RemoveItem(self, item)
   766             DebugVariableViewer.RemoveItem(self, item)
   663             if not self.IsEmpty():
   767             if not self.IsEmpty():
       
   768                 self.ResetVariableNameMask()
   664                 self.ResetGraphics()
   769                 self.ResetGraphics()
   665         
   770         
   666         def UnregisterObsoleteData(self):
   771         def UnregisterObsoleteData(self):
   667             DebugVariableViewer.UnregisterObsoleteData(self)
   772             DebugVariableViewer.UnregisterObsoleteData(self)
   668             if not self.IsEmpty():
   773             if not self.IsEmpty():
       
   774                 self.ResetVariableNameMask()
   669                 self.ResetGraphics()
   775                 self.ResetGraphics()
   670         
   776         
   671         def Is3DCanvas(self):
   777         def Is3DCanvas(self):
   672             return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
   778             return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
   673         
   779         
       
   780         def SetCursorTick(self, cursor_tick):
       
   781             self.CursorTick = cursor_tick
       
   782             
   674         def Refresh(self, refresh_graphics=True):
   783         def Refresh(self, refresh_graphics=True):
   675             
   784             
   676             if refresh_graphics:
   785             if refresh_graphics:
   677                 start_tick, end_tick = self.ParentWindow.GetRange()
   786                 start_tick, end_tick = self.ParentWindow.GetRange()
   678                 
   787                 
   705                         y_center = 0.5
   814                         y_center = 0.5
   706                         y_range = 1.0
   815                         y_range = 1.0
   707                     x_min, x_max = start_tick, end_tick
   816                     x_min, x_max = start_tick, end_tick
   708                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
   817                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
   709                     
   818                     
       
   819                     if self.CursorTick is not None:
       
   820                         if self.VLine is None:
       
   821                             self.VLine = self.Axes.axvline(self.CursorTick, color='r')
       
   822                         else:
       
   823                             self.VLine.set_xdata((self.CursorTick, self.CursorTick))
       
   824                         self.VLine.set_visible(True)
       
   825                         tick_label = self.ParentWindow.GetTickLabel(self.CursorTick)
       
   826                         if self.TickLabel is None:
       
   827                             self.TickLabel = self.Axes.text(0.5, 0.05, tick_label, 
       
   828                                 size = 'small', transform = self.Axes.transAxes)
       
   829                         else:
       
   830                             self.TickLabel.set_text(tick_label)
       
   831                     else:
       
   832                         if self.VLine is not None:
       
   833                             self.VLine.set_visible(False)
       
   834                         if self.TickLabel is not None:
       
   835                             self.TickLabel.set_text("")
   710                 else:
   836                 else:
   711                     min_start_tick = reduce(max, [item.GetData()[0, 0] 
   837                     min_start_tick = reduce(max, [item.GetData()[0, 0] 
   712                                                   for item in self.Items
   838                                                   for item in self.Items
   713                                                   if len(item.GetData()) > 0], 0)
   839                                                   if len(item.GetData()) > 0], 0)
   714                     start_tick = max(start_tick, min_start_tick)
   840                     start_tick = max(start_tick, min_start_tick)
   715                     end_tick = max(end_tick, min_start_tick)
   841                     end_tick = max(end_tick, min_start_tick)
   716                     x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick)
   842                     x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick)
   717                     y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick)
   843                     y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick)
       
   844                     if self.CursorTick is not None:
       
   845                         x_cursor, x_forced = self.Items[0].GetValue(self.CursorTick, raw=True)
       
   846                         y_cursor, y_forced = self.Items[1].GetValue(self.CursorTick, raw=True)
   718                     length = 0
   847                     length = 0
   719                     if x_data is not None and y_data is not None:  
   848                     if x_data is not None and y_data is not None:  
   720                         length = min(len(x_data), len(y_data))
   849                         length = min(len(x_data), len(y_data))
   721                     if len(self.Items) < 3:
   850                     if len(self.Items) < 3:
   722                         if x_data is not None and y_data is not None:
   851                         if x_data is not None and y_data is not None:
   726                                                    y_data[:, 1][:length])[0])
   855                                                    y_data[:, 1][:length])[0])
   727                             else:
   856                             else:
   728                                 self.Plots[0].set_data(
   857                                 self.Plots[0].set_data(
   729                                     x_data[:, 1][:length], 
   858                                     x_data[:, 1][:length], 
   730                                     y_data[:, 1][:length])
   859                                     y_data[:, 1][:length])
       
   860                         
       
   861                         if self.CursorTick is not None:
       
   862                             if self.VLine is None:
       
   863                                 self.VLine = self.Axes.axvline(x_cursor, color='r')
       
   864                             else:
       
   865                                 self.VLine.set_xdata((x_cursor, x_cursor))
       
   866                             if self.HLine is None:
       
   867                                 self.HLine = self.Axes.axhline(y_cursor, color='r')
       
   868                             else:
       
   869                                 self.HLine.set_ydata((y_cursor, y_cursor))
       
   870                             self.VLine.set_visible(True)
       
   871                             self.HLine.set_visible(True)
       
   872                             tick_label = self.ParentWindow.GetTickLabel(self.CursorTick)
       
   873                             if self.TickLabel is None:
       
   874                                 self.TickLabel = self.Axes.text(0.05, 0.90, tick_label, 
       
   875                                     size = 'small', transform = self.Axes.transAxes)
       
   876                             else:
       
   877                                 self.TickLabel.set_text(tick_label)
       
   878                         else:
       
   879                             if self.VLine is not None:
       
   880                                 self.VLine.set_visible(False)
       
   881                             if self.HLine is not None:
       
   882                                 self.HLine.set_visible(False)
       
   883                             if self.TickLabel is not None:
       
   884                                 self.TickLabel.set_text("")
   731                     else:
   885                     else:
   732                         while len(self.Axes.lines) > 0:
   886                         while len(self.Axes.lines) > 0:
   733                             self.Axes.lines.pop()
   887                             self.Axes.lines.pop()
   734                         z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick)
   888                         z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick)
       
   889                         if self.CursorTick is not None:
       
   890                             z_cursor, z_forced = self.Items[2].GetValue(self.CursorTick, raw=True)
   735                         if x_data is not None and y_data is not None and z_data is not None:
   891                         if x_data is not None and y_data is not None and z_data is not None:
   736                             length = min(length, len(z_data))
   892                             length = min(length, len(z_data))
   737                             self.Axes.plot(x_data[:, 1][:length],
   893                             self.Axes.plot(x_data[:, 1][:length],
   738                                            y_data[:, 1][:length],
   894                                            y_data[:, 1][:length],
   739                                            zs = z_data[:, 1][:length])
   895                                            zs = z_data[:, 1][:length])
   740                         self.Axes.set_zlim(z_min, z_max)
   896                         self.Axes.set_zlim(z_min, z_max)
   741                 
   897                         if self.CursorTick is not None:
       
   898                             for kwargs in [{"xs": numpy.array([x_min, x_max])},
       
   899                                            {"ys": numpy.array([y_min, y_max])},
       
   900                                            {"zs": numpy.array([z_min, z_max])}]:
       
   901                                 for param, value in [("xs", numpy.array([x_cursor, x_cursor])),
       
   902                                                      ("ys", numpy.array([y_cursor, y_cursor])),
       
   903                                                      ("zs", numpy.array([z_cursor, z_cursor]))]:
       
   904                                     kwargs.setdefault(param, value)
       
   905                                 kwargs["color"] = 'r'
       
   906                                 self.Axes.plot(**kwargs)
       
   907                     
   742                 self.Axes.set_xlim(x_min, x_max)
   908                 self.Axes.set_xlim(x_min, x_max)
   743                 self.Axes.set_ylim(y_min, y_max)
   909                 self.Axes.set_ylim(y_min, y_max)
   744             
   910             
   745             labels = ["%s: %s" % (item.GetVariable(40), item.GetValue())
   911             if self.CursorTick is not None:
   746                       for item in self.Items]
   912                 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items])
   747             colors = [{True: 'b', False: 'k'}[item.IsForced()] for item in self.Items]
   913             else:
       
   914                 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items])
       
   915             names = [item.GetVariable(self.VariableNameMask) for item in self.Items]
       
   916             labels = map(lambda x: "%s: %s" % x, zip(names, values))
       
   917             colors = map(lambda x: {True: 'b', False: 'k'}[x], forced)
   748             if self.GraphType == GRAPH_PARALLEL:
   918             if self.GraphType == GRAPH_PARALLEL:
   749                 self.Legend = self.Axes.legend(self.Plots, labels, 
   919                 self.Legend = self.Axes.legend(self.Plots, labels, 
   750                     loc="upper left", frameon=False,
   920                     loc="upper left", frameon=False,
   751                     prop={'size':'small'})
   921                     prop={'size':'small'})
   752                 for t, color in zip(self.Legend.get_texts(), colors):
   922                 for t, color in zip(self.Legend.get_texts(), colors):
   784             self.Ticks = numpy.array([])
   954             self.Ticks = numpy.array([])
   785             self.RangeValues = None
   955             self.RangeValues = None
   786             self.StartTick = 0
   956             self.StartTick = 0
   787             self.Fixed = False
   957             self.Fixed = False
   788             self.Force = False
   958             self.Force = False
       
   959             
   789             self.GraphicPanels = []
   960             self.GraphicPanels = []
   790             
   961             
   791             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   962             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   792             main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   963             main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   793             
   964             
   832             
  1003             
   833             self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
  1004             self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
   834             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
  1005             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
   835             
  1006             
   836             self.RefreshCanvasRange()
  1007             self.RefreshCanvasRange()
       
  1008             self.CursorTickRatio = None
   837             
  1009             
   838         else:
  1010         else:
   839             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
  1011             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
   840             main_sizer.AddGrowableCol(0)
  1012             main_sizer.AddGrowableCol(0)
   841             main_sizer.AddGrowableRow(1)
  1013             main_sizer.AddGrowableRow(1)
   921     def NewDataAvailable(self, tick, *args, **kwargs):
  1093     def NewDataAvailable(self, tick, *args, **kwargs):
   922         if USE_MPL and tick is not None:
  1094         if USE_MPL and tick is not None:
   923             self.Ticks = numpy.append(self.Ticks, [tick])
  1095             self.Ticks = numpy.append(self.Ticks, [tick])
   924             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
  1096             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
   925                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
  1097                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
       
  1098                 self.ResetCursorTick(False)
   926         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
  1099         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
       
  1100     
       
  1101     def ForceRefresh(self):
       
  1102         self.Force = True
       
  1103         wx.CallAfter(self.NewDataAvailable, None, True)
   927     
  1104     
   928     def RefreshGraphicsSizer(self):
  1105     def RefreshGraphicsSizer(self):
   929         self.GraphicsSizer.Clear()
  1106         self.GraphicsSizer.Clear()
   930         
  1107         
   931         for panel in self.GraphicPanels:
  1108         for panel in self.GraphicPanels:
   932             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
  1109             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
   933             
  1110             
   934         self.GraphicsSizer.Layout()
  1111         self.GraphicsSizer.Layout()
   935         self.RefreshGraphicsWindowScrollbars()
  1112         self.RefreshGraphicsWindowScrollbars()
       
  1113     
       
  1114     def SetCursorTickRatio(self, cursor_tick_ratio):
       
  1115         self.CursorTickRatio = cursor_tick_ratio
       
  1116         self.ResetCursorTick() 
       
  1117     
       
  1118     def ResetCursorTickRatio(self):
       
  1119         self.CursorTickRatio = None
       
  1120         self.ResetCursorTick()
       
  1121     
       
  1122     def ResetCursorTick(self, force_refresh=True):
       
  1123         if self.CursorTickRatio is not None and len(self.Ticks) > 0:
       
  1124             raw_tick = self.StartTick + self.CursorTickRatio * self.CurrentRange
       
  1125             cursor_tick = self.Ticks[numpy.argmin(abs(self.Ticks - raw_tick))]
       
  1126         else:
       
  1127             cursor_tick = None
       
  1128         for panel in self.GraphicPanels:
       
  1129             if isinstance(panel, DebugVariableGraphic):
       
  1130                 panel.SetCursorTick(cursor_tick)
       
  1131         if force_refresh:
       
  1132             self.ForceRefresh()
       
  1133     
       
  1134     def GetTickLabel(self, tick):
       
  1135         label = "Tick: %d" % tick
       
  1136         if self.Ticktime > 0:
       
  1137             tick_duration = int(tick * self.Ticktime)
       
  1138             not_null = False
       
  1139             duration = ""
       
  1140             for value, format in [(tick_duration / DAY, "%dd"),
       
  1141                                   ((tick_duration % DAY) / HOUR, "%dh"),
       
  1142                                   ((tick_duration % HOUR) / MINUTE, "%dm"),
       
  1143                                   ((tick_duration % MINUTE) / SECOND, "%ds")]:
       
  1144                 
       
  1145                 if value > 0 or not_null:
       
  1146                     duration += format % value
       
  1147                     not_null = True
       
  1148             
       
  1149             duration += "%gms" % (float(tick_duration % SECOND) / MILLISECOND) 
       
  1150             label += "(%s)" % duration
       
  1151         return label
   936     
  1152     
   937     def RefreshView(self, only_values=False):
  1153     def RefreshView(self, only_values=False):
   938         if USE_MPL:
  1154         if USE_MPL:
   939             self.RefreshCanvasPosition()
  1155             self.RefreshCanvasPosition()
   940             
  1156             
   970                 self.Ticktime = self.DataProducer.GetTicktime()
  1186                 self.Ticktime = self.DataProducer.GetTicktime()
   971                 self.RefreshCanvasRange()
  1187                 self.RefreshCanvasRange()
   972             
  1188             
   973             for panel in self.GraphicPanels:
  1189             for panel in self.GraphicPanels:
   974                 panel.UnregisterObsoleteData()
  1190                 panel.UnregisterObsoleteData()
       
  1191                 if panel.IsEmpty():
       
  1192                     if panel.Canvas.HasCapture():
       
  1193                         panel.Canvas.ReleaseMouse()
       
  1194                     self.GraphicPanels.remove(panel)
       
  1195                     panel.Destroy()
       
  1196             
       
  1197             self.RefreshGraphicsSizer()
       
  1198             self.ForceRefresh()
   975             
  1199             
   976         else:
  1200         else:
   977             items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
  1201             items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   978             items.reverse()
  1202             items.reverse()
   979             for idx, item in items:
  1203             for idx, item in items:
  1084                 self.Fixed = False
  1308                 self.Fixed = False
  1085             if self.Fixed:
  1309             if self.Fixed:
  1086                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
  1310                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
  1087             else:
  1311             else:
  1088                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1312                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1089         self.Force = True
  1313         self.ForceRefresh()
  1090         self.RefreshView(True)
       
  1091     
  1314     
  1092     def OnRangeChanged(self, event):
  1315     def OnRangeChanged(self, event):
  1093         try:
  1316         try:
  1094             if self.Ticktime == 0:
  1317             if self.Ticktime == 0:
  1095                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
  1318                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
  1103     def OnResetButton(self, event):
  1326     def OnResetButton(self, event):
  1104         self.StartTick = 0
  1327         self.StartTick = 0
  1105         self.Fixed = False
  1328         self.Fixed = False
  1106         for panel in self.GraphicPanels:
  1329         for panel in self.GraphicPanels:
  1107             panel.ResetData()
  1330             panel.ResetData()
  1108         self.RefreshView(True)
  1331         self.ForceRefresh()
  1109         event.Skip()
  1332         event.Skip()
  1110 
  1333 
  1111     def OnCurrentButton(self, event):
  1334     def OnCurrentButton(self, event):
  1112         if len(self.Ticks) > 0:
  1335         if len(self.Ticks) > 0:
  1113             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1336             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1114             self.Fixed = False
  1337             self.Fixed = False
  1115             self.Force = True
  1338             self.ForceRefresh()
  1116             self.RefreshView(True)
       
  1117         event.Skip()
  1339         event.Skip()
  1118     
  1340     
  1119     def CopyDataToClipboard(self, variables):
  1341     def CopyDataToClipboard(self, variables):
  1120         text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables])
  1342         text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
  1121         next_tick = NextTick(variables)
  1343         next_tick = NextTick(variables)
  1122         while next_tick is not None:
  1344         while next_tick is not None:
  1123             values = []
  1345             values = []
  1124             for var_name, data in variables:
  1346             for item, data in variables:
  1125                 if len(data) > 0:
  1347                 if len(data) > 0:
  1126                     if next_tick == data[0][0]:
  1348                     if next_tick == data[0][0]:
  1127                         values.append("%.3f" % data.pop(0)[1])
  1349                         var_type = item.GetVariableType()
       
  1350                         if var_type in ["STRING", "WSTRING"]:
       
  1351                             value = item.GetRawValue(int(data.pop(0)[2]))
       
  1352                             if var_type == "STRING":
       
  1353                                 values.append("'%s'" % value)
       
  1354                             else:
       
  1355                                 values.append('"%s"' % value)
       
  1356                         else:
       
  1357                             values.append("%.3f" % data.pop(0)[1])
  1128                     else:
  1358                     else:
  1129                         values.append("")
  1359                         values.append("")
  1130                 else:
  1360                 else:
  1131                     values.append("")
  1361                     values.append("")
  1132             text += "%d;%s;\n" % (next_tick, ";".join(values))
  1362             text += "%d;%s;\n" % (next_tick, ";".join(values))
  1133             next_tick = NextTick(variables)
  1363             next_tick = NextTick(variables)
  1134         self.ParentWindow.SetCopyBuffer(text)
  1364         self.ParentWindow.SetCopyBuffer(text)
  1135     
  1365     
  1136     def OnExportGraphButton(self, event):
  1366     def OnExportGraphButton(self, event):
  1137         variables = []
  1367         variables = []
  1138         for item in self.Table.GetData():
  1368         if USE_MPL:
       
  1369             items = []
       
  1370             for panel in self.GraphicPanels:
       
  1371                 items.extend(panel.GetItems())
       
  1372         else:
       
  1373             items = self.Table.GetData()
       
  1374         for item in items:
  1139             if item.IsNumVariable():
  1375             if item.IsNumVariable():
  1140                 variables.append((item.GetVariable(), [entry for entry in item.GetData()]))
  1376                 variables.append((item, [entry for entry in item.GetData()]))
  1141         wx.CallAfter(self.CopyDataToClipboard, variables)
  1377         wx.CallAfter(self.CopyDataToClipboard, variables)
  1142         event.Skip()
  1378         event.Skip()
  1143     
  1379     
  1144     def OnPositionChanging(self, event):
  1380     def OnPositionChanging(self, event):
  1145         if len(self.Ticks) > 0:
  1381         if len(self.Ticks) > 0:
  1146             self.StartTick = self.Ticks[0] + event.GetPosition()
  1382             self.StartTick = self.Ticks[0] + event.GetPosition()
  1147             self.Fixed = True
  1383             self.Fixed = True
  1148             self.Force = True
  1384             self.ForceRefresh()
  1149             wx.CallAfter(self.NewDataAvailable, None, True)
       
  1150         event.Skip()
  1385         event.Skip()
  1151     
  1386     
  1152     def GetRange(self):
  1387     def GetRange(self):
  1153         return self.StartTick, self.StartTick + self.CurrentRange
  1388         return self.StartTick, self.StartTick + self.CurrentRange
  1154     
  1389     
  1185                     self.GraphicPanels.append(panel)
  1420                     self.GraphicPanels.append(panel)
  1186                 self.RefreshGraphicsSizer()
  1421                 self.RefreshGraphicsSizer()
  1187             else:
  1422             else:
  1188                 self.Table.InsertItem(idx, item)
  1423                 self.Table.InsertItem(idx, item)
  1189             
  1424             
  1190             self.RefreshView()
  1425             self.ForceRefresh()
  1191     
  1426     
  1192     def MoveGraph(self, iec_path, idx = None):
  1427     def MoveGraph(self, iec_path, idx = None):
  1193         if idx is None:
  1428         if idx is None:
  1194             idx = len(self.GraphicPanels)
  1429             idx = len(self.GraphicPanels)
  1195         source_panel = None
  1430         source_panel = None
  1208                 source_panel.Destroy()
  1443                 source_panel.Destroy()
  1209             
  1444             
  1210             panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
  1445             panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
  1211             self.GraphicPanels.insert(idx, panel)
  1446             self.GraphicPanels.insert(idx, panel)
  1212             self.RefreshGraphicsSizer()
  1447             self.RefreshGraphicsSizer()
  1213             self.RefreshView()
  1448             self.ForceRefresh()
  1214     
  1449     
  1215     def SplitGraphs(self, source_panel, item=None):
  1450     def SplitGraphs(self, source_panel, item=None):
  1216         source_idx = self.GetViewerIndex(source_panel)
  1451         source_idx = self.GetViewerIndex(source_panel)
  1217         if source_idx is not None:
  1452         if source_idx is not None:
  1218             
  1453             
  1236                 else:
  1471                 else:
  1237                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
  1472                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
  1238                 self.GraphicPanels.insert(source_idx + 1, panel)
  1473                 self.GraphicPanels.insert(source_idx + 1, panel)
  1239             
  1474             
  1240             self.RefreshGraphicsSizer()
  1475             self.RefreshGraphicsSizer()
  1241             self.RefreshView()
  1476             self.ForceRefresh()
  1242     
  1477     
  1243     def MergeGraphs(self, source, target_idx, merge_type, force=False):
  1478     def MergeGraphs(self, source, target_idx, merge_type, force=False):
  1244         source_item = None
  1479         source_item = None
  1245         source_panel = None
  1480         source_panel = None
  1246         for panel in self.GraphicPanels:
  1481         for panel in self.GraphicPanels:
  1275                 target_panel.AddItem(source_item)
  1510                 target_panel.AddItem(source_item)
  1276                 target_panel.GraphType = merge_type
  1511                 target_panel.GraphType = merge_type
  1277                 target_panel.ResetGraphics()
  1512                 target_panel.ResetGraphics()
  1278                 
  1513                 
  1279                 self.RefreshGraphicsSizer()
  1514                 self.RefreshGraphicsSizer()
  1280                 self.RefreshView()
  1515                 self.ForceRefresh()
  1281     
  1516     
  1282     def DeleteValue(self, source_panel, item=None):
  1517     def DeleteValue(self, source_panel, item=None):
  1283         source_idx = self.GetViewerIndex(source_panel)
  1518         source_idx = self.GetViewerIndex(source_panel)
  1284         if source_idx is not None:
  1519         if source_idx is not None:
  1285             
  1520             
  1292                 source_panel.RemoveItem(item)
  1527                 source_panel.RemoveItem(item)
  1293                 if source_panel.IsEmpty():
  1528                 if source_panel.IsEmpty():
  1294                     source_panel.Destroy()
  1529                     source_panel.Destroy()
  1295                     self.GraphicPanels.remove(source_panel)
  1530                     self.GraphicPanels.remove(source_panel)
  1296                     self.RefreshGraphicsSizer()
  1531                     self.RefreshGraphicsSizer()
  1297             self.RefreshView()
  1532             self.ForceRefresh()
  1298     
  1533     
  1299     def GetDebugVariables(self):
  1534     def GetDebugVariables(self):
  1300         if USE_MPL:
  1535         if USE_MPL:
  1301             return [panel.GetVariables() for panel in self.GraphicPanels]
  1536             return [panel.GetVariables() for panel in self.GraphicPanels]
  1302         else:
  1537         else:
  1321                         continue
  1556                         continue
  1322                     self.GraphicPanels.append(panel)
  1557                     self.GraphicPanels.append(panel)
  1323                     self.RefreshGraphicsSizer()
  1558                     self.RefreshGraphicsSizer()
  1324                 else:
  1559                 else:
  1325                     self.InsertValue(variable, force=True)
  1560                     self.InsertValue(variable, force=True)
  1326             self.RefreshView()
  1561             self.ForceRefresh()
  1327         else:
  1562         else:
  1328             for variable in variables:
  1563             for variable in variables:
  1329                 if isinstance(variable, (ListType, TupleType)):
  1564                 if isinstance(variable, (ListType, TupleType)):
  1330                     for iec_path in variable:
  1565                     for iec_path in variable:
  1331                         self.InsertValue(iec_path, force=True)
  1566                         self.InsertValue(iec_path, force=True)