controls/DebugVariablePanel.py
changeset 902 ffa8ee5ee2fe
parent 898 6b2958f04f30
child 903 e70daa8bca85
equal deleted inserted replaced
901:ab43f3e40b9d 902:ffa8ee5ee2fe
    22 #License along with this library; if not, write to the Free Software
    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
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 from types import TupleType, FloatType
    25 from types import TupleType, FloatType
    26 from time import time as gettime
    26 from time import time as gettime
       
    27 import math
    27 import numpy
    28 import numpy
    28 
    29 
    29 import wx
    30 import wx
    30 import wx.lib.buttons
    31 import wx.lib.buttons
    31 
    32 
    32 try:
    33 try:
    33     import matplotlib
    34     import matplotlib
    34     matplotlib.use('WX')
    35     matplotlib.use('WX')
    35     import matplotlib.pyplot
    36     import matplotlib.pyplot
    36     from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
    37     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
       
    38     #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
    37     from mpl_toolkits.mplot3d import Axes3D
    39     from mpl_toolkits.mplot3d import Axes3D
    38     USE_MPL = True
    40     USE_MPL = True
    39 except:
    41 except:
    40     USE_MPL = False
    42     USE_MPL = False
    41 
    43 
    42 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
    44 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
    43 from controls import CustomGrid, CustomTable
    45 from controls import CustomGrid, CustomTable
    44 from dialogs.ForceVariableDialog import ForceVariableDialog
    46 from dialogs.ForceVariableDialog import ForceVariableDialog
    45 from util.BitmapLibrary import GetBitmap
    47 from util.BitmapLibrary import GetBitmap
       
    48 
       
    49 SECOND = 1000000000
       
    50 MINUTE = 60 * SECOND
       
    51 HOUR = 60 * MINUTE
       
    52 
       
    53 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
    54 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
       
    55 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
       
    56                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
       
    57                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
    46 
    58 
    47 def AppendMenu(parent, help, id, kind, text):
    59 def AppendMenu(parent, help, id, kind, text):
    48     parent.Append(help=help, id=id, kind=kind, text=text)
    60     parent.Append(help=help, id=id, kind=kind, text=text)
    49 
    61 
    50 def GetDebugVariablesTableColnames():
    62 def GetDebugVariablesTableColnames():
    81         self.ResetData()
    93         self.ResetData()
    82     
    94     
    83     def GetVariableType(self):
    95     def GetVariableType(self):
    84         return self.VariableType
    96         return self.VariableType
    85     
    97     
    86     def GetData(self):
    98     def GetData(self, start_tick=None, end_tick=None):
    87         return self.Data
    99         if self.IsNumVariable():
       
   100             if len(self.Data) == 0:
       
   101                 return self.Data
       
   102             
       
   103             start_idx = end_idx = None
       
   104             if start_tick is not None:
       
   105                 start_idx = self.GetNearestData(start_tick, -1)
       
   106             if end_tick is not None:
       
   107                 end_idx = self.GetNearestData(end_tick, 1)
       
   108             if start_idx is None:
       
   109                 start_idx = 0
       
   110             if end_idx is not None:
       
   111                 return self.Data[start_idx:end_idx + 1]
       
   112             else:
       
   113                 return self.Data[start_idx:]
       
   114             
       
   115         return None
       
   116     
       
   117     def GetRange(self):
       
   118         return self.MinValue, self.MaxValue
    88     
   119     
    89     def ResetData(self):
   120     def ResetData(self):
    90         if self.IsNumVariable():
   121         if self.IsNumVariable():
    91             self.Data = numpy.array([]).reshape(0, 2)
   122             self.Data = numpy.array([]).reshape(0, 2)
       
   123             self.MinValue = None
       
   124             self.MaxValue = None
    92         else:
   125         else:
    93             self.Data = None
   126             self.Data = None
    94     
   127     
    95     def IsNumVariable(self):
   128     def IsNumVariable(self):
    96         return self.Parent.IsNumType(self.VariableType)
   129         return self.Parent.IsNumType(self.VariableType)
    97     
   130     
    98     def NewValue(self, tick, value, forced=False):
   131     def NewValue(self, tick, value, forced=False):
    99         if self.IsNumVariable():
   132         if self.IsNumVariable():
   100             num_value = {True:1., False:0.}.get(value, float(value))
   133             num_value = {True:1., False:0.}.get(value, float(value))
       
   134             if self.MinValue is None:
       
   135                 self.MinValue = num_value
       
   136             else:
       
   137                 self.MinValue = min(self.MinValue, num_value)
       
   138             if self.MaxValue is None:
       
   139                 self.MaxValue = num_value
       
   140             else:
       
   141                 self.MaxValue = max(self.MaxValue, num_value)
   101             self.Data = numpy.append(self.Data, [[float(tick), num_value]], axis=0)
   142             self.Data = numpy.append(self.Data, [[float(tick), num_value]], axis=0)
   102             self.Parent.HasNewData = True
   143             self.Parent.HasNewData = True
   103         DebugDataConsumer.NewValue(self, tick, value, forced)
   144         DebugDataConsumer.NewValue(self, tick, value, forced)
   104     
   145     
   105     def SetForced(self, forced):
   146     def SetForced(self, forced):
   130         
   171         
   131     def GetAxis3D(self):
   172     def GetAxis3D(self):
   132         if self.IsNumVariable():
   173         if self.IsNumVariable():
   133             return self.Axis3D
   174             return self.Axis3D
   134         return ""
   175         return ""
       
   176 
       
   177     def GetNearestData(self, tick, adjust):
       
   178         if self.IsNumVariable():
       
   179             ticks = self.Data[:, 0]
       
   180             new_cursor = numpy.argmin(abs(ticks - tick))
       
   181             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
       
   182                 new_cursor -= 1
       
   183             elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(ticks):
       
   184                 new_cursor += 1
       
   185             return new_cursor
       
   186         return None
   135 
   187 
   136 class DebugVariableTable(CustomTable):
   188 class DebugVariableTable(CustomTable):
   137     
   189     
   138     def GetValue(self, row, col):
   190     def GetValue(self, row, col):
   139         if row < self.GetNumberRows():
   191         if row < self.GetNumberRows():
   245         dialog.ShowModal()
   297         dialog.ShowModal()
   246         dialog.Destroy()
   298         dialog.Destroy()
   247 
   299 
   248 SCROLLBAR_UNIT = 10
   300 SCROLLBAR_UNIT = 10
   249 
   301 
       
   302 def NextTick(variables):
       
   303     next_tick = None
       
   304     for var_name, data in variables:
       
   305         if len(data) > 0:
       
   306             if next_tick is None:
       
   307                 next_tick = data[0][0]
       
   308             else:
       
   309                 next_tick = min(next_tick, data[0][0])
       
   310     return next_tick
       
   311 
   250 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
   312 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
   251     
   313     
   252     def __init__(self, parent, producer):
   314     def __init__(self, parent, producer, window):
   253         wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
   315         wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
       
   316         
       
   317         self.ParentWindow = window
       
   318         
   254         DebugViewer.__init__(self, producer, True)
   319         DebugViewer.__init__(self, producer, True)
   255         
   320         
   256         self.SetSashGravity(0.5)
   321         self.SetSashGravity(0.5)
   257         self.SetNeedUpdating(True)
   322         self.SetNeedUpdating(True)
   258         self.SetMinimumPaneSize(1)
   323         self.SetMinimumPaneSize(1)
   288         main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   353         main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   289         
   354         
   290         self.MainPanel.SetSizer(main_panel_sizer)
   355         self.MainPanel.SetSizer(main_panel_sizer)
   291         
   356         
   292         self.HasNewData = False
   357         self.HasNewData = False
       
   358         self.Ticks = numpy.array([])
       
   359         self.RangeValues = None
       
   360         self.StartTick = 0
       
   361         self.Fixed = False
       
   362         self.Force = False
   293         
   363         
   294         self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
   364         self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
   295         self.VariablesGrid.SetTable(self.Table)
   365         self.VariablesGrid.SetTable(self.Table)
   296         self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
   366         self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
   297                                        "Up": self.UpButton,
   367                                        "Up": self.UpButton,
   335         if USE_MPL:
   405         if USE_MPL:
   336             self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
   406             self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
   337             
   407             
   338             graphics_panel_sizer = wx.BoxSizer(wx.VERTICAL)
   408             graphics_panel_sizer = wx.BoxSizer(wx.VERTICAL)
   339             
   409             
       
   410             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   411             graphics_panel_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
       
   412             
       
   413             range_label = wx.StaticText(self.GraphicsPanel, label=_('Range:'))
       
   414             graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
       
   415             
       
   416             self.CanvasRange = wx.ComboBox(self.GraphicsPanel, 
       
   417                   size=wx.Size(100, 28), style=wx.CB_READONLY)
       
   418             self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
       
   419             graphics_button_sizer.AddWindow(self.CanvasRange, 1, flag=wx.ALIGN_CENTER_VERTICAL)
       
   420             
       
   421             for name, bitmap, help in [
       
   422                 ("ResetButton", "reset", _("Clear the graph values")),
       
   423                 ("CurrentButton", "current", _("Go to current value")),
       
   424                 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
       
   425                 button = wx.lib.buttons.GenBitmapButton(self.GraphicsPanel, 
       
   426                       bitmap=GetBitmap(bitmap), 
       
   427                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   428                 button.SetToolTipString(help)
       
   429                 setattr(self, name, button)
       
   430                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   431                 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   432             
       
   433             self.CanvasPosition = wx.ScrollBar(self.GraphicsPanel, 
       
   434                   size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
       
   435             self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
       
   436                   self.OnPositionChanging, self.CanvasPosition)
       
   437             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
       
   438                   self.OnPositionChanging, self.CanvasPosition)
       
   439             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, 
       
   440                   self.OnPositionChanging, self.CanvasPosition)
       
   441             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
       
   442                   self.OnPositionChanging, self.CanvasPosition)
       
   443             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
       
   444                   self.OnPositionChanging, self.CanvasPosition)
       
   445             graphics_panel_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
       
   446             
   340             self.GraphicsCanvasWindow = wx.ScrolledWindow(self.GraphicsPanel, style=wx.HSCROLL|wx.VSCROLL)
   447             self.GraphicsCanvasWindow = wx.ScrolledWindow(self.GraphicsPanel, style=wx.HSCROLL|wx.VSCROLL)
   341             self.GraphicsCanvasWindow.Bind(wx.EVT_SIZE, self.OnGraphicsCanvasWindowResize)
   448             self.GraphicsCanvasWindow.Bind(wx.EVT_SIZE, self.OnGraphicsCanvasWindowResize)
   342             graphics_panel_sizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
   449             graphics_panel_sizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
   343             
   450             
   344             graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
   451             graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
   372         
   479         
   373         else:
   480         else:
   374             self.Initialize(self.MainPanel)
   481             self.Initialize(self.MainPanel)
   375         
   482         
   376         self.ResetGraphics()
   483         self.ResetGraphics()
   377     
   484         self.RefreshCanvasRange()
   378     def RefreshNewData(self):
   485         self.RefreshScrollBar()
   379         if self.HasNewData:
   486         
       
   487     def SetDataProducer(self, producer):
       
   488         DebugViewer.SetDataProducer(self, producer)
       
   489         
       
   490         if self.DataProducer is not None:
       
   491             self.Ticktime = self.DataProducer.GetTicktime()
       
   492             self.RefreshCanvasRange()
       
   493         else:
       
   494             self.Ticktime = 0
       
   495     
       
   496     def RefreshNewData(self, *args, **kwargs):
       
   497         if self.HasNewData or self.Force:
   380             self.HasNewData = False
   498             self.HasNewData = False
   381             self.RefreshGrid(only_values=True)
   499             self.RefreshGrid(only_values=True)
   382         DebugViewer.RefreshNewData(self)
   500         DebugViewer.RefreshNewData(self, *args, **kwargs)
       
   501     
       
   502     def NewDataAvailable(self, tick, *args, **kwargs):
       
   503         if tick is not None:
       
   504             self.Ticks = numpy.append(self.Ticks, [tick])
       
   505             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
       
   506                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
       
   507         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
   383     
   508     
   384     def RefreshGrid(self, only_values=False):
   509     def RefreshGrid(self, only_values=False):
   385         self.Freeze()
   510         self.Freeze()
   386         if only_values:
   511         if only_values:
   387             for col in xrange(self.Table.GetNumberCols()):
   512             for col in xrange(self.Table.GetNumberCols()):
   390                         self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
   515                         self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
   391         else:
   516         else:
   392             self.Table.ResetView(self.VariablesGrid)
   517             self.Table.ResetView(self.VariablesGrid)
   393         self.VariablesGrid.RefreshButtons()
   518         self.VariablesGrid.RefreshButtons()
   394         
   519         
   395         if USE_MPL:
   520         self.RefreshScrollBar()
       
   521         
       
   522         self.Thaw()
       
   523         
       
   524         if USE_MPL and (not self.Fixed or self.Force):
       
   525             self.Force = False
       
   526             
   396             # Refresh graphics
   527             # Refresh graphics
   397             idx = 0
   528             idx = 0
       
   529             start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
   398             for item in self.Table.GetData():
   530             for item in self.Table.GetData():
   399                 data = item.GetData()
   531                 data = item.GetData(start_tick, end_tick)
   400                 if data is not None:
   532                 if data is not None:
       
   533                     min_value, max_value = item.GetRange()
       
   534                     if min_value is not None and max_value is not None:
       
   535                         y_center = (min_value + max_value) / 2.
       
   536                         y_range = max(1.0, max_value - min_value)
       
   537                     else:
       
   538                         y_center = 0.5
       
   539                         y_range = 1.0
   401                     self.GraphicsAxes[idx].clear()
   540                     self.GraphicsAxes[idx].clear()
   402                     self.GraphicsAxes[idx].plot(data[:, 0], data[:, 1])
   541                     self.GraphicsAxes[idx].plot(data[:, 0], data[:, 1])
       
   542                     self.GraphicsAxes[idx].set_xlim(start_tick, end_tick)
       
   543                     self.GraphicsAxes[idx].set_ylim(
       
   544                         y_center - y_range * 0.55, y_center + y_range * 0.55)
   403                     idx += 1
   545                     idx += 1
   404             self.GraphicsCanvas.draw()
   546             if idx > 0:
       
   547                 self.GraphicsCanvas.draw()
   405                     
   548                     
   406             # Refresh 3D graphics
   549             # Refresh 3D graphics
   407             while len(self.Graphics3DAxes.lines) > 0:
   550             while len(self.Graphics3DAxes.lines) > 0:
   408                 self.Graphics3DAxes.lines.pop()
   551                 self.Graphics3DAxes.lines.pop()
   409             if self.Axis3DValues is not None:
   552             if self.Axis3DValues is not None:
   410                 self.Graphics3DAxes.plot(
   553                 axis = self.Axis3DValues[0]
   411                     self.Axis3DValues[0][1].GetData()[self.Axis3DValues[0][0]:, 1],
   554                 start_tick = max(self.StartTick, self.Axis3DValues[1])
   412                     self.Axis3DValues[1][1].GetData()[self.Axis3DValues[1][0]:, 1],
   555                 end_tick = max(self.StartTick + self.CurrentRange, self.Axis3DValues[1])
   413                     zs = self.Axis3DValues[2][1].GetData()[self.Axis3DValues[2][0]:, 1])
   556                 xyz_data = [axe.GetData(start_tick, end_tick)[:, 1] for axe in axis]
   414             self.Graphics3DCanvas.draw()
   557                 length = reduce(min, [len(data) for data in xyz_data])
   415         
   558                 self.Graphics3DAxes.plot(xyz_data[0][:length],
   416         self.Thaw()
   559                     xyz_data[1][:length],
   417         
   560                     zs = xyz_data[2][:length])
       
   561                 self.Graphics3DCanvas.draw()
       
   562             
   418     def UnregisterObsoleteData(self):
   563     def UnregisterObsoleteData(self):
   419         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   564         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   420         items.reverse()
   565         items.reverse()
   421         for idx, item in items:
   566         for idx, item in items:
   422             iec_path = item.GetVariable().upper()
   567             iec_path = item.GetVariable().upper()
   428                 item.RefreshVariableType()
   573                 item.RefreshVariableType()
   429         self.Freeze()
   574         self.Freeze()
   430         self.Table.ResetView(self.VariablesGrid)
   575         self.Table.ResetView(self.VariablesGrid)
   431         self.VariablesGrid.RefreshButtons()
   576         self.VariablesGrid.RefreshButtons()
   432         self.Thaw()
   577         self.Thaw()
       
   578         if self.DataProducer is not None:
       
   579             self.Ticktime = self.DataProducer.GetTicktime()
       
   580             self.RefreshCanvasRange()
   433     
   581     
   434     def ResetGrid(self):
   582     def ResetGrid(self):
   435         self.DeleteDataConsumers()
   583         self.DeleteDataConsumers()
   436         self.Table.Empty()
   584         self.Table.Empty()
   437         self.Freeze()
   585         self.Freeze()
   438         self.Table.ResetView(self.VariablesGrid)
   586         self.Table.ResetView(self.VariablesGrid)
   439         self.VariablesGrid.RefreshButtons()
   587         self.VariablesGrid.RefreshButtons()
   440         self.Thaw()
   588         self.Thaw()
   441         self.ResetGraphics()
   589         self.ResetGraphics()
   442     
   590     
       
   591     def RefreshCanvasRange(self):
       
   592         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
       
   593             self.RangeValues = RANGE_VALUES
       
   594             self.CanvasRange.Clear()
       
   595             for text, value in RANGE_VALUES:
       
   596                 self.CanvasRange.Append(text)
       
   597             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
       
   598             self.CurrentRange = RANGE_VALUES[0][1]
       
   599             self.RefreshGrid(True)
       
   600         elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES:
       
   601             self.RangeValues = TIME_RANGE_VALUES
       
   602             self.CanvasRange.Clear()
       
   603             for text, value in TIME_RANGE_VALUES:
       
   604                 self.CanvasRange.Append(text)
       
   605             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
       
   606             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
       
   607             self.RefreshGrid(True)
       
   608     
       
   609     def RefreshScrollBar(self):
       
   610         if len(self.Ticks) > 0:
       
   611             pos = int(self.StartTick - self.Ticks[0])
       
   612             range = int(self.Ticks[-1] - self.Ticks[0])
       
   613         else:
       
   614             pos = 0
       
   615             range = 0
       
   616         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
       
   617     
   443     def GetForceVariableMenuFunction(self, iec_path, item):
   618     def GetForceVariableMenuFunction(self, iec_path, item):
   444         iec_type = self.GetDataType(iec_path)
   619         iec_type = self.GetDataType(iec_path)
   445         def ForceVariableFunction(event):
   620         def ForceVariableFunction(event):
   446             if iec_type is not None:
   621             if iec_type is not None:
   447                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
   622                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
   490     
   665     
   491     def OnVariablesGridCellChange(self, event):
   666     def OnVariablesGridCellChange(self, event):
   492         row, col = event.GetRow(), event.GetCol()
   667         row, col = event.GetRow(), event.GetCol()
   493         if self.Table.GetColLabelValue(col, False) == "3DAxis":
   668         if self.Table.GetColLabelValue(col, False) == "3DAxis":
   494             wx.CallAfter(self.Reset3DGraphics)
   669             wx.CallAfter(self.Reset3DGraphics)
       
   670         event.Skip()
       
   671     
       
   672     def RefreshRange(self):
       
   673         if len(self.Ticks) > 0:
       
   674             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
       
   675                 self.Fixed = False
       
   676             if self.Fixed:
       
   677                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
       
   678             else:
       
   679                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
   680         self.Force = True
       
   681         self.RefreshGrid(True)
       
   682     
       
   683     def OnRangeChanged(self, event):
       
   684         try:
       
   685             if self.Ticktime == 0:
       
   686                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
       
   687             else:
       
   688                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime
       
   689         except ValueError, e:
       
   690             self.CanvasRange.SetValue(str(self.CurrentRange))
       
   691         wx.CallAfter(self.RefreshRange)
       
   692         event.Skip()
       
   693     
       
   694     def OnResetButton(self, event):
       
   695         self.StartTick = 0
       
   696         self.Fixed = False
       
   697         for item in self.Table.GetData():
       
   698             item.ResetData()
       
   699         self.RefreshGrid(True)
       
   700         event.Skip()
       
   701 
       
   702     def OnCurrentButton(self, event):
       
   703         if len(self.Ticks) > 0:
       
   704             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
   705             self.Fixed = False
       
   706             self.Force = True
       
   707             self.RefreshGrid(True)
       
   708         event.Skip()
       
   709     
       
   710     def CopyDataToClipboard(self, variables):
       
   711         text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables])
       
   712         next_tick = NextTick(variables)
       
   713         while next_tick is not None:
       
   714             values = []
       
   715             for var_name, data in variables:
       
   716                 if len(data) > 0:
       
   717                     if next_tick == data[0][0]:
       
   718                         values.append("%.3f" % data.pop(0)[1])
       
   719                     else:
       
   720                         values.append("")
       
   721                 else:
       
   722                     values.append("")
       
   723             text += "%d;%s;\n" % (next_tick, ";".join(values))
       
   724             next_tick = NextTick(variables)
       
   725         self.ParentWindow.SetCopyBuffer(text)
       
   726     
       
   727     def OnExportGraphButton(self, event):
       
   728         variables = []
       
   729         for item in self.Table.GetData():
       
   730             if item.IsNumVariable():
       
   731                 variables.append((item.GetVariable(), [entry for entry in item.GetData()]))
       
   732         wx.CallAfter(self.CopyDataToClipboard, variables)
       
   733         event.Skip()
       
   734     
       
   735     def OnPositionChanging(self, event):
       
   736         if len(self.Ticks) > 0:
       
   737             self.StartTick = self.Ticks[0] + event.GetPosition()
       
   738             self.Fixed = True
       
   739             self.Force = True
       
   740             wx.CallAfter(self.NewDataAvailable, None, True)
   495         event.Skip()
   741         event.Skip()
   496     
   742     
   497     def InsertValue(self, iec_path, idx = None, force=False, axis3D=False):
   743     def InsertValue(self, iec_path, idx = None, force=False, axis3D=False):
   498         if idx is None:
   744         if idx is None:
   499             idx = self.Table.GetNumberRows()
   745             idx = self.Table.GetNumberRows()
   539             self.GraphicsCanvas.draw()
   785             self.GraphicsCanvas.draw()
   540             
   786             
   541             self.Reset3DGraphics()
   787             self.Reset3DGraphics()
   542     
   788     
   543     def Reset3DGraphics(self):
   789     def Reset3DGraphics(self):
       
   790         self.Axis3DValues = None
   544         axis = [item for item in self.Table.GetData() if item.GetAxis3D()]
   791         axis = [item for item in self.Table.GetData() if item.GetAxis3D()]
   545         if len(axis) == 3:
   792         if len(axis) == 3:
   546             max_tick = None
   793             max_tick = None
   547             xaxis, yaxis, zaxis = [item.GetData() for item in axis]
   794             xaxis, yaxis, zaxis = [item.GetData() for item in axis]
   548             if len(xaxis) > 0 and len(yaxis) > 0 and len(zaxis) > 0:
   795             if len(xaxis) > 0 and len(yaxis) > 0 and len(zaxis) > 0:
   549                 max_tick = max(xaxis[0, 0], yaxis[0, 0], zaxis[0, 0])
   796                 max_tick = max(xaxis[0, 0], yaxis[0, 0], zaxis[0, 0])
   550             if max_tick is not None:
   797             if max_tick is not None:
   551                 self.Axis3DValues = [(numpy.argmin(abs(item.GetData()[:, 0] - max_tick)), item)
   798                 self.Axis3DValues = (axis, max_tick)
   552                                      for item in axis]
   799             else:
   553             else:
   800                 self.Axis3DValues = (axis, 0)
   554                self.Axis3DValues = [(0, item) for item in axis]
       
   555         else:
       
   556             self.Axis3DValues = None
       
   557     
   801     
   558     def OnGraphics3DMotion(self, event):
   802     def OnGraphics3DMotion(self, event):
   559         current_time = gettime()
   803         current_time = gettime()
   560         if current_time - self.LastMotionTime > REFRESH_PERIOD:
   804         if current_time - self.LastMotionTime > REFRESH_PERIOD:
   561             self.LastMotionTime = current_time
   805             self.LastMotionTime = current_time
   562             Axes3D._on_move(self.Graphics3DAxes, event)
   806             Axes3D._on_move(self.Graphics3DAxes, event)
   563 
   807     
   564     def RefreshGraphicsCanvasWindowScrollbars(self):
   808     def RefreshGraphicsCanvasWindowScrollbars(self):
   565         xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
   809         xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
   566         window_size = self.GraphicsCanvasWindow.GetClientSize()
   810         window_size = self.GraphicsCanvasWindow.GetClientSize()
   567         vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 50)
   811         vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 50)
   568         self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
   812         self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))