--- a/controls/DebugVariablePanel.py Wed Dec 19 13:32:51 2012 +0100
+++ b/controls/DebugVariablePanel.py Tue Jan 15 10:57:06 2013 +0100
@@ -24,6 +24,7 @@
from types import TupleType, FloatType
from time import time as gettime
@@ -33,7 +34,8 @@
- from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
+ from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas + #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas from mpl_toolkits.mplot3d import Axes3D
@@ -44,6 +46,16 @@
from dialogs.ForceVariableDialog import ForceVariableDialog
from util.BitmapLibrary import GetBitmap
+ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)]) +RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)]) +TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \ + [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \ + [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)] def AppendMenu(parent, help, id, kind, text):
parent.Append(help=help, id=id, kind=kind, text=text)
@@ -83,12 +95,33 @@
def GetVariableType(self):
+ def GetData(self, start_tick=None, end_tick=None): + if self.IsNumVariable(): + if len(self.Data) == 0: + start_idx = end_idx = None + if start_tick is not None: + start_idx = self.GetNearestData(start_tick, -1) + if end_tick is not None: + end_idx = self.GetNearestData(end_tick, 1) + if end_idx is not None: + return self.Data[start_idx:end_idx + 1] + return self.Data[start_idx:] + return self.MinValue, self.MaxValue self.Data = numpy.array([]).reshape(0, 2)
@@ -98,6 +131,14 @@
def NewValue(self, tick, value, forced=False):
num_value = {True:1., False:0.}.get(value, float(value))
+ if self.MinValue is None: + self.MinValue = num_value + self.MinValue = min(self.MinValue, num_value) + if self.MaxValue is None: + self.MaxValue = num_value + self.MaxValue = max(self.MaxValue, num_value) self.Data = numpy.append(self.Data, [[float(tick), num_value]], axis=0)
self.Parent.HasNewData = True
DebugDataConsumer.NewValue(self, tick, value, forced)
@@ -133,6 +174,17 @@
+ def GetNearestData(self, tick, adjust): + if self.IsNumVariable(): + ticks = self.Data[:, 0] + new_cursor = numpy.argmin(abs(ticks - tick)) + if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0: + elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(ticks): class DebugVariableTable(CustomTable):
def GetValue(self, row, col):
@@ -247,10 +299,23 @@
+def NextTick(variables): + for var_name, data in variables: + next_tick = min(next_tick, data[0][0]) class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
- def __init__(self, parent, producer):
+ def __init__(self, parent, producer, window): wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
+ self.ParentWindow = window DebugViewer.__init__(self, producer, True)
@@ -290,6 +355,11 @@
self.MainPanel.SetSizer(main_panel_sizer)
+ self.Ticks = numpy.array([]) + self.RangeValues = None self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
self.VariablesGrid.SetTable(self.Table)
@@ -337,6 +407,43 @@
graphics_panel_sizer = wx.BoxSizer(wx.VERTICAL)
+ graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) + graphics_panel_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) + range_label = wx.StaticText(self.GraphicsPanel, label=_('Range:')) + graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) + self.CanvasRange = wx.ComboBox(self.GraphicsPanel, + size=wx.Size(100, 28), style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) + graphics_button_sizer.AddWindow(self.CanvasRange, 1, flag=wx.ALIGN_CENTER_VERTICAL) + for name, bitmap, help in [ + ("ResetButton", "reset", _("Clear the graph values")), + ("CurrentButton", "current", _("Go to current value")), + ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: + button = wx.lib.buttons.GenBitmapButton(self.GraphicsPanel, + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) + button.SetToolTipString(help) + setattr(self, name, button) + self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) + graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) + self.CanvasPosition = wx.ScrollBar(self.GraphicsPanel, + size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) + self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, + self.OnPositionChanging, self.CanvasPosition) + graphics_panel_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) self.GraphicsCanvasWindow = wx.ScrolledWindow(self.GraphicsPanel, style=wx.HSCROLL|wx.VSCROLL)
self.GraphicsCanvasWindow.Bind(wx.EVT_SIZE, self.OnGraphicsCanvasWindowResize)
graphics_panel_sizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
@@ -374,12 +481,30 @@
self.Initialize(self.MainPanel)
+ self.RefreshCanvasRange() + self.RefreshScrollBar() + def SetDataProducer(self, producer): + DebugViewer.SetDataProducer(self, producer) + if self.DataProducer is not None: + self.Ticktime = self.DataProducer.GetTicktime() + self.RefreshCanvasRange() - def RefreshNewData(self):
+ def RefreshNewData(self, *args, **kwargs): + if self.HasNewData or self.Force: self.RefreshGrid(only_values=True)
- DebugViewer.RefreshNewData(self)
+ DebugViewer.RefreshNewData(self, *args, **kwargs) + def NewDataAvailable(self, tick, *args, **kwargs): + self.Ticks = numpy.append(self.Ticks, [tick]) + if not self.Fixed or tick < self.StartTick + self.CurrentRange: + self.StartTick = max(self.StartTick, tick - self.CurrentRange) + DebugViewer.NewDataAvailable(self, tick, *args, **kwargs) def RefreshGrid(self, only_values=False):
@@ -392,29 +517,49 @@
self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
+ self.RefreshScrollBar() + if USE_MPL and (not self.Fixed or self.Force): + start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange for item in self.Table.GetData():
+ data = item.GetData(start_tick, end_tick) + min_value, max_value = item.GetRange() + if min_value is not None and max_value is not None: + y_center = (min_value + max_value) / 2. + y_range = max(1.0, max_value - min_value) self.GraphicsAxes[idx].clear()
self.GraphicsAxes[idx].plot(data[:, 0], data[:, 1])
+ self.GraphicsAxes[idx].set_xlim(start_tick, end_tick) + self.GraphicsAxes[idx].set_ylim( + y_center - y_range * 0.55, y_center + y_range * 0.55) - self.GraphicsCanvas.draw()
+ self.GraphicsCanvas.draw() while len(self.Graphics3DAxes.lines) > 0:
self.Graphics3DAxes.lines.pop()
if self.Axis3DValues is not None:
- self.Graphics3DAxes.plot(
- self.Axis3DValues[0][1].GetData()[self.Axis3DValues[0][0]:, 1],
- self.Axis3DValues[1][1].GetData()[self.Axis3DValues[1][0]:, 1],
- zs = self.Axis3DValues[2][1].GetData()[self.Axis3DValues[2][0]:, 1])
- self.Graphics3DCanvas.draw()
+ axis = self.Axis3DValues[0] + start_tick = max(self.StartTick, self.Axis3DValues[1]) + end_tick = max(self.StartTick + self.CurrentRange, self.Axis3DValues[1]) + xyz_data = [axe.GetData(start_tick, end_tick)[:, 1] for axe in axis] + length = reduce(min, [len(data) for data in xyz_data]) + self.Graphics3DAxes.plot(xyz_data[0][:length], + zs = xyz_data[2][:length]) + self.Graphics3DCanvas.draw() def UnregisterObsoleteData(self):
items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
@@ -430,6 +575,9 @@
self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
+ if self.DataProducer is not None: + self.Ticktime = self.DataProducer.GetTicktime() + self.RefreshCanvasRange() self.DeleteDataConsumers()
@@ -440,6 +588,33 @@
+ def RefreshCanvasRange(self): + if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES: + self.RangeValues = RANGE_VALUES + self.CanvasRange.Clear() + for text, value in RANGE_VALUES: + self.CanvasRange.Append(text) + self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0]) + self.CurrentRange = RANGE_VALUES[0][1] + elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES: + self.RangeValues = TIME_RANGE_VALUES + self.CanvasRange.Clear() + for text, value in TIME_RANGE_VALUES: + self.CanvasRange.Append(text) + self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0]) + self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime + def RefreshScrollBar(self): + if len(self.Ticks) > 0: + pos = int(self.StartTick - self.Ticks[0]) + range = int(self.Ticks[-1] - self.Ticks[0]) + self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) def GetForceVariableMenuFunction(self, iec_path, item):
iec_type = self.GetDataType(iec_path)
def ForceVariableFunction(event):
@@ -494,6 +669,77 @@
wx.CallAfter(self.Reset3DGraphics)
+ def RefreshRange(self): + if len(self.Ticks) > 0: + if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: + self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange) + self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) + def OnRangeChanged(self, event): + self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] + self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime + self.CanvasRange.SetValue(str(self.CurrentRange)) + wx.CallAfter(self.RefreshRange) + def OnResetButton(self, event): + for item in self.Table.GetData(): + def OnCurrentButton(self, event): + if len(self.Ticks) > 0: + self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) + def CopyDataToClipboard(self, variables): + text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables]) + next_tick = NextTick(variables) + while next_tick is not None: + for var_name, data in variables: + if next_tick == data[0][0]: + values.append("%.3f" % data.pop(0)[1]) + text += "%d;%s;\n" % (next_tick, ";".join(values)) + next_tick = NextTick(variables) + self.ParentWindow.SetCopyBuffer(text) + def OnExportGraphButton(self, event): + for item in self.Table.GetData(): + if item.IsNumVariable(): + variables.append((item.GetVariable(), [entry for entry in item.GetData()])) + wx.CallAfter(self.CopyDataToClipboard, variables) + def OnPositionChanging(self, event): + if len(self.Ticks) > 0: + self.StartTick = self.Ticks[0] + event.GetPosition() + wx.CallAfter(self.NewDataAvailable, None, True) def InsertValue(self, iec_path, idx = None, force=False, axis3D=False):
idx = self.Table.GetNumberRows()
@@ -541,6 +787,7 @@
def Reset3DGraphics(self):
+ self.Axis3DValues = None axis = [item for item in self.Table.GetData() if item.GetAxis3D()]
@@ -548,19 +795,16 @@
if len(xaxis) > 0 and len(yaxis) > 0 and len(zaxis) > 0:
max_tick = max(xaxis[0, 0], yaxis[0, 0], zaxis[0, 0])
- self.Axis3DValues = [(numpy.argmin(abs(item.GetData()[:, 0] - max_tick)), item)
+ self.Axis3DValues = (axis, max_tick) - self.Axis3DValues = [(0, item) for item in axis]
- self.Axis3DValues = None
+ self.Axis3DValues = (axis, 0) def OnGraphics3DMotion(self, event):
if current_time - self.LastMotionTime > REFRESH_PERIOD:
self.LastMotionTime = current_time
Axes3D._on_move(self.Graphics3DAxes, event)
def RefreshGraphicsCanvasWindowScrollbars(self):
xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
window_size = self.GraphicsCanvasWindow.GetClientSize()
--- a/editors/GraphicViewer.py Wed Dec 19 13:32:51 2012 +0100
+++ b/editors/GraphicViewer.py Tue Jan 15 10:57:06 2013 +0100
@@ -311,7 +311,7 @@
self.StartTick = self.Data[self.StartIdx, 0]
- self.NewDataAvailable()
+ self.NewDataAvailable(None) def RefreshScrollBar(self):
@@ -355,7 +355,7 @@
self.StartTick = self.Data[0, 0] + event.GetPosition()
- self.NewDataAvailable(True)
+ self.NewDataAvailable(None, True) def OnResetButton(self, event):
@@ -368,7 +368,7 @@
self.StartTick = max(self.Data[0, 0], self.Data[-1, 0] - self.CurrentRange)
- self.NewDataAvailable(True)
+ self.NewDataAvailable(None, True) def OnResetZoomOffsetButton(self, event):
@@ -443,7 +443,7 @@
self.StartTick = max(self.Data[0, 0], min(self.CurrentMotionValue, self.Data[-1, 0] - self.CurrentRange))
self.CurrentMousePos = event.GetPosition()
- self.NewDataAvailable(True)
+ self.NewDataAvailable(None, True) def OnCanvasMouseWheel(self, event):