editors/GraphicViewer.py
changeset 814 5743cbdff669
child 887 d3c6c4ab8b28
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import numpy
       
    26 import math
       
    27 
       
    28 import wx
       
    29 import wx.lib.plot as plot
       
    30 import wx.lib.buttons
       
    31 
       
    32 from graphics.GraphicCommons import DebugViewer, MODE_SELECTION, MODE_MOTION
       
    33 from EditorPanel import EditorPanel
       
    34 from util.BitmapLibrary import GetBitmap
       
    35 
       
    36 colours = ['blue', 'red', 'green', 'yellow', 'orange', 'purple', 'brown', 'cyan',
       
    37            'pink', 'grey']
       
    38 markers = ['circle', 'dot', 'square', 'triangle', 'triangle_down', 'cross', 'plus', 'circle']
       
    39 
       
    40 
       
    41 #-------------------------------------------------------------------------------
       
    42 #                       Debug Variable Graphic Viewer class
       
    43 #-------------------------------------------------------------------------------
       
    44 
       
    45 SECOND = 1000000000
       
    46 MINUTE = 60 * SECOND
       
    47 HOUR = 60 * MINUTE
       
    48 
       
    49 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
    50 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
       
    51 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
       
    52                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
       
    53                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
       
    54 
       
    55 class GraphicViewer(EditorPanel, DebugViewer):
       
    56 
       
    57     def _init_Editor(self, prnt):
       
    58         self.Editor = wx.Panel(prnt)
       
    59         
       
    60         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
    61         main_sizer.AddGrowableCol(0)
       
    62         main_sizer.AddGrowableRow(0)
       
    63         
       
    64         self.Canvas = plot.PlotCanvas(self.Editor, name='Canvas')
       
    65         def _axisInterval(spec, lower, upper):
       
    66             if spec == 'border':
       
    67                 if lower == upper:
       
    68                     return lower - 0.5, upper + 0.5
       
    69                 else:
       
    70                     border = (upper - lower) * 0.05
       
    71                     return lower - border, upper + border
       
    72             else:
       
    73                 return plot.PlotCanvas._axisInterval(self.Canvas, spec, lower, upper)
       
    74         self.Canvas._axisInterval = _axisInterval
       
    75         self.Canvas.SetYSpec('border')
       
    76         self.Canvas.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasLeftDown)
       
    77         self.Canvas.canvas.Bind(wx.EVT_LEFT_UP, self.OnCanvasLeftUp)
       
    78         self.Canvas.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.OnCanvasMiddleDown)
       
    79         self.Canvas.canvas.Bind(wx.EVT_MIDDLE_UP, self.OnCanvasMiddleUp)
       
    80         self.Canvas.canvas.Bind(wx.EVT_MOTION, self.OnCanvasMotion)
       
    81         self.Canvas.canvas.Bind(wx.EVT_SIZE, self.OnCanvasResize)
       
    82         main_sizer.AddWindow(self.Canvas, 0, border=0, flag=wx.GROW)
       
    83         
       
    84         range_sizer = wx.FlexGridSizer(cols=10, hgap=5, rows=1, vgap=0)
       
    85         range_sizer.AddGrowableCol(5)
       
    86         range_sizer.AddGrowableRow(0)
       
    87         main_sizer.AddSizer(range_sizer, 0, border=5, flag=wx.GROW|wx.ALL)
       
    88         
       
    89         range_label = wx.StaticText(self.Editor, label=_('Range:'))
       
    90         range_sizer.AddWindow(range_label, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
    91         
       
    92         self.CanvasRange = wx.ComboBox(self.Editor, 
       
    93               size=wx.Size(100, 28), style=wx.CB_READONLY)
       
    94         self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
       
    95         range_sizer.AddWindow(self.CanvasRange, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
    96         
       
    97         zoom_label = wx.StaticText(self.Editor, label=_('Zoom:'))
       
    98         range_sizer.AddWindow(zoom_label, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
    99         
       
   100         self.CanvasZoom = wx.ComboBox(self.Editor, 
       
   101               size=wx.Size(70, 28), style=wx.CB_READONLY)
       
   102         self.Bind(wx.EVT_COMBOBOX, self.OnZoomChanged, self.CanvasZoom)
       
   103         range_sizer.AddWindow(self.CanvasZoom, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
   104         
       
   105         position_label = wx.StaticText(self.Editor, label=_('Position:'))
       
   106         range_sizer.AddWindow(position_label, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
   107         
       
   108         self.CanvasPosition = wx.ScrollBar(self.Editor, 
       
   109               size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
       
   110         self.CanvasPosition.SetScrollbar(0, 10, 100, 10)
       
   111         self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
       
   112               self.OnPositionChanging, self.CanvasPosition)
       
   113         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
       
   114               self.OnPositionChanging, self.CanvasPosition)
       
   115         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, 
       
   116               self.OnPositionChanging, self.CanvasPosition)
       
   117         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
       
   118               self.OnPositionChanging, self.CanvasPosition)
       
   119         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
       
   120               self.OnPositionChanging, self.CanvasPosition)
       
   121         range_sizer.AddWindow(self.CanvasPosition, 0, border=5, flag=wx.GROW|wx.ALL)
       
   122         
       
   123         self.ResetButton = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   124               bitmap=GetBitmap("reset"), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   125         self.ResetButton.SetToolTipString(_("Clear the graph values"))
       
   126         self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton)
       
   127         range_sizer.AddWindow(self.ResetButton, 0, border=0, flag=0)
       
   128         
       
   129         self.CurrentButton = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   130               bitmap=GetBitmap("current"), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   131         self.CurrentButton.SetToolTipString(_("Go to current value"))
       
   132         self.Bind(wx.EVT_BUTTON, self.OnCurrentButton, self.CurrentButton)
       
   133         range_sizer.AddWindow(self.CurrentButton, 0, border=0, flag=0)
       
   134         
       
   135         self.ResetZoomOffsetButton = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   136               bitmap=GetBitmap("fit"), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   137         self.ResetZoomOffsetButton.SetToolTipString(_("Reset zoom and offset"))
       
   138         self.Bind(wx.EVT_BUTTON, self.OnResetZoomOffsetButton, 
       
   139               self.ResetZoomOffsetButton)
       
   140         range_sizer.AddWindow(self.ResetZoomOffsetButton, 0, border=0, flag=0)
       
   141         
       
   142         self.ExportGraphButton = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   143               bitmap=GetBitmap("export_graph"), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   144         self.ExportGraphButton.SetToolTipString(_("Export graph values to clipboard"))
       
   145         self.Bind(wx.EVT_BUTTON, self.OnExportGraphButtonClick, 
       
   146                 self.ExportGraphButton)
       
   147         range_sizer.AddWindow(self.ExportGraphButton, 0, border=0, flag=0)
       
   148         
       
   149         self.Editor.SetSizer(main_sizer)
       
   150 
       
   151         self.Editor.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasMouseWheel)
       
   152 
       
   153     def __init__(self, parent, window, producer, instancepath = ""):
       
   154         EditorPanel.__init__(self, parent, "", window, None)
       
   155         DebugViewer.__init__(self, producer, True, False)
       
   156         
       
   157         self.InstancePath = instancepath
       
   158         self.RangeValues = None
       
   159         self.CursorIdx = None
       
   160         self.LastCursor = None
       
   161         self.CurrentMousePos = None
       
   162         self.CurrentMotionValue = None
       
   163         self.Dragging = False
       
   164         
       
   165         # Initialize Viewer mode to Selection mode
       
   166         self.Mode = MODE_SELECTION
       
   167         
       
   168         self.Datas = []
       
   169         self.StartTick = 0
       
   170         self.StartIdx = 0
       
   171         self.EndIdx = 0
       
   172         self.MinValue = None
       
   173         self.MaxValue = None
       
   174         self.YCenter = 0
       
   175         self.CurrentZoom = 1.0
       
   176         self.Fixed = False
       
   177         self.Ticktime = self.DataProducer.GetTicktime()
       
   178         self.RefreshCanvasRange()
       
   179         
       
   180         for zoom_txt, zoom in ZOOM_VALUES:
       
   181             self.CanvasZoom.Append(zoom_txt)
       
   182         self.CanvasZoom.SetSelection(0)
       
   183         
       
   184         self.AddDataConsumer(self.InstancePath.upper(), self)
       
   185     
       
   186     def __del__(self):
       
   187         DebugViewer.__del__(self)
       
   188         self.RemoveDataConsumer(self)
       
   189     
       
   190     def GetTitle(self):
       
   191         if len(self.InstancePath) > 15:
       
   192             return "..." + self.InstancePath[-12:]
       
   193         return self.InstancePath
       
   194     
       
   195     # Changes Viewer mode
       
   196     def SetMode(self, mode):
       
   197         if self.Mode != mode or mode == MODE_SELECTION:    
       
   198             if self.Mode == MODE_MOTION:
       
   199                 wx.CallAfter(self.Canvas.canvas.SetCursor, wx.NullCursor)
       
   200             self.Mode = mode
       
   201         if self.Mode == MODE_MOTION:
       
   202             wx.CallAfter(self.Canvas.canvas.SetCursor, wx.StockCursor(wx.CURSOR_HAND))
       
   203         
       
   204     def ResetView(self, register=False):
       
   205         self.Datas = []
       
   206         self.StartTick = 0
       
   207         self.StartIdx = 0
       
   208         self.EndIdx = 0
       
   209         self.MinValue = None
       
   210         self.MaxValue = None
       
   211         self.CursorIdx = None
       
   212         self.Fixed = False
       
   213         self.Ticktime = self.DataProducer.GetTicktime()
       
   214         if register:
       
   215             self.AddDataConsumer(self.InstancePath.upper(), self)
       
   216         self.ResetLastCursor()
       
   217         self.RefreshCanvasRange()
       
   218         self.RefreshView()
       
   219     
       
   220     def RefreshNewData(self, *args, **kwargs):
       
   221         self.RefreshView(*args, **kwargs)
       
   222         DebugViewer.RefreshNewData(self)
       
   223     
       
   224     def GetNearestData(self, tick, adjust):
       
   225         ticks = numpy.array(zip(*self.Datas)[0])
       
   226         new_cursor = numpy.argmin(abs(ticks - tick))
       
   227         if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
       
   228             new_cursor -= 1
       
   229         elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(self.Datas):
       
   230             new_cursor += 1
       
   231         return new_cursor
       
   232     
       
   233     def GetBounds(self):
       
   234         if self.StartIdx is None or self.EndIdx is None:
       
   235             self.StartIdx = self.GetNearestData(self.StartTick, -1)
       
   236             self.EndIdx = self.GetNearestData(self.StartTick + self.CurrentRange, 1)
       
   237     
       
   238     def ResetBounds(self):
       
   239         self.StartIdx = None
       
   240         self.EndIdx = None
       
   241     
       
   242     def RefreshCanvasRange(self):
       
   243         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
       
   244             self.RangeValues = RANGE_VALUES
       
   245             self.CanvasRange.Clear()
       
   246             for text, value in RANGE_VALUES:
       
   247                 self.CanvasRange.Append(text)
       
   248             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
       
   249             self.CurrentRange = RANGE_VALUES[0][1]
       
   250         elif self.RangeValues != TIME_RANGE_VALUES:
       
   251             self.RangeValues = TIME_RANGE_VALUES
       
   252             self.CanvasRange.Clear()
       
   253             for text, value in TIME_RANGE_VALUES:
       
   254                 self.CanvasRange.Append(text)
       
   255             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
       
   256             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
       
   257         
       
   258     def RefreshView(self, force=False):
       
   259         self.Freeze()
       
   260         if force or not self.Fixed or (len(self.Datas) > 0 and self.StartTick + self.CurrentRange > self.Datas[-1][0]):
       
   261             if (self.MinValue is not None and 
       
   262                 self.MaxValue is not None and 
       
   263                 self.MinValue != self.MaxValue):
       
   264                 Yrange = float(self.MaxValue - self.MinValue) / self.CurrentZoom
       
   265             else:
       
   266                 Yrange = 2. / self.CurrentZoom
       
   267             
       
   268             if not force and not self.Fixed and len(self.Datas) > 0:
       
   269                 self.YCenter = max(self.Datas[-1][1] - Yrange / 2, 
       
   270                                min(self.YCenter, 
       
   271                                    self.Datas[-1][1] + Yrange / 2))
       
   272             
       
   273             var_name = self.InstancePath.split(".")[-1]
       
   274             
       
   275             self.GetBounds()
       
   276             self.VariableGraphic = plot.PolyLine(self.Datas[self.StartIdx:self.EndIdx + 1], 
       
   277                                                  legend=var_name, colour=colours[0])
       
   278             self.GraphicsObject = plot.PlotGraphics([self.VariableGraphic], _("%s Graphics") % var_name, _("Tick"), _("Values"))
       
   279             self.Canvas.Draw(self.GraphicsObject, 
       
   280                              xAxis=(self.StartTick, self.StartTick + self.CurrentRange),
       
   281                              yAxis=(self.YCenter - Yrange * 1.1 / 2., self.YCenter + Yrange * 1.1 / 2.))
       
   282         
       
   283             # Reset and draw cursor 
       
   284             self.ResetLastCursor()
       
   285             self.RefreshCursor()
       
   286         
       
   287         self.RefreshScrollBar()
       
   288         
       
   289         self.Thaw()
       
   290     
       
   291     def GetInstancePath(self):
       
   292         return self.InstancePath
       
   293     
       
   294     def IsViewing(self, tagname):
       
   295         return self.InstancePath == tagname
       
   296     
       
   297     def NewValue(self, tick, value, forced=False):
       
   298         value = {True:1., False:0.}.get(value, float(value))
       
   299         self.Datas.append((float(tick), value))
       
   300         if self.MinValue is None:
       
   301             self.MinValue = value
       
   302         else:
       
   303             self.MinValue = min(self.MinValue, value)
       
   304         if self.MaxValue is None:
       
   305             self.MaxValue = value
       
   306         else:
       
   307             self.MaxValue = max(self.MaxValue, value)
       
   308         if not self.Fixed or tick < self.StartTick + self.CurrentRange:
       
   309             self.GetBounds()
       
   310             while int(self.Datas[self.StartIdx][0]) < tick - self.CurrentRange:
       
   311                 self.StartIdx += 1
       
   312             self.EndIdx += 1
       
   313             self.StartTick = self.Datas[self.StartIdx][0]
       
   314         self.NewDataAvailable()
       
   315     
       
   316     def RefreshScrollBar(self):
       
   317         if len(self.Datas) > 0:
       
   318             self.GetBounds()
       
   319             pos = int(self.Datas[self.StartIdx][0] - self.Datas[0][0])
       
   320             range = int(self.Datas[-1][0] - self.Datas[0][0])
       
   321         else:
       
   322             pos = 0
       
   323             range = 0
       
   324         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
       
   325 
       
   326     def RefreshRange(self):
       
   327         if len(self.Datas) > 0:
       
   328             if self.Fixed and self.Datas[-1][0] - self.Datas[0][0] < self.CurrentRange:
       
   329                 self.Fixed = False
       
   330             self.ResetBounds()
       
   331             if self.Fixed:
       
   332                 self.StartTick = min(self.StartTick, self.Datas[-1][0] - self.CurrentRange)
       
   333             else:
       
   334                 self.StartTick = max(self.Datas[0][0], self.Datas[-1][0] - self.CurrentRange)
       
   335         self.RefreshView(True)
       
   336 
       
   337     def OnRangeChanged(self, event):
       
   338         try:
       
   339             if self.Ticktime == 0:
       
   340                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
       
   341             else:
       
   342                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime
       
   343         except ValueError, e:
       
   344             self.CanvasRange.SetValue(str(self.CurrentRange))
       
   345         wx.CallAfter(self.RefreshRange)
       
   346         event.Skip()
       
   347     
       
   348     def OnZoomChanged(self, event):
       
   349         self.CurrentZoom = ZOOM_VALUES[self.CanvasZoom.GetSelection()][1]
       
   350         wx.CallAfter(self.RefreshView, True)
       
   351         event.Skip()
       
   352     
       
   353     def OnPositionChanging(self, event):
       
   354         if len(self.Datas) > 0:
       
   355             self.ResetBounds()
       
   356             self.StartTick = self.Datas[0][0] + event.GetPosition()
       
   357             self.Fixed = True
       
   358             self.NewDataAvailable(True)
       
   359         event.Skip()
       
   360 
       
   361     def OnResetButton(self, event):
       
   362         self.Fixed = False
       
   363         self.ResetView()
       
   364         event.Skip()
       
   365 
       
   366     def OnCurrentButton(self, event):
       
   367         if len(self.Datas) > 0:
       
   368             self.ResetBounds()
       
   369             self.StartTick = max(self.Datas[0][0], self.Datas[-1][0] - self.CurrentRange)
       
   370             self.Fixed = False
       
   371             self.NewDataAvailable(True)
       
   372         event.Skip()
       
   373     
       
   374     def OnResetZoomOffsetButton(self, event):
       
   375         if len(self.Datas) > 0:
       
   376             self.YCenter = (self.MaxValue + self.MinValue) / 2
       
   377         else:
       
   378             self.YCenter = 0.0
       
   379         self.CurrentZoom = 1.0
       
   380         self.CanvasZoom.SetSelection(0)
       
   381         wx.CallAfter(self.RefreshView, True)
       
   382         event.Skip()
       
   383     
       
   384     def OnExportGraphButtonClick(self, event):
       
   385         data_copy = self.Datas[:]
       
   386         text = "tick;%s;\n" % self.InstancePath
       
   387         for tick, value in data_copy:
       
   388             text += "%d;%.3f;\n" % (tick, value)
       
   389         self.ParentWindow.SetCopyBuffer(text)
       
   390         event.Skip()
       
   391 
       
   392     def OnCanvasLeftDown(self, event):
       
   393         self.Fixed = True
       
   394         self.Canvas.canvas.CaptureMouse()
       
   395         if len(self.Datas) > 0:
       
   396             if self.Mode == MODE_SELECTION:
       
   397                 self.Dragging = True
       
   398                 pos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   399                 self.CursorIdx = self.GetNearestData(pos[0], -1)
       
   400                 self.RefreshCursor()
       
   401             elif self.Mode == MODE_MOTION:
       
   402                 self.GetBounds()
       
   403                 self.CurrentMousePos = event.GetPosition()
       
   404                 self.CurrentMotionValue = self.Datas[self.StartIdx][0]
       
   405         event.Skip()
       
   406     
       
   407     def OnCanvasLeftUp(self, event):
       
   408         self.Dragging = False
       
   409         if self.Mode == MODE_MOTION:
       
   410             self.CurrentMousePos = None
       
   411             self.CurrentMotionValue = None
       
   412         if self.Canvas.canvas.HasCapture():
       
   413             self.Canvas.canvas.ReleaseMouse()
       
   414         event.Skip()
       
   415     
       
   416     def OnCanvasMiddleDown(self, event):
       
   417         self.Fixed = True
       
   418         self.Canvas.canvas.CaptureMouse()
       
   419         if len(self.Datas) > 0:
       
   420             self.GetBounds()
       
   421             self.CurrentMousePos = event.GetPosition()
       
   422             self.CurrentMotionValue = self.Datas[self.StartIdx][0]
       
   423         event.Skip()
       
   424         
       
   425     def OnCanvasMiddleUp(self, event):
       
   426         self.CurrentMousePos = None
       
   427         self.CurrentMotionValue = None
       
   428         if self.Canvas.canvas.HasCapture():
       
   429             self.Canvas.canvas.ReleaseMouse()
       
   430         event.Skip()
       
   431         
       
   432     def OnCanvasMotion(self, event):
       
   433         if self.Mode == MODE_SELECTION and self.Dragging:
       
   434             pos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   435             graphics, xAxis, yAxis = self.Canvas.last_draw
       
   436             self.CursorIdx = self.GetNearestData(max(xAxis[0], min(pos[0], xAxis[1])), -1)
       
   437             self.RefreshCursor()
       
   438         elif self.CurrentMousePos is not None and len(self.Datas) > 0:
       
   439             oldpos = self.Canvas.PositionScreenToUser(self.CurrentMousePos)
       
   440             newpos = self.Canvas.PositionScreenToUser(event.GetPosition())
       
   441             self.CurrentMotionValue += oldpos[0] - newpos[0]
       
   442             self.YCenter += oldpos[1] - newpos[1]
       
   443             self.ResetBounds()
       
   444             self.StartTick = max(self.Datas[0][0], min(self.CurrentMotionValue, self.Datas[-1][0] - self.CurrentRange))
       
   445             self.CurrentMousePos = event.GetPosition()
       
   446             self.NewDataAvailable(True)
       
   447         event.Skip()
       
   448 
       
   449     def OnCanvasMouseWheel(self, event):
       
   450         if self.CurrentMousePos is None:
       
   451             rotation = event.GetWheelRotation() / event.GetWheelDelta()
       
   452             if event.ShiftDown():
       
   453                 current = self.CanvasRange.GetSelection()
       
   454                 new = max(0, min(current - rotation, len(self.RangeValues) - 1))
       
   455                 if new != current:
       
   456                     if self.Ticktime == 0:
       
   457                         self.CurrentRange = self.RangeValues[new][1]
       
   458                     else:
       
   459                         self.CurrentRange = self.RangeValues[new][1] / self.Ticktime
       
   460                     self.CanvasRange.SetStringSelection(self.RangeValues[new][0])
       
   461                     wx.CallAfter(self.RefreshRange)
       
   462             else:
       
   463                 current = self.CanvasZoom.GetSelection()
       
   464                 new = max(0, min(current + rotation, len(ZOOM_VALUES) - 1))
       
   465                 if new != current:
       
   466                     self.CurrentZoom = ZOOM_VALUES[new][1]
       
   467                     self.CanvasZoom.SetStringSelection(ZOOM_VALUES[new][0])
       
   468                     wx.CallAfter(self.RefreshView, True)
       
   469         event.Skip()
       
   470 
       
   471     def OnCanvasResize(self, event):
       
   472         self.ResetLastCursor()
       
   473         wx.CallAfter(self.RefreshCursor)
       
   474         event.Skip()
       
   475 
       
   476     ## Reset the last cursor
       
   477     def ResetLastCursor(self):
       
   478         self.LastCursor = None
       
   479 
       
   480     ## Draw the cursor on graphic
       
   481     #  @param dc The draw canvas
       
   482     #  @param cursor The cursor parameters
       
   483     def DrawCursor(self, dc, cursor, value):
       
   484         if self.StartTick <= cursor <= self.StartTick + self.CurrentRange:
       
   485             # Prepare temporary dc for drawing
       
   486             width = self.Canvas._Buffer.GetWidth()
       
   487             height = self.Canvas._Buffer.GetHeight()
       
   488             tmp_Buffer = wx.EmptyBitmap(width, height)
       
   489             dcs = wx.MemoryDC()
       
   490             dcs.SelectObject(tmp_Buffer)
       
   491             dcs.Clear()
       
   492             dcs.BeginDrawing()
       
   493             
       
   494             dcs.SetPen(wx.Pen(wx.RED))
       
   495             dcs.SetBrush(wx.Brush(wx.RED, wx.SOLID))
       
   496             dcs.SetFont(self.Canvas._getFont(self.Canvas._fontSizeAxis))
       
   497             
       
   498             # Calculate clipping region
       
   499             graphics, xAxis, yAxis = self.Canvas.last_draw
       
   500             p1 = numpy.array([xAxis[0], yAxis[0]])
       
   501             p2 = numpy.array([xAxis[1], yAxis[1]])
       
   502             cx, cy, cwidth, cheight = self.Canvas._point2ClientCoord(p1, p2)
       
   503             
       
   504             px, py = self.Canvas.PositionUserToScreen((float(cursor), 0.))
       
   505             
       
   506             # Draw line cross drawing for diaplaying time cursor
       
   507             dcs.DrawLine(px, cy + 1, px, cy + cheight - 1)
       
   508             
       
   509             lines = ("X:%d\nY:%f" % (cursor, value)).splitlines()
       
   510             
       
   511             wtext = 0
       
   512             for line in lines:
       
   513                 w, h = dcs.GetTextExtent(line)
       
   514                 wtext = max(wtext, w)
       
   515             
       
   516             offset = 0
       
   517             for line in lines:
       
   518                 # Draw time cursor date
       
   519                 dcs.DrawText(line, min(px + 3, cx + cwidth - wtext), cy + 3 + offset)
       
   520                 w, h = dcs.GetTextExtent(line)
       
   521                 offset += h
       
   522             
       
   523             dcs.EndDrawing()
       
   524     
       
   525             #this will erase if called twice
       
   526             dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV)  #(NOT src) XOR dst
       
   527     
       
   528     ## Refresh the variable cursor.
       
   529     #  @param dc The draw canvas
       
   530     def RefreshCursor(self, dc=None):
       
   531         if self:
       
   532             if dc is None:
       
   533                 dc = wx.BufferedDC(wx.ClientDC(self.Canvas.canvas), self.Canvas._Buffer)
       
   534             
       
   535             # Erase previous time cursor if drawn
       
   536             if self.LastCursor is not None:
       
   537                 self.DrawCursor(dc, *self.LastCursor)
       
   538             
       
   539             # Draw new time cursor
       
   540             if self.CursorIdx is not None:
       
   541                 self.LastCursor = self.Datas[self.CursorIdx]
       
   542                 self.DrawCursor(dc, *self.LastCursor)