lbessard@301: #!/usr/bin/env python lbessard@301: # -*- coding: utf-8 -*- lbessard@301: lbessard@301: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor lbessard@301: #based on the plcopen standard. lbessard@301: # lbessard@301: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD lbessard@301: # lbessard@301: #See COPYING file for copyrights details. lbessard@301: # lbessard@301: #This library is free software; you can redistribute it and/or lbessard@301: #modify it under the terms of the GNU General Public lbessard@301: #License as published by the Free Software Foundation; either lbessard@301: #version 2.1 of the License, or (at your option) any later version. lbessard@301: # lbessard@301: #This library is distributed in the hope that it will be useful, lbessard@301: #but WITHOUT ANY WARRANTY; without even the implied warranty of lbessard@301: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lbessard@301: #General Public License for more details. lbessard@301: # lbessard@301: #You should have received a copy of the GNU General Public lbessard@301: #License along with this library; if not, write to the Free Software lbessard@301: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lbessard@301: lbessard@301: import wx lbessard@301: import wx.lib.plot as plot laurent@642: import numpy laurent@660: import math laurent@660: from graphics.GraphicCommons import DebugViewer, MODE_SELECTION, MODE_MOTION laurent@586: from controls import EditorPanel lbessard@301: lbessard@301: colours = ['blue', 'red', 'green', 'yellow', 'orange', 'purple', 'brown', 'cyan', lbessard@301: 'pink', 'grey'] lbessard@301: markers = ['circle', 'dot', 'square', 'triangle', 'triangle_down', 'cross', 'plus', 'circle'] lbessard@301: lbessard@301: lbessard@301: #------------------------------------------------------------------------------- lbessard@301: # Debug Variable Graphic Viewer class lbessard@301: #------------------------------------------------------------------------------- lbessard@301: laurent@632: SECOND = 1000000000 laurent@632: MINUTE = 60 * SECOND laurent@632: HOUR = 60 * MINUTE laurent@632: laurent@660: ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)]) laurent@660: RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)]) laurent@640: TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \ laurent@632: [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \ laurent@632: [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)] lbessard@301: laurent@660: [ID_GRAPHICVIEWER, ID_GRAPHICVIEWERCANVAS, laurent@660: ID_GRAPHICVIEWERCANVASRANGE, ID_GRAPHICVIEWERCANVASZOOM, laurent@660: ID_GRAPHICVIEWERCANVASPOSITION, ID_GRAPHICVIEWERRESETBUTTON, laurent@660: ID_GRAPHICVIEWERCURRENTBUTTON, ID_GRAPHICVIEWERSTATICTEXT1, laurent@660: ID_GRAPHICVIEWERSTATICTEXT2, ID_GRAPHICVIEWERSTATICTEXT3, laurent@660: ] = [wx.NewId() for _init_ctrls in range(10)] lbessard@301: laurent@586: class GraphicViewer(EditorPanel, DebugViewer): lbessard@301: lbessard@301: def _init_coll_MainGridSizer_Items(self, parent): lbessard@301: # generated method, don't edit lbessard@301: parent.AddWindow(self.Canvas, 0, border=0, flag=wx.GROW) lbessard@301: parent.AddSizer(self.RangeSizer, 0, border=0, flag=wx.GROW) lbessard@301: lbessard@301: def _init_coll_MainGridSizer_Growables(self, parent): lbessard@301: # generated method, don't edit lbessard@301: parent.AddGrowableCol(0) lbessard@301: parent.AddGrowableRow(0) lbessard@301: lbessard@301: def _init_coll_RangeSizer_Items(self, parent): lbessard@301: # generated method, don't edit laurent@391: parent.AddWindow(self.staticbox1, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) lbessard@301: parent.AddWindow(self.CanvasRange, 0, border=5, flag=wx.ALL) laurent@660: parent.AddWindow(self.staticbox3, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) laurent@660: parent.AddWindow(self.CanvasZoom, 0, border=5, flag=wx.ALL) laurent@391: parent.AddWindow(self.staticText2, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) lbessard@301: parent.AddWindow(self.CanvasPosition, 0, border=5, flag=wx.GROW|wx.ALL) lbessard@301: parent.AddWindow(self.ResetButton, 0, border=5, flag=wx.ALL) lbessard@301: parent.AddWindow(self.CurrentButton, 0, border=5, flag=wx.ALL) lbessard@301: lbessard@301: def _init_coll_RangeSizer_Growables(self, parent): lbessard@301: # generated method, don't edit lbessard@301: parent.AddGrowableCol(3) lbessard@301: parent.AddGrowableRow(0) lbessard@301: lbessard@301: def _init_sizers(self): lbessard@301: # generated method, don't edit lbessard@301: self.MainGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) laurent@660: self.RangeSizer = wx.FlexGridSizer(cols=8, hgap=0, rows=1, vgap=0) lbessard@301: lbessard@301: self._init_coll_MainGridSizer_Items(self.MainGridSizer) lbessard@301: self._init_coll_MainGridSizer_Growables(self.MainGridSizer) lbessard@301: self._init_coll_RangeSizer_Items(self.RangeSizer) lbessard@301: self._init_coll_RangeSizer_Growables(self.RangeSizer) lbessard@301: laurent@586: self.Editor.SetSizer(self.MainGridSizer) laurent@586: laurent@586: def _init_Editor(self, prnt): laurent@586: self.Editor = wx.Panel(prnt, ID_GRAPHICVIEWER, wx.DefaultPosition, lbessard@301: wx.DefaultSize, 0) lbessard@301: lbessard@301: self.Canvas = plot.PlotCanvas(id=ID_GRAPHICVIEWERCANVAS, laurent@586: name='Canvas', parent=self.Editor, pos=wx.Point(0, 0), lbessard@301: size=wx.Size(0, 0), style=0) lbessard@301: def _axisInterval(spec, lower, upper): lbessard@301: if spec == 'border': lbessard@301: if lower == upper: lbessard@301: return lower - 0.5, upper + 0.5 lbessard@301: else: lbessard@301: border = (upper - lower) * 0.05 lbessard@301: return lower - border, upper + border lbessard@301: else: lbessard@301: return plot.PlotCanvas._axisInterval(self.Canvas, spec, lower, upper) lbessard@301: self.Canvas._axisInterval = _axisInterval lbessard@301: self.Canvas.SetYSpec('border') laurent@660: self.Canvas.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasLeftDown) laurent@660: self.Canvas.canvas.Bind(wx.EVT_LEFT_UP, self.OnCanvasLeftUp) laurent@660: self.Canvas.canvas.Bind(wx.EVT_MOTION, self.OnCanvasMotion) laurent@660: self.Canvas.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnCanvasMouseWheel) laurent@660: lbessard@301: self.staticbox1 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT1, laurent@586: label=_('Range:'), name='staticText1', parent=self.Editor, laurent@391: pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) lbessard@301: lbessard@301: self.CanvasRange = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASRANGE, laurent@586: name='CanvasRange', parent=self.Editor, pos=wx.Point(0, 0), laurent@640: size=wx.Size(100, 28), style=wx.CB_READONLY) lbessard@301: self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, id=ID_GRAPHICVIEWERCANVASRANGE) lbessard@301: laurent@660: self.staticbox3 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT3, laurent@660: label=_('Zoom:'), name='staticText3', parent=self.Editor, laurent@660: pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) laurent@660: laurent@660: self.CanvasZoom = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASZOOM, laurent@660: name='CanvasZoom', parent=self.Editor, pos=wx.Point(0, 0), laurent@660: size=wx.Size(70, 28), style=wx.CB_READONLY) laurent@660: self.Bind(wx.EVT_COMBOBOX, self.OnZoomChanged, id=ID_GRAPHICVIEWERCANVASZOOM) laurent@660: lbessard@301: self.staticText2 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT2, laurent@586: label=_('Position:'), name='staticText2', parent=self.Editor, laurent@391: pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) lbessard@301: lbessard@301: self.CanvasPosition = wx.ScrollBar(id=ID_GRAPHICVIEWERCANVASPOSITION, laurent@586: name='Position', parent=self.Editor, pos=wx.Point(0, 0), lbessard@301: size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) lbessard@301: self.CanvasPosition.SetScrollbar(0, 10, 100, 10) lbessard@301: self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnPositionChanging, lbessard@301: id = ID_GRAPHICVIEWERCANVASPOSITION) lbessard@301: self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, self.OnPositionChanging, lbessard@301: id = ID_GRAPHICVIEWERCANVASPOSITION) lbessard@301: self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnPositionChanging, lbessard@301: id = ID_GRAPHICVIEWERCANVASPOSITION) lbessard@301: self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, self.OnPositionChanging, lbessard@301: id = ID_GRAPHICVIEWERCANVASPOSITION) lbessard@301: self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnPositionChanging, lbessard@301: id = ID_GRAPHICVIEWERCANVASPOSITION) lbessard@301: lbessard@301: self.ResetButton = wx.Button(id=ID_GRAPHICVIEWERRESETBUTTON, label='Reset', laurent@586: name='ResetButton', parent=self.Editor, pos=wx.Point(0, 0), lbessard@301: size=wx.Size(72, 24), style=0) lbessard@301: self.Bind(wx.EVT_BUTTON, self.OnResetButton, id=ID_GRAPHICVIEWERRESETBUTTON) lbessard@301: lbessard@301: self.CurrentButton = wx.Button(id=ID_GRAPHICVIEWERCURRENTBUTTON, label='Current', laurent@586: name='CurrentButton', parent=self.Editor, pos=wx.Point(0, 0), lbessard@301: size=wx.Size(72, 24), style=0) lbessard@301: self.Bind(wx.EVT_BUTTON, self.OnCurrentButton, id=ID_GRAPHICVIEWERCURRENTBUTTON) lbessard@301: lbessard@301: self._init_sizers() lbessard@301: b@415: def __init__(self, parent, window, producer, instancepath = ""): laurent@586: EditorPanel.__init__(self, parent, "", window, None) b@415: DebugViewer.__init__(self, producer, True, False) greg@361: lbessard@301: self.InstancePath = instancepath laurent@632: self.RangeValues = None laurent@660: self.CursorIdx = None laurent@660: self.LastCursor = None laurent@660: self.CurrentMousePos = None laurent@660: self.CurrentMotionValue = None laurent@660: self.Dragging = False laurent@660: laurent@660: # Initialize Viewer mode to Selection mode laurent@660: self.Mode = MODE_SELECTION laurent@632: lbessard@301: self.Datas = [] laurent@660: self.StartTick = 0 laurent@660: self.EndTick = 0 laurent@660: self.StartIdx = 0 laurent@660: self.EndIdx = 0 laurent@660: self.MinValue = None laurent@660: self.MaxValue = None laurent@660: self.YCenter = 0 laurent@660: self.CurrentZoom = 1 laurent@642: self.Fixed = False laurent@632: self.Ticktime = self.DataProducer.GetTicktime() laurent@632: self.RefreshCanvasRange() lbessard@301: laurent@660: for zoom_txt, zoom in ZOOM_VALUES: laurent@660: self.CanvasZoom.Append(zoom_txt) laurent@660: self.CanvasZoom.SetSelection(0) laurent@660: greg@361: self.AddDataConsumer(self.InstancePath.upper(), self) lbessard@301: lbessard@338: def __del__(self): greg@361: DebugViewer.__del__(self) greg@361: self.RemoveDataConsumer(self) lbessard@338: laurent@586: def GetTitle(self): laurent@586: if len(self.InstancePath) > 15: laurent@586: return "..." + self.InstancePath[-12:] laurent@586: return self.InstancePath laurent@586: laurent@660: # Changes Viewer mode laurent@660: def SetMode(self, mode): laurent@660: if self.Mode != mode or mode == MODE_SELECTION: laurent@660: if self.Mode == MODE_MOTION: laurent@660: wx.CallAfter(self.Canvas.canvas.SetCursor, wx.NullCursor) laurent@660: self.Mode = mode laurent@660: if self.Mode == MODE_MOTION: laurent@660: wx.CallAfter(self.Canvas.canvas.SetCursor, wx.StockCursor(wx.CURSOR_HAND)) laurent@660: laurent@660: def ResetView(self, register=False): lbessard@338: self.Datas = [] laurent@660: self.StartTick = 0 laurent@660: self.EndTick = 0 laurent@660: self.StartIdx = 0 laurent@660: self.EndIdx = 0 laurent@642: self.Fixed = False laurent@632: self.Ticktime = self.DataProducer.GetTicktime() laurent@660: if register: laurent@660: self.AddDataConsumer(self.InstancePath.upper(), self) laurent@632: self.RefreshCanvasRange() lbessard@338: self.RefreshView() lbessard@338: laurent@642: def RefreshNewData(self, *args, **kwargs): laurent@642: self.RefreshView(*args, **kwargs) greg@374: DebugViewer.RefreshNewData(self) greg@361: laurent@642: def GetNearestData(self, tick, adjust): laurent@642: ticks = numpy.array(zip(*self.Datas)[0]) laurent@642: new_cursor = numpy.argmin(abs(ticks - tick)) laurent@642: if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0: laurent@642: new_cursor -= 1 laurent@642: elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(self.Datas): laurent@642: new_cursor += 1 laurent@642: return new_cursor laurent@642: laurent@660: def GetBounds(self): laurent@660: if self.StartIdx is None or self.EndIdx is None: laurent@660: self.StartIdx = self.GetNearestData(self.StartTick, -1) laurent@660: self.EndIdx = self.GetNearestData(self.EndTick, 1) laurent@660: laurent@660: def ResetBounds(self): laurent@660: self.StartIdx = None laurent@660: self.EndIdx = None laurent@660: laurent@660: def RefreshCanvasRange(self): laurent@660: if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES: laurent@660: self.RangeValues = RANGE_VALUES laurent@660: self.CanvasRange.Clear() laurent@660: for text, value in RANGE_VALUES: laurent@660: self.CanvasRange.Append(text) laurent@660: self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0]) laurent@660: self.CurrentRange = RANGE_VALUES[0][1] laurent@660: elif self.RangeValues != TIME_RANGE_VALUES: laurent@660: self.RangeValues = TIME_RANGE_VALUES laurent@660: self.CanvasRange.Clear() laurent@660: for text, value in TIME_RANGE_VALUES: laurent@660: self.CanvasRange.Append(text) laurent@660: self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0]) laurent@660: self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime laurent@660: laurent@660: def RefreshView(self, force=True): laurent@660: self.Freeze() laurent@660: if force or not self.Fixed: laurent@660: if (self.MinValue is not None and laurent@660: self.MaxValue is not None and laurent@660: self.MinValue != self.MaxValue): laurent@660: Yrange = float(self.MaxValue - self.MinValue) / self.CurrentZoom laurent@660: else: laurent@660: Yrange = 2. / self.CurrentZoom laurent@660: laurent@660: if not self.Fixed and len(self.Datas) > 0: laurent@660: self.YCenter = max(self.Datas[-1][1] - Yrange / 2, laurent@660: min(self.YCenter, laurent@660: self.Datas[-1][1] + Yrange / 2)) laurent@660: laurent@660: var_name = self.InstancePath.split(".")[-1] laurent@660: laurent@660: self.GetBounds() laurent@660: self.VariableGraphic = plot.PolyLine(self.Datas[self.StartIdx:self.EndIdx + 1], laurent@660: legend=var_name, colour=colours[0]) laurent@660: self.GraphicsObject = plot.PlotGraphics([self.VariableGraphic], _("%s Graphics") % var_name, _("Tick"), _("Values")) laurent@660: datas_length = len(self.Datas) laurent@660: if datas_length > 1: laurent@660: start = self.Datas[self.StartIdx][0] laurent@660: else: laurent@660: start = 0. laurent@660: self.Canvas.Draw(self.GraphicsObject, laurent@660: xAxis=(start, start + self.CurrentRange), laurent@660: yAxis=(self.YCenter - Yrange * 1.1 / 2, self.YCenter + Yrange * 1.1 / 2)) laurent@660: self.RefreshScrollBar() laurent@660: laurent@660: # Reset and draw cursor laurent@660: self.ResetLastCursor() laurent@660: self.RefreshCursor() laurent@660: laurent@660: self.Thaw() laurent@660: laurent@660: def GetInstancePath(self): laurent@660: return self.InstancePath laurent@660: laurent@660: def IsViewing(self, tagname): laurent@660: return self.InstancePath == tagname laurent@660: edouard@504: def NewValue(self, tick, value, forced=False): greg@361: self.Datas.append((float(tick), {True:1., False:0.}.get(value, float(value)))) laurent@660: if self.MinValue is None: laurent@660: self.MinValue = value laurent@660: else: laurent@660: self.MinValue = min(self.MinValue, value) laurent@660: if self.MaxValue is None: laurent@660: self.MaxValue = value laurent@660: else: laurent@660: self.MaxValue = max(self.MaxValue, value) laurent@642: if not self.Fixed: laurent@660: self.GetBounds() laurent@660: while int(self.Datas[self.StartIdx][0]) < tick - self.CurrentRange: laurent@660: self.StartIdx += 1 laurent@660: self.EndIdx += 1 laurent@660: self.StartTick = self.Datas[self.StartIdx][0] laurent@660: self.EndTick = self.StartTick + self.CurrentRange greg@361: self.NewDataAvailable() greg@361: lbessard@301: def RefreshScrollBar(self): laurent@642: if len(self.Datas) > 0: laurent@660: self.GetBounds() laurent@660: pos = int(self.Datas[self.StartIdx][0] - self.Datas[0][0]) laurent@642: range = int(self.Datas[-1][0] - self.Datas[0][0]) laurent@642: else: laurent@642: pos = 0 laurent@642: range = 0 laurent@642: self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) lbessard@301: laurent@660: def RefreshRange(self): laurent@648: if len(self.Datas) > 0: laurent@648: if self.Fixed and self.Datas[-1][0] - self.Datas[0][0] < self.CurrentRange: laurent@648: self.Fixed = False laurent@660: self.ResetBounds() laurent@648: if self.Fixed: laurent@660: self.StartTick = min(self.StartTick, self.Datas[-1][0] - self.CurrentRange) laurent@648: else: laurent@660: self.StartTick = max(self.Datas[0][0], self.EndTick - self.CurrentRange - 1) laurent@660: self.EndTick = self.StartTick + self.CurrentRange laurent@642: self.NewDataAvailable(True) laurent@660: laurent@660: def OnRangeChanged(self, event): laurent@660: try: laurent@660: if self.Ticktime == 0: laurent@660: self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] laurent@660: else: laurent@660: self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime laurent@660: except ValueError, e: laurent@660: self.CanvasRange.SetValue(str(self.CurrentRange)) laurent@660: wx.CallAfter(self.RefreshRange) laurent@660: event.Skip() laurent@660: laurent@660: def OnZoomChanged(self, event): laurent@660: self.CurrentZoom = ZOOM_VALUES[self.CanvasZoom.GetSelection()][1] laurent@660: wx.CallAfter(self.NewDataAvailable, True) lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnPositionChanging(self, event): laurent@660: self.ResetBounds() laurent@660: self.StartTick = self.Datas[0][0] + event.GetPosition() laurent@660: self.EndTick = self.StartTick + self.CurrentRange laurent@642: self.Fixed = True laurent@642: self.NewDataAvailable(True) lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnResetButton(self, event): laurent@642: self.Fixed = False laurent@660: self.ResetView() lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnCurrentButton(self, event): laurent@660: self.ResetBounds() laurent@660: self.StartTick = max(self.Datas[0][0], self.Datas[-1][0] - self.CurrentRange) laurent@660: self.EndTick = self.StartTick + self.CurrentRange laurent@642: self.Fixed = False laurent@642: self.NewDataAvailable(True) laurent@642: event.Skip() laurent@642: laurent@660: def OnCanvasLeftDown(self, event): laurent@660: self.Fixed = True laurent@660: self.Canvas.canvas.CaptureMouse() laurent@660: if self.Mode == MODE_SELECTION: laurent@660: self.Dragging = True laurent@660: pos = self.Canvas.PositionScreenToUser(event.GetPosition()) laurent@660: self.CursorIdx = self.GetNearestData(pos[0], -1) laurent@660: self.RefreshCursor() laurent@660: elif self.Mode == MODE_MOTION: laurent@660: self.GetBounds() laurent@660: self.CurrentMousePos = event.GetPosition() laurent@660: self.CurrentMotionValue = self.Datas[self.StartIdx][0] laurent@660: event.Skip() laurent@660: laurent@660: def OnCanvasLeftUp(self, event): laurent@660: self.Dragging = False laurent@660: if self.Mode == MODE_MOTION: laurent@660: self.CurrentMousePos = None laurent@660: self.CurrentMotionValue = None laurent@660: if self.Canvas.canvas.HasCapture(): laurent@660: self.Canvas.canvas.ReleaseMouse() laurent@660: event.Skip() laurent@660: laurent@660: def OnCanvasMotion(self, event): laurent@660: if self.Mode == MODE_SELECTION and self.Dragging: laurent@660: pos = self.Canvas.PositionScreenToUser(event.GetPosition()) laurent@660: graphics, xAxis, yAxis = self.Canvas.last_draw laurent@660: self.CursorIdx = self.GetNearestData(max(xAxis[0], min(pos[0], xAxis[1])), -1) laurent@660: self.RefreshCursor() laurent@660: elif self.CurrentMousePos is not None: laurent@660: oldpos = self.Canvas.PositionScreenToUser(self.CurrentMousePos) laurent@660: newpos = self.Canvas.PositionScreenToUser(event.GetPosition()) laurent@660: self.CurrentMotionValue += oldpos[0] - newpos[0] laurent@660: self.YCenter += oldpos[1] - newpos[1] laurent@660: self.ResetBounds() laurent@660: self.StartTick = self.CurrentMotionValue laurent@660: self.EndTick = self.StartTick + self.CurrentRange laurent@660: self.CurrentMousePos = event.GetPosition() laurent@660: self.NewDataAvailable(True) laurent@660: event.Skip() laurent@660: laurent@660: def OnCanvasMouseWheel(self, event): laurent@660: if self.CurrentMousePos is None: laurent@660: rotation = event.GetWheelRotation() / event.GetWheelDelta() laurent@660: if event.ShiftDown(): laurent@660: current = self.CanvasRange.GetSelection() laurent@660: new = max(0, min(current - rotation, len(self.RangeValues) - 1)) laurent@660: if new != current: laurent@660: if self.Ticktime == 0: laurent@660: self.CurrentRange = self.RangeValues[new][1] laurent@660: else: laurent@660: self.CurrentRange = self.RangeValues[new][1] / self.Ticktime laurent@660: self.CanvasRange.SetStringSelection(self.RangeValues[new][0]) laurent@660: wx.CallAfter(self.RefreshRange) laurent@660: else: laurent@660: current = self.CanvasZoom.GetSelection() laurent@660: new = max(0, min(current - rotation, len(ZOOM_VALUES) - 1)) laurent@660: if new != current: laurent@660: self.CurrentZoom = ZOOM_VALUES[new][1] laurent@660: self.CanvasZoom.SetStringSelection(ZOOM_VALUES[new][0]) laurent@660: wx.CallAfter(self.NewDataAvailable, True) laurent@660: event.Skip() laurent@660: laurent@660: ## Reset the last cursor laurent@660: def ResetLastCursor(self): laurent@660: self.LastCursor = None laurent@660: laurent@660: ## Draw the cursor on graphic laurent@660: # @param dc The draw canvas laurent@660: # @param cursor The cursor parameters laurent@660: def DrawCursor(self, dc, cursor, value): laurent@660: if self.StartTick <= cursor <= self.EndTick: laurent@660: # Prepare temporary dc for drawing laurent@660: width = self.Canvas._Buffer.GetWidth() laurent@660: height = self.Canvas._Buffer.GetHeight() laurent@660: tmp_Buffer = wx.EmptyBitmap(width, height) laurent@660: dcs = wx.MemoryDC() laurent@660: dcs.SelectObject(tmp_Buffer) laurent@660: dcs.Clear() laurent@660: dcs.BeginDrawing() laurent@660: laurent@660: dcs.SetPen(wx.Pen(wx.RED)) laurent@660: dcs.SetBrush(wx.Brush(wx.RED, wx.SOLID)) laurent@660: dcs.SetFont(self.Canvas._getFont(self.Canvas._fontSizeAxis)) laurent@660: laurent@660: # Calculate clipping region laurent@660: graphics, xAxis, yAxis = self.Canvas.last_draw laurent@660: p1 = numpy.array([xAxis[0], yAxis[0]]) laurent@660: p2 = numpy.array([xAxis[1], yAxis[1]]) laurent@660: cx, cy, cwidth, cheight = self.Canvas._point2ClientCoord(p1, p2) laurent@660: laurent@660: px, py = self.Canvas.PositionUserToScreen((float(cursor), 0.)) laurent@660: laurent@660: # Draw line cross drawing for diaplaying time cursor laurent@660: dcs.DrawLine(px, cy + 1, px, cy + cheight - 1) laurent@660: laurent@660: text = "X:%d\nY:%f"%(cursor, value) laurent@660: w, h = dcs.GetTextExtent(text) laurent@660: # Draw time cursor date laurent@660: dcs.DrawText(text, min(px + 3, cx + cwidth - w), cy + 3) laurent@660: laurent@660: dcs.EndDrawing() laurent@660: laurent@660: #this will erase if called twice laurent@660: dc.Blit(0, 0, width, height, dcs, 0, 0, wx.EQUIV) #(NOT src) XOR dst laurent@660: laurent@660: ## Refresh the variable cursor. laurent@660: # @param dc The draw canvas laurent@660: def RefreshCursor(self, dc=None): laurent@660: if dc is None: laurent@660: dc = wx.BufferedDC(wx.ClientDC(self.Canvas.canvas), self.Canvas._Buffer) laurent@660: laurent@660: # Erase previous time cursor if drawn laurent@660: if self.LastCursor is not None: laurent@660: self.DrawCursor(dc, *self.LastCursor) laurent@660: laurent@660: # Draw new time cursor laurent@660: if self.CursorIdx is not None: laurent@660: self.LastCursor = self.Datas[self.CursorIdx] laurent@660: self.DrawCursor(dc, *self.LastCursor)