GraphicViewer.py
changeset 660 30c0371ac086
parent 648 95d165193770
child 665 6a376615142e
equal deleted inserted replaced
659:46c3d5410888 660:30c0371ac086
    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 import wx
    25 import wx
    26 import wx.lib.plot as plot
    26 import wx.lib.plot as plot
    27 import numpy
    27 import numpy
    28 from graphics.GraphicCommons import DebugViewer
    28 import math
       
    29 from graphics.GraphicCommons import DebugViewer, MODE_SELECTION, MODE_MOTION
    29 from controls import EditorPanel
    30 from controls import EditorPanel
    30 
    31 
    31 colours = ['blue', 'red', 'green', 'yellow', 'orange', 'purple', 'brown', 'cyan',
    32 colours = ['blue', 'red', 'green', 'yellow', 'orange', 'purple', 'brown', 'cyan',
    32            'pink', 'grey']
    33            'pink', 'grey']
    33 markers = ['circle', 'dot', 'square', 'triangle', 'triangle_down', 'cross', 'plus', 'circle']
    34 markers = ['circle', 'dot', 'square', 'triangle', 'triangle_down', 'cross', 'plus', 'circle']
    39 
    40 
    40 SECOND = 1000000000
    41 SECOND = 1000000000
    41 MINUTE = 60 * SECOND
    42 MINUTE = 60 * SECOND
    42 HOUR = 60 * MINUTE
    43 HOUR = 60 * MINUTE
    43 
    44 
    44 RANGE_VALUES = [(str(25 * 2 ** i), 25 * 2 ** i) for i in xrange(6)]
    45 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
    46 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
    45 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
    47 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
    46                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
    48                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
    47                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
    49                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
    48 
    50 
    49 [ID_GRAPHICVIEWER, ID_GRAPHICVIEWERCANVAS,
    51 [ID_GRAPHICVIEWER, ID_GRAPHICVIEWERCANVAS, 
    50  ID_GRAPHICVIEWERCANVASRANGE, ID_GRAPHICVIEWERCANVASPOSITION,
    52  ID_GRAPHICVIEWERCANVASRANGE, ID_GRAPHICVIEWERCANVASZOOM, 
    51  ID_GRAPHICVIEWERRESETBUTTON, ID_GRAPHICVIEWERCURRENTBUTTON, 
    53  ID_GRAPHICVIEWERCANVASPOSITION, ID_GRAPHICVIEWERRESETBUTTON, 
    52  ID_GRAPHICVIEWERSTATICTEXT1, ID_GRAPHICVIEWERSTATICTEXT2,
    54  ID_GRAPHICVIEWERCURRENTBUTTON, ID_GRAPHICVIEWERSTATICTEXT1, 
    53 ] = [wx.NewId() for _init_ctrls in range(8)]
    55  ID_GRAPHICVIEWERSTATICTEXT2, ID_GRAPHICVIEWERSTATICTEXT3, 
       
    56 ] = [wx.NewId() for _init_ctrls in range(10)]
    54 
    57 
    55 class GraphicViewer(EditorPanel, DebugViewer):
    58 class GraphicViewer(EditorPanel, DebugViewer):
    56 
    59 
    57     def _init_coll_MainGridSizer_Items(self, parent):
    60     def _init_coll_MainGridSizer_Items(self, parent):
    58         # generated method, don't edit
    61         # generated method, don't edit
    66 
    69 
    67     def _init_coll_RangeSizer_Items(self, parent):
    70     def _init_coll_RangeSizer_Items(self, parent):
    68         # generated method, don't edit
    71         # generated method, don't edit
    69         parent.AddWindow(self.staticbox1, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
    72         parent.AddWindow(self.staticbox1, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
    70         parent.AddWindow(self.CanvasRange, 0, border=5, flag=wx.ALL)
    73         parent.AddWindow(self.CanvasRange, 0, border=5, flag=wx.ALL)
       
    74         parent.AddWindow(self.staticbox3, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
    75         parent.AddWindow(self.CanvasZoom, 0, border=5, flag=wx.ALL)
    71         parent.AddWindow(self.staticText2, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
    76         parent.AddWindow(self.staticText2, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
    72         parent.AddWindow(self.CanvasPosition, 0, border=5, flag=wx.GROW|wx.ALL)
    77         parent.AddWindow(self.CanvasPosition, 0, border=5, flag=wx.GROW|wx.ALL)
    73         parent.AddWindow(self.ResetButton, 0, border=5, flag=wx.ALL)
    78         parent.AddWindow(self.ResetButton, 0, border=5, flag=wx.ALL)
    74         parent.AddWindow(self.CurrentButton, 0, border=5, flag=wx.ALL)
    79         parent.AddWindow(self.CurrentButton, 0, border=5, flag=wx.ALL)
    75         
    80         
    79         parent.AddGrowableRow(0)
    84         parent.AddGrowableRow(0)
    80         
    85         
    81     def _init_sizers(self):
    86     def _init_sizers(self):
    82         # generated method, don't edit
    87         # generated method, don't edit
    83         self.MainGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    88         self.MainGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    84         self.RangeSizer = wx.FlexGridSizer(cols=6, hgap=0, rows=1, vgap=0)
    89         self.RangeSizer = wx.FlexGridSizer(cols=8, hgap=0, rows=1, vgap=0)
    85 
    90 
    86         self._init_coll_MainGridSizer_Items(self.MainGridSizer)
    91         self._init_coll_MainGridSizer_Items(self.MainGridSizer)
    87         self._init_coll_MainGridSizer_Growables(self.MainGridSizer)
    92         self._init_coll_MainGridSizer_Growables(self.MainGridSizer)
    88         self._init_coll_RangeSizer_Items(self.RangeSizer)
    93         self._init_coll_RangeSizer_Items(self.RangeSizer)
    89         self._init_coll_RangeSizer_Growables(self.RangeSizer)
    94         self._init_coll_RangeSizer_Growables(self.RangeSizer)
   106                     return lower - border, upper + border
   111                     return lower - border, upper + border
   107             else:
   112             else:
   108                 return plot.PlotCanvas._axisInterval(self.Canvas, spec, lower, upper)
   113                 return plot.PlotCanvas._axisInterval(self.Canvas, spec, lower, upper)
   109         self.Canvas._axisInterval = _axisInterval
   114         self.Canvas._axisInterval = _axisInterval
   110         self.Canvas.SetYSpec('border')
   115         self.Canvas.SetYSpec('border')
   111 
   116         self.Canvas.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasLeftDown)
       
   117         self.Canvas.canvas.Bind(wx.EVT_LEFT_UP, self.OnCanvasLeftUp)
       
   118         self.Canvas.canvas.Bind(wx.EVT_MOTION, self.OnCanvasMotion)
       
   119         self.Canvas.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasMouseWheel)
       
   120         
   112         self.staticbox1 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT1,
   121         self.staticbox1 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT1,
   113               label=_('Range:'), name='staticText1', parent=self.Editor,
   122               label=_('Range:'), name='staticText1', parent=self.Editor,
   114               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
   123               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
   115         
   124         
   116         self.CanvasRange = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASRANGE,
   125         self.CanvasRange = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASRANGE,
   117               name='CanvasRange', parent=self.Editor, pos=wx.Point(0, 0),
   126               name='CanvasRange', parent=self.Editor, pos=wx.Point(0, 0),
   118               size=wx.Size(100, 28), style=wx.CB_READONLY)
   127               size=wx.Size(100, 28), style=wx.CB_READONLY)
   119         self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, id=ID_GRAPHICVIEWERCANVASRANGE)
   128         self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, id=ID_GRAPHICVIEWERCANVASRANGE)
       
   129         
       
   130         self.staticbox3 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT3,
       
   131               label=_('Zoom:'), name='staticText3', parent=self.Editor,
       
   132               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   133         
       
   134         self.CanvasZoom = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASZOOM,
       
   135               name='CanvasZoom', parent=self.Editor, pos=wx.Point(0, 0),
       
   136               size=wx.Size(70, 28), style=wx.CB_READONLY)
       
   137         self.Bind(wx.EVT_COMBOBOX, self.OnZoomChanged, id=ID_GRAPHICVIEWERCANVASZOOM)
   120         
   138         
   121         self.staticText2 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT2,
   139         self.staticText2 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT2,
   122               label=_('Position:'), name='staticText2', parent=self.Editor,
   140               label=_('Position:'), name='staticText2', parent=self.Editor,
   123               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
   141               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
   124 
   142 
   153         EditorPanel.__init__(self, parent, "", window, None)
   171         EditorPanel.__init__(self, parent, "", window, None)
   154         DebugViewer.__init__(self, producer, True, False)
   172         DebugViewer.__init__(self, producer, True, False)
   155         
   173         
   156         self.InstancePath = instancepath
   174         self.InstancePath = instancepath
   157         self.RangeValues = None
   175         self.RangeValues = None
       
   176         self.CursorIdx = None
       
   177         self.LastCursor = None
       
   178         self.CurrentMousePos = None
       
   179         self.CurrentMotionValue = None
       
   180         self.Dragging = False
       
   181         
       
   182         # Initialize Viewer mode to Selection mode
       
   183         self.Mode = MODE_SELECTION
   158         
   184         
   159         self.Datas = []
   185         self.Datas = []
   160         self.StartValue = 0
   186         self.StartTick = 0
   161         self.EndValue = 0
   187         self.EndTick = 0
       
   188         self.StartIdx = 0
       
   189         self.EndIdx = 0
       
   190         self.MinValue = None
       
   191         self.MaxValue = None
       
   192         self.YCenter = 0
       
   193         self.CurrentZoom = 1
   162         self.Fixed = False
   194         self.Fixed = False
   163         self.Ticktime = self.DataProducer.GetTicktime()
   195         self.Ticktime = self.DataProducer.GetTicktime()
   164         self.RefreshCanvasRange()
   196         self.RefreshCanvasRange()
       
   197         
       
   198         for zoom_txt, zoom in ZOOM_VALUES:
       
   199             self.CanvasZoom.Append(zoom_txt)
       
   200         self.CanvasZoom.SetSelection(0)
   165         
   201         
   166         self.AddDataConsumer(self.InstancePath.upper(), self)
   202         self.AddDataConsumer(self.InstancePath.upper(), self)
   167     
   203     
   168     def __del__(self):
   204     def __del__(self):
   169         DebugViewer.__del__(self)
   205         DebugViewer.__del__(self)
   172     def GetTitle(self):
   208     def GetTitle(self):
   173         if len(self.InstancePath) > 15:
   209         if len(self.InstancePath) > 15:
   174             return "..." + self.InstancePath[-12:]
   210             return "..." + self.InstancePath[-12:]
   175         return self.InstancePath
   211         return self.InstancePath
   176     
   212     
   177     def ResetView(self):
   213     # Changes Viewer mode
       
   214     def SetMode(self, mode):
       
   215         if self.Mode != mode or mode == MODE_SELECTION:    
       
   216             if self.Mode == MODE_MOTION:
       
   217                 wx.CallAfter(self.Canvas.canvas.SetCursor, wx.NullCursor)
       
   218             self.Mode = mode
       
   219         if self.Mode == MODE_MOTION:
       
   220             wx.CallAfter(self.Canvas.canvas.SetCursor, wx.StockCursor(wx.CURSOR_HAND))
       
   221         
       
   222     def ResetView(self, register=False):
   178         self.Datas = []
   223         self.Datas = []
   179         self.StartValue = 0
   224         self.StartTick = 0
   180         self.EndValue = 0
   225         self.EndTick = 0
       
   226         self.StartIdx = 0
       
   227         self.EndIdx = 0
   181         self.Fixed = False
   228         self.Fixed = False
   182         self.Ticktime = self.DataProducer.GetTicktime()
   229         self.Ticktime = self.DataProducer.GetTicktime()
       
   230         if register:
       
   231             self.AddDataConsumer(self.InstancePath.upper(), self)
   183         self.RefreshCanvasRange()
   232         self.RefreshCanvasRange()
   184         self.RefreshView()
   233         self.RefreshView()
   185     
   234     
   186     def RefreshNewData(self, *args, **kwargs):
   235     def RefreshNewData(self, *args, **kwargs):
   187         self.RefreshView(*args, **kwargs)
   236         self.RefreshView(*args, **kwargs)
   188         DebugViewer.RefreshNewData(self)
   237         DebugViewer.RefreshNewData(self)
   189     
       
   190     def RefreshCanvasRange(self):
       
   191         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
       
   192             self.RangeValues = RANGE_VALUES
       
   193             self.RangeValues_dict = dict(RANGE_VALUES)
       
   194             self.CanvasRange.Clear()
       
   195             for text, value in RANGE_VALUES:
       
   196                 self.CanvasRange.Append(text)
       
   197             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
       
   198             self.CurrentRange = RANGE_VALUES[0][1]
       
   199         elif self.RangeValues != TIME_RANGE_VALUES:
       
   200             self.RangeValues = TIME_RANGE_VALUES
       
   201             self.RangeValues_dict = dict(TIME_RANGE_VALUES)
       
   202             self.CanvasRange.Clear()
       
   203             for text, value in TIME_RANGE_VALUES:
       
   204                 self.CanvasRange.Append(text)
       
   205             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
       
   206             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
       
   207         
       
   208     def RefreshView(self, force=True):
       
   209         self.Freeze()
       
   210         if force or not self.Fixed:
       
   211             var_name = self.InstancePath.split(".")[-1]
       
   212             
       
   213             self.VariableGraphic = plot.PolyLine(self.Datas[self.StartValue:self.EndValue + 1], 
       
   214                                                  legend=var_name, colour=colours[0])
       
   215             self.GraphicsObject = plot.PlotGraphics([self.VariableGraphic], _("%s Graphics") % var_name, _("Tick"), _("Values"))
       
   216             datas_length = len(self.Datas)
       
   217             if datas_length > 1:
       
   218                 start = self.Datas[self.StartValue][0]
       
   219             else:
       
   220                 start = 0.
       
   221             self.Canvas.Draw(self.GraphicsObject, xAxis=(start, start + self.CurrentRange))
       
   222         self.RefreshScrollBar()
       
   223         self.Thaw()
       
   224     
       
   225     def GetInstancePath(self):
       
   226         return self.InstancePath
       
   227     
       
   228     def IsViewing(self, tagname):
       
   229         return self.InstancePath == tagname
       
   230     
   238     
   231     def GetNearestData(self, tick, adjust):
   239     def GetNearestData(self, tick, adjust):
   232         ticks = numpy.array(zip(*self.Datas)[0])
   240         ticks = numpy.array(zip(*self.Datas)[0])
   233         new_cursor = numpy.argmin(abs(ticks - tick))
   241         new_cursor = numpy.argmin(abs(ticks - tick))
   234         if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   242         if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   235             new_cursor -= 1
   243             new_cursor -= 1
   236         elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(self.Datas):
   244         elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(self.Datas):
   237             new_cursor += 1
   245             new_cursor += 1
   238         return new_cursor
   246         return new_cursor
   239     
   247     
       
   248     def GetBounds(self):
       
   249         if self.StartIdx is None or self.EndIdx is None:
       
   250             self.StartIdx = self.GetNearestData(self.StartTick, -1)
       
   251             self.EndIdx = self.GetNearestData(self.EndTick, 1)
       
   252     
       
   253     def ResetBounds(self):
       
   254         self.StartIdx = None
       
   255         self.EndIdx = None
       
   256     
       
   257     def RefreshCanvasRange(self):
       
   258         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
       
   259             self.RangeValues = RANGE_VALUES
       
   260             self.CanvasRange.Clear()
       
   261             for text, value in RANGE_VALUES:
       
   262                 self.CanvasRange.Append(text)
       
   263             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
       
   264             self.CurrentRange = RANGE_VALUES[0][1]
       
   265         elif self.RangeValues != TIME_RANGE_VALUES:
       
   266             self.RangeValues = TIME_RANGE_VALUES
       
   267             self.CanvasRange.Clear()
       
   268             for text, value in TIME_RANGE_VALUES:
       
   269                 self.CanvasRange.Append(text)
       
   270             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
       
   271             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
       
   272         
       
   273     def RefreshView(self, force=True):
       
   274         self.Freeze()
       
   275         if force or not self.Fixed:
       
   276             if (self.MinValue is not None and 
       
   277                 self.MaxValue is not None and 
       
   278                 self.MinValue != self.MaxValue):
       
   279                 Yrange = float(self.MaxValue - self.MinValue) / self.CurrentZoom
       
   280             else:
       
   281                 Yrange = 2. / self.CurrentZoom
       
   282             
       
   283             if not self.Fixed and len(self.Datas) > 0:
       
   284                 self.YCenter = max(self.Datas[-1][1] - Yrange / 2, 
       
   285                                min(self.YCenter, 
       
   286                                    self.Datas[-1][1] + Yrange / 2))
       
   287             
       
   288             var_name = self.InstancePath.split(".")[-1]
       
   289             
       
   290             self.GetBounds()
       
   291             self.VariableGraphic = plot.PolyLine(self.Datas[self.StartIdx:self.EndIdx + 1], 
       
   292                                                  legend=var_name, colour=colours[0])
       
   293             self.GraphicsObject = plot.PlotGraphics([self.VariableGraphic], _("%s Graphics") % var_name, _("Tick"), _("Values"))
       
   294             datas_length = len(self.Datas)
       
   295             if datas_length > 1:
       
   296                 start = self.Datas[self.StartIdx][0]
       
   297             else:
       
   298                 start = 0.
       
   299             self.Canvas.Draw(self.GraphicsObject, 
       
   300                              xAxis=(start, start + self.CurrentRange),
       
   301                              yAxis=(self.YCenter - Yrange * 1.1 / 2, self.YCenter + Yrange * 1.1 / 2))
       
   302         self.RefreshScrollBar()
       
   303         
       
   304         # Reset and draw cursor 
       
   305         self.ResetLastCursor()
       
   306         self.RefreshCursor()
       
   307         
       
   308         self.Thaw()
       
   309     
       
   310     def GetInstancePath(self):
       
   311         return self.InstancePath
       
   312     
       
   313     def IsViewing(self, tagname):
       
   314         return self.InstancePath == tagname
       
   315     
   240     def NewValue(self, tick, value, forced=False):
   316     def NewValue(self, tick, value, forced=False):
   241         self.Datas.append((float(tick), {True:1., False:0.}.get(value, float(value))))
   317         self.Datas.append((float(tick), {True:1., False:0.}.get(value, float(value))))
       
   318         if self.MinValue is None:
       
   319             self.MinValue = value
       
   320         else:
       
   321             self.MinValue = min(self.MinValue, value)
       
   322         if self.MaxValue is None:
       
   323             self.MaxValue = value
       
   324         else:
       
   325             self.MaxValue = max(self.MaxValue, value)
   242         if not self.Fixed:
   326         if not self.Fixed:
   243             while int(self.Datas[self.StartValue][0]) < tick - self.CurrentRange:
   327             self.GetBounds()
   244                 self.StartValue += 1
   328             while int(self.Datas[self.StartIdx][0]) < tick - self.CurrentRange:
   245             self.EndValue += 1
   329                 self.StartIdx += 1
       
   330             self.EndIdx += 1
       
   331             self.StartTick = self.Datas[self.StartIdx][0]
       
   332             self.EndTick = self.StartTick + self.CurrentRange
   246         self.NewDataAvailable()
   333         self.NewDataAvailable()
   247     
   334     
   248     def RefreshScrollBar(self):
   335     def RefreshScrollBar(self):
   249         if len(self.Datas) > 0:
   336         if len(self.Datas) > 0:
   250             pos = int(self.Datas[self.StartValue][0] - self.Datas[0][0])
   337             self.GetBounds()
       
   338             pos = int(self.Datas[self.StartIdx][0] - self.Datas[0][0])
   251             range = int(self.Datas[-1][0] - self.Datas[0][0])
   339             range = int(self.Datas[-1][0] - self.Datas[0][0])
   252         else:
   340         else:
   253             pos = 0
   341             pos = 0
   254             range = 0
   342             range = 0
   255         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
   343         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
   256 
   344 
   257     def OnRangeChanged(self, event):
   345     def RefreshRange(self):
   258         old_range = self.CurrentRange
       
   259         try:
       
   260             if self.Ticktime == 0:
       
   261                 self.CurrentRange = self.RangeValues_dict[self.CanvasRange.GetValue()]
       
   262             else:
       
   263                 self.CurrentRange = self.RangeValues_dict[self.CanvasRange.GetValue()] / self.Ticktime
       
   264         except ValueError, e:
       
   265             self.CanvasRange.SetValue(str(self.CurrentRange))
       
   266         if len(self.Datas) > 0:
   346         if len(self.Datas) > 0:
   267             if self.Fixed and self.Datas[-1][0] - self.Datas[0][0] < self.CurrentRange:
   347             if self.Fixed and self.Datas[-1][0] - self.Datas[0][0] < self.CurrentRange:
   268                 self.Fixed = False
   348                 self.Fixed = False
       
   349             self.ResetBounds()
   269             if self.Fixed:
   350             if self.Fixed:
   270                 self.StartValue = min(self.StartValue, self.GetNearestData(self.Datas[-1][0] - self.CurrentRange, -1))
   351                 self.StartTick = min(self.StartTick, self.Datas[-1][0] - self.CurrentRange)
   271                 self.EndValue = self.GetNearestData(self.StartValue + self.CurrentRange, 1)
       
   272             else:
   352             else:
   273                 self.StartValue = self.GetNearestData(self.Datas[-1][0] - self.CurrentRange - 1, -1)
   353                 self.StartTick = max(self.Datas[0][0], self.EndTick - self.CurrentRange - 1)
   274                 self.EndValue = len(self.Datas) - 1
   354             self.EndTick = self.StartTick + self.CurrentRange
   275         self.NewDataAvailable(True)
   355         self.NewDataAvailable(True)
       
   356 
       
   357     def OnRangeChanged(self, event):
       
   358         try:
       
   359             if self.Ticktime == 0:
       
   360                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
       
   361             else:
       
   362                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime
       
   363         except ValueError, e:
       
   364             self.CanvasRange.SetValue(str(self.CurrentRange))
       
   365         wx.CallAfter(self.RefreshRange)
       
   366         event.Skip()
       
   367     
       
   368     def OnZoomChanged(self, event):
       
   369         self.CurrentZoom = ZOOM_VALUES[self.CanvasZoom.GetSelection()][1]
       
   370         wx.CallAfter(self.NewDataAvailable, True)
   276         event.Skip()
   371         event.Skip()
   277     
   372     
   278     def OnPositionChanging(self, event):
   373     def OnPositionChanging(self, event):
   279         self.StartValue = self.GetNearestData(self.Datas[0][0] + event.GetPosition(), -1)
   374         self.ResetBounds()
   280         self.EndValue = self.GetNearestData(self.Datas[self.StartValue][0] + self.CurrentRange, 1)
   375         self.StartTick = self.Datas[0][0] + event.GetPosition()
       
   376         self.EndTick = self.StartTick + self.CurrentRange
   281         self.Fixed = True
   377         self.Fixed = True
   282         self.NewDataAvailable(True)
   378         self.NewDataAvailable(True)
   283         event.Skip()
   379         event.Skip()
   284 
   380 
   285     def OnResetButton(self, event):
   381     def OnResetButton(self, event):
   286         self.Fixed = False
   382         self.Fixed = False
   287         self.ResteView()
   383         self.ResetView()
   288         event.Skip()
   384         event.Skip()
   289 
   385 
   290     def OnCurrentButton(self, event):
   386     def OnCurrentButton(self, event):
   291         self.StartValue = self.GetNearestData(self.Datas[-1][0] - self.CurrentRange, -1)
   387         self.ResetBounds()
   292         self.EndValue = self.GetNearestData(self.Datas[self.StartValue][0] + self.CurrentRange, 1)
   388         self.StartTick = max(self.Datas[0][0], self.Datas[-1][0] - self.CurrentRange)
       
   389         self.EndTick = self.StartTick + self.CurrentRange
   293         self.Fixed = False
   390         self.Fixed = False
   294         self.NewDataAvailable(True)
   391         self.NewDataAvailable(True)
   295         event.Skip()
   392         event.Skip()
   296 
   393 
       
   394     def OnCanvasLeftDown(self, event):
       
   395         self.Fixed = True
       
   396         self.Canvas.canvas.CaptureMouse()
       
   397         if self.Mode == MODE_SELECTION:
       
   398             self.Dragging = True
       
   399             pos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   400             self.CursorIdx = self.GetNearestData(pos[0], -1)
       
   401             self.RefreshCursor()
       
   402         elif self.Mode == MODE_MOTION:
       
   403             self.GetBounds()
       
   404             self.CurrentMousePos = event.GetPosition()
       
   405             self.CurrentMotionValue = self.Datas[self.StartIdx][0]
       
   406         event.Skip()
       
   407         
       
   408     def OnCanvasLeftUp(self, event):
       
   409         self.Dragging = False
       
   410         if self.Mode == MODE_MOTION:
       
   411             self.CurrentMousePos = None
       
   412             self.CurrentMotionValue = None
       
   413         if self.Canvas.canvas.HasCapture():
       
   414             self.Canvas.canvas.ReleaseMouse()
       
   415         event.Skip()
       
   416         
       
   417     def OnCanvasMotion(self, event):
       
   418         if self.Mode == MODE_SELECTION and self.Dragging:
       
   419             pos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   420             graphics, xAxis, yAxis = self.Canvas.last_draw
       
   421             self.CursorIdx = self.GetNearestData(max(xAxis[0], min(pos[0], xAxis[1])), -1)
       
   422             self.RefreshCursor()
       
   423         elif self.CurrentMousePos is not None:
       
   424             oldpos = self.Canvas.PositionScreenToUser(self.CurrentMousePos)
       
   425             newpos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   426             self.CurrentMotionValue += oldpos[0] - newpos[0]
       
   427             self.YCenter += oldpos[1] - newpos[1]
       
   428             self.ResetBounds()
       
   429             self.StartTick = self.CurrentMotionValue
       
   430             self.EndTick = self.StartTick + self.CurrentRange
       
   431             self.CurrentMousePos = event.GetPosition()
       
   432             self.NewDataAvailable(True)
       
   433         event.Skip()
       
   434 
       
   435     def OnCanvasMouseWheel(self, event):
       
   436         if self.CurrentMousePos is None:
       
   437             rotation = event.GetWheelRotation() / event.GetWheelDelta()
       
   438             if event.ShiftDown():
       
   439                 current = self.CanvasRange.GetSelection()
       
   440                 new = max(0, min(current - rotation, len(self.RangeValues) - 1))
       
   441                 if new != current:
       
   442                     if self.Ticktime == 0:
       
   443                         self.CurrentRange = self.RangeValues[new][1]
       
   444                     else:
       
   445                         self.CurrentRange = self.RangeValues[new][1] / self.Ticktime
       
   446                     self.CanvasRange.SetStringSelection(self.RangeValues[new][0])
       
   447                     wx.CallAfter(self.RefreshRange)
       
   448             else:
       
   449                 current = self.CanvasZoom.GetSelection()
       
   450                 new = max(0, min(current - rotation, len(ZOOM_VALUES) - 1))
       
   451                 if new != current:
       
   452                     self.CurrentZoom = ZOOM_VALUES[new][1]
       
   453                     self.CanvasZoom.SetStringSelection(ZOOM_VALUES[new][0])
       
   454                     wx.CallAfter(self.NewDataAvailable, True)
       
   455         event.Skip()
       
   456 
       
   457     ## Reset the last cursor
       
   458     def ResetLastCursor(self):
       
   459         self.LastCursor = None
       
   460 
       
   461     ## Draw the cursor on graphic
       
   462     #  @param dc The draw canvas
       
   463     #  @param cursor The cursor parameters
       
   464     def DrawCursor(self, dc, cursor, value):
       
   465         if self.StartTick <= cursor <= self.EndTick:
       
   466             # Prepare temporary dc for drawing
       
   467             width = self.Canvas._Buffer.GetWidth()
       
   468             height = self.Canvas._Buffer.GetHeight()
       
   469             tmp_Buffer = wx.EmptyBitmap(width, height)
       
   470             dcs = wx.MemoryDC()
       
   471             dcs.SelectObject(tmp_Buffer)
       
   472             dcs.Clear()
       
   473             dcs.BeginDrawing()
       
   474             
       
   475             dcs.SetPen(wx.Pen(wx.RED))
       
   476             dcs.SetBrush(wx.Brush(wx.RED, wx.SOLID))
       
   477             dcs.SetFont(self.Canvas._getFont(self.Canvas._fontSizeAxis))
       
   478             
       
   479             # Calculate clipping region
       
   480             graphics, xAxis, yAxis = self.Canvas.last_draw
       
   481             p1 = numpy.array([xAxis[0], yAxis[0]])
       
   482             p2 = numpy.array([xAxis[1], yAxis[1]])
       
   483             cx, cy, cwidth, cheight = self.Canvas._point2ClientCoord(p1, p2)
       
   484             
       
   485             px, py = self.Canvas.PositionUserToScreen((float(cursor), 0.))
       
   486             
       
   487             # Draw line cross drawing for diaplaying time cursor
       
   488             dcs.DrawLine(px, cy + 1, px, cy + cheight - 1)
       
   489             
       
   490             text = "X:%d\nY:%f"%(cursor, value)
       
   491             w, h = dcs.GetTextExtent(text)
       
   492             # Draw time cursor date
       
   493             dcs.DrawText(text, min(px + 3, cx + cwidth - w), cy + 3)
       
   494             
       
   495             dcs.EndDrawing()
       
   496     
       
   497             #this will erase if called twice
       
   498             dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV)  #(NOT src) XOR dst
       
   499     
       
   500     ## Refresh the variable cursor.
       
   501     #  @param dc The draw canvas
       
   502     def RefreshCursor(self, dc=None):
       
   503         if dc is None:
       
   504             dc = wx.BufferedDC(wx.ClientDC(self.Canvas.canvas), self.Canvas._Buffer)
       
   505         
       
   506         # Erase previous time cursor if drawn
       
   507         if self.LastCursor is not None:
       
   508             self.DrawCursor(dc, *self.LastCursor)
       
   509         
       
   510         # Draw new time cursor
       
   511         if self.CursorIdx is not None:
       
   512             self.LastCursor = self.Datas[self.CursorIdx]
       
   513             self.DrawCursor(dc, *self.LastCursor)