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 greg@361: from graphics.GraphicCommons import DebugViewer lbessard@301: 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: lbessard@301: lbessard@301: RANGE_VALUES = [str(25 * 2 ** i) for i in xrange(6)] lbessard@301: lbessard@301: [ID_GRAPHICVIEWER, ID_GRAPHICVIEWERCANVAS, lbessard@301: ID_GRAPHICVIEWERCANVASRANGE, ID_GRAPHICVIEWERCANVASPOSITION, lbessard@301: ID_GRAPHICVIEWERRESETBUTTON, ID_GRAPHICVIEWERCURRENTBUTTON, lbessard@301: ID_GRAPHICVIEWERSTATICTEXT1, ID_GRAPHICVIEWERSTATICTEXT2, lbessard@301: ] = [wx.NewId() for _init_ctrls in range(8)] lbessard@301: greg@361: class GraphicViewer(wx.Panel, 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@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) lbessard@301: self.RangeSizer = wx.FlexGridSizer(cols=6, 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: lbessard@301: self.SetSizer(self.MainGridSizer) lbessard@301: lbessard@301: def _init_ctrls(self, prnt): lbessard@301: wx.Panel.__init__(self, prnt, ID_GRAPHICVIEWER, wx.DefaultPosition, lbessard@301: wx.DefaultSize, 0) lbessard@301: lbessard@301: self.Canvas = plot.PlotCanvas(id=ID_GRAPHICVIEWERCANVAS, lbessard@301: name='Canvas', parent=self, 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') lbessard@301: lbessard@301: self.staticbox1 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT1, laurent@391: label=_('Range:'), name='staticText1', parent=self, laurent@391: pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) lbessard@301: lbessard@301: self.CanvasRange = wx.ComboBox(id=ID_GRAPHICVIEWERCANVASRANGE, lbessard@301: name='CanvasRange', parent=self, pos=wx.Point(0, 0), lbessard@313: size=wx.Size(100, 28), choices=RANGE_VALUES, style=0) lbessard@301: self.CanvasRange.SetStringSelection("25") lbessard@301: self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, id=ID_GRAPHICVIEWERCANVASRANGE) lbessard@301: self.Bind(wx.EVT_TEXT_ENTER, self.OnRangeChanged, id=ID_GRAPHICVIEWERCANVASRANGE) lbessard@301: lbessard@301: self.staticText2 = wx.StaticText(id=ID_GRAPHICVIEWERSTATICTEXT2, laurent@391: label=_('Position:'), name='staticText2', parent=self, laurent@391: pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) lbessard@301: lbessard@301: self.CanvasPosition = wx.ScrollBar(id=ID_GRAPHICVIEWERCANVASPOSITION, lbessard@301: name='Position', parent=self, 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', lbessard@301: name='ResetButton', parent=self, 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', lbessard@301: name='CurrentButton', parent=self, 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 = ""): lbessard@301: self._init_ctrls(parent) b@415: DebugViewer.__init__(self, producer, True, False) greg@361: lbessard@301: self.ParentWindow = window lbessard@301: self.InstancePath = instancepath lbessard@301: lbessard@301: self.Datas = [] lbessard@301: self.CurrentValue = 0 lbessard@301: self.CurrentRange = 25 lbessard@301: 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: lbessard@338: def ResetView(self): lbessard@338: self.Datas = [] lbessard@338: self.CurrentValue = 0 lbessard@338: self.RefreshView() lbessard@338: greg@361: def RefreshNewData(self): greg@361: self.RefreshView(False) greg@374: DebugViewer.RefreshNewData(self) greg@361: greg@361: def RefreshView(self, force=True): greg@361: self.Freeze() greg@361: if force or self.CurrentValue + self.CurrentRange == len(self.Datas) or self.CurrentValue + len(self.Datas) < self.CurrentRange: greg@361: var_name = self.InstancePath.split(".")[-1] greg@361: greg@361: self.VariableGraphic = plot.PolyLine(self.Datas[self.CurrentValue:self.CurrentValue + self.CurrentRange], greg@361: legend=var_name, colour=colours[0]) laurent@391: self.GraphicsObject = plot.PlotGraphics([self.VariableGraphic], _("%s Graphics") % var_name, _("Tick"), _("Values")) greg@361: datas_length = len(self.Datas) greg@361: if datas_length > 1: greg@361: start = self.Datas[self.CurrentValue][0] greg@361: if self.CurrentValue + self.CurrentRange > datas_length: greg@361: end = start + (self.Datas[datas_length - 1][0] - start) * self.CurrentRange / (datas_length - self.CurrentValue - 1) greg@361: else: greg@361: end = self.Datas[self.CurrentValue + self.CurrentRange - 1][0] lbessard@301: else: greg@361: start = 0. greg@361: end = 25. greg@361: self.Canvas.Draw(self.GraphicsObject, xAxis=(start, end)) lbessard@301: self.RefreshScrollBar() greg@361: self.Thaw() lbessard@326: lbessard@326: def SetMode(self, mode): lbessard@326: pass lbessard@326: lbessard@301: def GetTagName(self): lbessard@301: return "" lbessard@301: lbessard@301: def GetInstancePath(self): lbessard@301: return self.InstancePath lbessard@301: lbessard@326: def IsViewing(self, tagname): lbessard@326: return self.InstancePath == tagname lbessard@326: lbessard@326: def ResetBuffer(self): lbessard@326: pass lbessard@326: lbessard@326: def RefreshScaling(self, refresh=True): lbessard@326: pass lbessard@326: lbessard@331: def SelectAll(self): lbessard@331: pass lbessard@331: laurent@411: def ClearErrors(self): laurent@411: pass laurent@411: edouard@504: def NewValue(self, tick, value, forced=False): greg@361: self.Datas.append((float(tick), {True:1., False:0.}.get(value, float(value)))) greg@361: if self.CurrentValue + self.CurrentRange == len(self.Datas) - 1: greg@361: self.CurrentValue += 1 greg@361: self.NewDataAvailable() greg@361: lbessard@301: def RefreshScrollBar(self): lbessard@301: self.CanvasPosition.SetScrollbar(self.CurrentValue, self.CurrentRange, len(self.Datas), self.CurrentRange) lbessard@301: lbessard@301: def OnRangeChanged(self, event): lbessard@301: old_range = self.CurrentRange lbessard@301: try: lbessard@301: self.CurrentRange = int(self.CanvasRange.GetValue()) lbessard@301: except ValueError, e: lbessard@301: self.CanvasRange.SetValue(str(self.CurrentRange)) lbessard@301: self.CurrentValue = max(0, min(self.CurrentValue + old_range - self.CurrentRange, lbessard@301: len(self.Datas) - self.CurrentRange)) lbessard@301: self.RefreshView() lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnPositionChanging(self, event): lbessard@301: self.CurrentValue = event.GetPosition() lbessard@301: self.RefreshView() lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnResetButton(self, event): lbessard@338: self.ResetView() lbessard@301: event.Skip() lbessard@301: lbessard@301: def OnCurrentButton(self, event): lbessard@301: self.CurrentValue = max(0, len(self.Datas) - self.CurrentRange) lbessard@301: self.RefreshView() lbessard@301: event.Skip() lbessard@301: