diff -r e87e0166d0a7 -r e9e17d3b2849 controls/DebugVariablePanel/DebugVariableGraphicPanel.py --- a/controls/DebugVariablePanel/DebugVariableGraphicPanel.py Mon Oct 14 10:31:32 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,958 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor -#based on the plcopen standard. -# -#Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD -# -#See COPYING file for copyrights details. -# -#This library is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public -#License as published by the Free Software Foundation; either -#version 2.1 of the License, or (at your option) any later version. -# -#This library is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -#General Public License for more details. -# -#You should have received a copy of the GNU General Public -#License along with this library; if not, write to the Free Software -#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from types import TupleType -import math -import numpy - -import wx -import wx.lib.buttons - -import matplotlib -import matplotlib.pyplot -from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap - -from editors.DebugViewer import DebugViewer -from util.BitmapLibrary import GetBitmap - -from DebugVariableItem import DebugVariableItem -from DebugVariableTextViewer import DebugVariableTextViewer -from DebugVariableGraphicViewer import * - -MILLISECOND = 1000000 # Number of nanosecond in a millisecond -SECOND = 1000 * MILLISECOND # Number of nanosecond in a second -MINUTE = 60 * SECOND # Number of nanosecond in a minute -HOUR = 60 * MINUTE # Number of nanosecond in a hour -DAY = 24 * HOUR # Number of nanosecond in a day - -# List of values possible for graph range -# Format is [(time_in_plain_text, value_in_nanosecond),...] -RANGE_VALUES = \ - [("%dms" % i, i * MILLISECOND) for i in (10, 20, 50, 100, 200, 500)] + \ - [("%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)] - -# Scrollbar increment in pixel -SCROLLBAR_UNIT = 10 - -def compute_mask(x, y): - return [(xp if xp == yp else "*") - for xp, yp in zip(x, y)] - -def NextTick(variables): - next_tick = None - for item, data in variables: - if len(data) == 0: - continue - - next_tick = (data[0][0] - if next_tick is None - else min(next_tick, data[0][0])) - - return next_tick - -#------------------------------------------------------------------------------- -# Debug Variable Graphic Panel Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Graphic -Panel -""" - -class DebugVariableDropTarget(wx.TextDropTarget): - - def __init__(self, window): - """ - Constructor - @param window: Reference to the Debug Variable Panel - """ - wx.TextDropTarget.__init__(self) - self.ParentWindow = window - - def __del__(self): - """ - Destructor - """ - # Remove reference to Debug Variable Panel - self.ParentWindow = None - - def OnDragOver(self, x, y, d): - """ - Function called when mouse is dragged over Drop Target - @param x: X coordinate of mouse pointer - @param y: Y coordinate of mouse pointer - @param d: Suggested default for return value - """ - # Signal Debug Variable Panel to refresh highlight giving mouse position - self.ParentWindow.RefreshHighlight(x, y) - return wx.TextDropTarget.OnDragOver(self, x, y, d) - - def OnDropText(self, x, y, data): - """ - Function called when mouse is released in Drop Target - @param x: X coordinate of mouse pointer - @param y: Y coordinate of mouse pointer - @param data: Text associated to drag'n drop - """ - # Signal Debug Variable Panel to reset highlight - self.ParentWindow.ResetHighlight() - - message = None - - # Check that data is valid regarding DebugVariablePanel - try: - values = eval(data) - if not isinstance(values, TupleType): - raise ValueError - except: - message = _("Invalid value \"%s\" for debug variable")%data - values = None - - # Display message if data is invalid - if message is not None: - wx.CallAfter(self.ShowMessage, message) - - # Data contain a reference to a variable to debug - elif values[1] == "debug": - - # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel - if len(values) > 2 and values[2] == "move": - self.ParentWindow.MoveValue(values[0]) - - # Drag'n Drop was initiated by another control of Beremiz - else: - self.ParentWindow.InsertValue(values[0], force=True) - - def OnLeave(self): - """ - Function called when mouse is leave Drop Target - """ - # Signal Debug Variable Panel to reset highlight - self.ParentWindow.ResetHighlight() - return wx.TextDropTarget.OnLeave(self) - - def ShowMessage(self, message): - """ - Show error message in Error Dialog - @param message: Error message to display - """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) - dialog.ShowModal() - dialog.Destroy() - - -#------------------------------------------------------------------------------- -# Debug Variable Graphic Panel Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a graphs -""" - -class DebugVariableGraphicPanel(wx.Panel, DebugViewer): - - def __init__(self, parent, producer, window): - """ - Constructor - @param parent: Reference to the parent wx.Window - @param producer: Object receiving debug value and dispatching them to - consumers - @param window: Reference to Beremiz frame - """ - wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) - - # Save Reference to Beremiz frame - self.ParentWindow = window - - # Variable storing flag indicating that variable displayed in table - # received new value and then table need to be refreshed - self.HasNewData = False - - # Variable storing flag indicating that refresh has been forced, and - # that next time refresh is possible, it will be done even if no new - # data is available - self.Force = False - - self.SetBackgroundColour(wx.WHITE) - - main_sizer = wx.BoxSizer(wx.VERTICAL) - - self.Ticks = numpy.array([]) # List of tick received - self.StartTick = 0 # Tick starting range of data displayed - self.Fixed = False # Flag that range of data is fixed - self.CursorTick = None # Tick of cursor for displaying values - - self.DraggingAxesPanel = None - self.DraggingAxesBoundingBox = None - self.DraggingAxesMousePos = None - self.VetoScrollEvent = False - - self.VariableNameMask = [] - - self.GraphicPanels = [] - - graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) - - range_label = wx.StaticText(self, label=_('Range:')) - graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) - - self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) - self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) - graphics_button_sizer.AddWindow(self.CanvasRange, 1, - border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - self.CanvasRange.Clear() - default_range_idx = 0 - for idx, (text, value) in enumerate(RANGE_VALUES): - self.CanvasRange.Append(text) - if text == "1s": - default_range_idx = idx - self.CanvasRange.SetSelection(default_range_idx) - - for name, bitmap, help in [ - ("CurrentButton", "current", _("Go to current value")), - ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: - button = wx.lib.buttons.GenBitmapButton(self, - 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, - 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) - main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) - - self.TickSizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW) - - self.TickLabel = wx.StaticText(self) - self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT) - - self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER) - self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW) - - self.TickTimeLabel = wx.StaticText(self) - self.TickSizer.AddWindow(self.TickTimeLabel) - - self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) - self.GraphicsWindow.SetBackgroundColour(wx.WHITE) - self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) - self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground) - self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint) - self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) - self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel) - - main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) - - self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) - self.GraphicsWindow.SetSizer(self.GraphicsSizer) - - DebugViewer.__init__(self, producer, True) - - self.SetSizer(main_sizer) - - def SetTickTime(self, ticktime=0): - """ - Set Ticktime for calculate data range according to time range selected - @param ticktime: Ticktime to apply to range (default: 0) - """ - # Save ticktime - self.Ticktime = ticktime - - # Set ticktime to millisecond if undefined - if self.Ticktime == 0: - self.Ticktime = MILLISECOND - - # Calculate range to apply to data - self.CurrentRange = RANGE_VALUES[ - self.CanvasRange.GetSelection()][1] / self.Ticktime - - def SetDataProducer(self, producer): - """ - Set Data Producer - @param producer: Data Producer - """ - DebugViewer.SetDataProducer(self, producer) - - # Set ticktime if data producer is available - if self.DataProducer is not None: - self.SetTickTime(self.DataProducer.GetTicktime()) - - def RefreshNewData(self, *args, **kwargs): - """ - Called to refresh Panel according to values received by variables - Can receive any parameters (not used here) - """ - # Refresh graphs if new data is available or refresh is forced - if self.HasNewData or self.Force: - self.HasNewData = False - self.RefreshView() - - DebugViewer.RefreshNewData(self, *args, **kwargs) - - def NewDataAvailable(self, ticks, *args, **kwargs): - """ - Called by DataProducer for each tick captured or by panel to refresh - graphs - @param tick: PLC tick captured - All other parameters are passed to refresh function - """ - # If tick given - if ticks is not None: - tick = ticks[-1] - - # Save tick as start tick for range if data is still empty - if len(self.Ticks) == 0: - self.StartTick = ticks[0] - - # Add tick to list of ticks received - self.Ticks = numpy.append(self.Ticks, ticks) - - # Update start tick for range if range follow ticks received - if not self.Fixed or tick < self.StartTick + self.CurrentRange: - self.StartTick = max(self.StartTick, tick - self.CurrentRange) - - # Force refresh if graph is fixed because range of data received - # is too small to fill data range selected - if self.Fixed and \ - self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: - self.Force = True - - self.HasNewData = False - self.RefreshView() - - else: - DebugViewer.NewDataAvailable(self, ticks, *args, **kwargs) - - def ForceRefresh(self): - """ - Called to force refresh of graphs - """ - self.Force = True - wx.CallAfter(self.NewDataAvailable, None, True) - - def SetCursorTick(self, cursor_tick): - """ - Set Cursor for displaying values of items at a tick given - @param cursor_tick: Tick of cursor - """ - # Save cursor tick - self.CursorTick = cursor_tick - self.Fixed = cursor_tick is not None - self.UpdateCursorTick() - - def MoveCursorTick(self, move): - if self.CursorTick is not None: - cursor_tick = max(self.Ticks[0], - min(self.CursorTick + move, - self.Ticks[-1])) - cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick)) - if self.Ticks[cursor_tick_idx] == self.CursorTick: - cursor_tick_idx = max(0, - min(cursor_tick_idx + abs(move) / move, - len(self.Ticks) - 1)) - self.CursorTick = self.Ticks[cursor_tick_idx] - self.StartTick = max(self.Ticks[ - numpy.argmin(numpy.abs(self.Ticks - - self.CursorTick + self.CurrentRange))], - min(self.StartTick, self.CursorTick)) - self.RefreshCanvasPosition() - self.UpdateCursorTick() - - def ResetCursorTick(self): - self.CursorTick = None - self.Fixed = False - self.UpdateCursorTick() - - def UpdateCursorTick(self): - for panel in self.GraphicPanels: - if isinstance(panel, DebugVariableGraphicViewer): - panel.SetCursorTick(self.CursorTick) - self.ForceRefresh() - - def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start): - if len(panel.GetItems()) > 1: - self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) - self.DraggingAxesPanel.SetCursorTick(self.CursorTick) - width, height = panel.GetSize() - self.DraggingAxesPanel.SetSize(wx.Size(width, height)) - self.DraggingAxesPanel.ResetGraphics() - self.DraggingAxesPanel.SetPosition(wx.Point(0, -height)) - else: - self.DraggingAxesPanel = panel - self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True) - self.DraggingAxesMousePos = wx.Point( - x_mouse_start - self.DraggingAxesBoundingBox.x, - y_mouse_start - self.DraggingAxesBoundingBox.y) - self.MoveDragNDrop(x_mouse, y_mouse) - - def MoveDragNDrop(self, x_mouse, y_mouse): - self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x - self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y - self.RefreshHighlight(x_mouse, y_mouse) - - def RefreshHighlight(self, x_mouse, y_mouse): - for idx, panel in enumerate(self.GraphicPanels): - x, y = panel.GetPosition() - width, height = panel.GetSize() - rect = wx.Rect(x, y, width, height) - if (rect.InsideXY(x_mouse, y_mouse) or - idx == 0 and y_mouse < 0 or - idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]): - panel.RefreshHighlight(x_mouse - x, y_mouse - y) - else: - panel.SetHighlight(HIGHLIGHT_NONE) - if wx.Platform == "__WXMSW__": - self.RefreshView() - else: - self.ForceRefresh() - - def ResetHighlight(self): - for panel in self.GraphicPanels: - panel.SetHighlight(HIGHLIGHT_NONE) - if wx.Platform == "__WXMSW__": - self.RefreshView() - else: - self.ForceRefresh() - - def IsDragging(self): - return self.DraggingAxesPanel is not None - - def GetDraggingAxesClippingRegion(self, panel): - x, y = panel.GetPosition() - width, height = panel.GetSize() - bbox = wx.Rect(x, y, width, height) - bbox = bbox.Intersect(self.DraggingAxesBoundingBox) - bbox.x -= x - bbox.y -= y - return bbox - - def GetDraggingAxesPosition(self, panel): - x, y = panel.GetPosition() - return wx.Point(self.DraggingAxesBoundingBox.x - x, - self.DraggingAxesBoundingBox.y - y) - - def StopDragNDrop(self, variable, x_mouse, y_mouse): - if self.DraggingAxesPanel not in self.GraphicPanels: - self.DraggingAxesPanel.Destroy() - self.DraggingAxesPanel = None - self.DraggingAxesBoundingBox = None - self.DraggingAxesMousePos = None - for idx, panel in enumerate(self.GraphicPanels): - panel.SetHighlight(HIGHLIGHT_NONE) - xw, yw = panel.GetPosition() - width, height = panel.GetSize() - bbox = wx.Rect(xw, yw, width, height) - if bbox.InsideXY(x_mouse, y_mouse): - panel.ShowButtons(True) - merge_type = GRAPH_PARALLEL - if isinstance(panel, DebugVariableTextViewer) or panel.Is3DCanvas(): - if y_mouse > yw + height / 2: - idx += 1 - wx.CallAfter(self.MoveValue, variable, idx, True) - else: - rect = panel.GetAxesBoundingBox(True) - if rect.InsideXY(x_mouse, y_mouse): - merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height) - if merge_rect.InsideXY(x_mouse, y_mouse): - merge_type = GRAPH_ORTHOGONAL - wx.CallAfter(self.MergeGraphs, variable, idx, merge_type, force=True) - else: - if y_mouse > yw + height / 2: - idx += 1 - wx.CallAfter(self.MoveValue, variable, idx, True) - self.ForceRefresh() - return - width, height = self.GraphicsWindow.GetVirtualSize() - rect = wx.Rect(0, 0, width, height) - if rect.InsideXY(x_mouse, y_mouse): - wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) - self.ForceRefresh() - - def RefreshGraphicsSizer(self): - self.GraphicsSizer.Clear() - - for panel in self.GraphicPanels: - self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) - - self.GraphicsSizer.Layout() - self.RefreshGraphicsWindowScrollbars() - - def RefreshView(self): - self.RefreshCanvasPosition() - - width, height = self.GraphicsWindow.GetVirtualSize() - bitmap = wx.EmptyBitmap(width, height) - dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap) - dc.Clear() - dc.BeginDrawing() - if self.DraggingAxesPanel is not None: - destBBox = self.DraggingAxesBoundingBox - srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox() - - srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None) - srcDC = wx.MemoryDC() - srcDC.SelectObject(srcBmp) - - dc.Blit(destBBox.x, destBBox.y, - int(destBBox.width), int(destBBox.height), - srcDC, srcBBox.x, srcBBox.y) - dc.EndDrawing() - - if not self.Fixed or self.Force: - self.Force = False - refresh_graphics = True - else: - refresh_graphics = False - - if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels: - self.DraggingAxesPanel.RefreshViewer(refresh_graphics) - for panel in self.GraphicPanels: - if isinstance(panel, DebugVariableGraphicViewer): - panel.RefreshViewer(refresh_graphics) - else: - panel.RefreshViewer() - - if self.CursorTick is not None: - tick = self.CursorTick - elif len(self.Ticks) > 0: - tick = self.Ticks[-1] - else: - tick = None - if tick is not None: - self.TickLabel.SetLabel("Tick: %d" % tick) - tick_duration = int(tick * self.Ticktime) - not_null = False - duration = "" - for value, format in [(tick_duration / DAY, "%dd"), - ((tick_duration % DAY) / HOUR, "%dh"), - ((tick_duration % HOUR) / MINUTE, "%dm"), - ((tick_duration % MINUTE) / SECOND, "%ds")]: - - if value > 0 or not_null: - duration += format % value - not_null = True - - duration += "%gms" % (float(tick_duration % SECOND) / MILLISECOND) - self.TickTimeLabel.SetLabel("t: %s" % duration) - else: - self.TickLabel.SetLabel("") - self.TickTimeLabel.SetLabel("") - self.TickSizer.Layout() - - def SubscribeAllDataConsumers(self): - DebugViewer.SubscribeAllDataConsumers(self) - - if self.DataProducer is not None: - if self.DataProducer is not None: - self.SetTickTime(self.DataProducer.GetTicktime()) - - self.ResetCursorTick() - - for panel in self.GraphicPanels[:]: - panel.SubscribeAllDataConsumers() - if panel.ItemsIsEmpty(): - if panel.HasCapture(): - panel.ReleaseMouse() - self.GraphicPanels.remove(panel) - panel.Destroy() - - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - self.ForceRefresh() - - def ResetView(self): - self.UnsubscribeAllDataConsumers() - - self.Fixed = False - for panel in self.GraphicPanels: - panel.Destroy() - self.GraphicPanels = [] - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - - def SetCanvasPosition(self, tick): - tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange)) - self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))] - self.Fixed = True - self.RefreshCanvasPosition() - self.ForceRefresh() - - def RefreshCanvasPosition(self): - if len(self.Ticks) > 0: - pos = int(self.StartTick - self.Ticks[0]) - range = int(self.Ticks[-1] - self.Ticks[0]) - else: - pos = 0 - range = 0 - self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) - - def ChangeRange(self, dir, tick=None): - current_range = self.CurrentRange - current_range_idx = self.CanvasRange.GetSelection() - new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1)) - if new_range_idx != current_range_idx: - self.CanvasRange.SetSelection(new_range_idx) - self.CurrentRange = RANGE_VALUES[new_range_idx][1] / self.Ticktime - if len(self.Ticks) > 0: - if tick is None: - tick = self.StartTick + self.CurrentRange / 2. - new_start_tick = min(tick - (tick - self.StartTick) * self.CurrentRange / current_range, - self.Ticks[-1] - self.CurrentRange) - self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))] - self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange - self.ForceRefresh() - - def RefreshRange(self): - if len(self.Ticks) > 0: - if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: - self.Fixed = False - if self.Fixed: - self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange) - else: - self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) - self.ForceRefresh() - - def OnRangeChanged(self, event): - try: - self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime - except ValueError, e: - self.CanvasRange.SetValue(str(self.CurrentRange)) - wx.CallAfter(self.RefreshRange) - event.Skip() - - def OnCurrentButton(self, event): - if len(self.Ticks) > 0: - self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) - self.ResetCursorTick() - event.Skip() - - def CopyDataToClipboard(self, variables): - text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables]) - next_tick = NextTick(variables) - while next_tick is not None: - values = [] - for item, data in variables: - if len(data) > 0: - if next_tick == data[0][0]: - var_type = item.GetVariableType() - if var_type in ["STRING", "WSTRING"]: - value = item.GetRawValue(int(data.pop(0)[2])) - if var_type == "STRING": - values.append("'%s'" % value) - else: - values.append('"%s"' % value) - else: - values.append("%.3f" % data.pop(0)[1]) - else: - values.append("") - else: - values.append("") - text += "%d;%s;\n" % (next_tick, ";".join(values)) - next_tick = NextTick(variables) - self.ParentWindow.SetCopyBuffer(text) - - def OnExportGraphButton(self, event): - items = reduce(lambda x, y: x + y, - [panel.GetItems() for panel in self.GraphicPanels], - []) - variables = [(item, [entry for entry in item.GetData()]) - for item in items - if item.IsNumVariable()] - wx.CallAfter(self.CopyDataToClipboard, variables) - event.Skip() - - def OnPositionChanging(self, event): - if len(self.Ticks) > 0: - self.StartTick = self.Ticks[0] + event.GetPosition() - self.Fixed = True - self.ForceRefresh() - event.Skip() - - def GetRange(self): - return self.StartTick, self.StartTick + self.CurrentRange - - def GetViewerIndex(self, viewer): - if viewer in self.GraphicPanels: - return self.GraphicPanels.index(viewer) - return None - - def IsViewerFirst(self, viewer): - return viewer == self.GraphicPanels[0] - - def HighlightPreviousViewer(self, viewer): - if self.IsViewerFirst(viewer): - return - idx = self.GetViewerIndex(viewer) - if idx is None: - return - self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER) - - def ResetVariableNameMask(self): - items = [] - for panel in self.GraphicPanels: - items.extend(panel.GetItems()) - if len(items) > 1: - self.VariableNameMask = reduce(compute_mask, - [item.GetVariable().split('.') for item in items]) - elif len(items) > 0: - self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*'] - else: - self.VariableNameMask = [] - self.MaskLabel.ChangeValue(".".join(self.VariableNameMask)) - self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition()) - - def GetVariableNameMask(self): - return self.VariableNameMask - - def InsertValue(self, iec_path, idx = None, force=False, graph=False): - for panel in self.GraphicPanels: - if panel.GetItem(iec_path) is not None: - if graph and isinstance(panel, DebugVariableTextViewer): - self.ToggleViewerType(panel) - return - if idx is None: - idx = len(self.GraphicPanels) - item = DebugVariableItem(self, iec_path, True) - result = self.AddDataConsumer(iec_path.upper(), item) - if result is not None or force: - - self.Freeze() - if item.IsNumVariable() and graph: - panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) - if self.CursorTick is not None: - panel.SetCursorTick(self.CursorTick) - else: - panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item]) - if idx is not None: - self.GraphicPanels.insert(idx, panel) - else: - self.GraphicPanels.append(panel) - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - self.Thaw() - self.ForceRefresh() - - def MoveValue(self, iec_path, idx = None, graph=False): - if idx is None: - idx = len(self.GraphicPanels) - source_panel = None - item = None - for panel in self.GraphicPanels: - item = panel.GetItem(iec_path) - if item is not None: - source_panel = panel - break - if source_panel is not None: - source_panel_idx = self.GraphicPanels.index(source_panel) - - if (len(source_panel.GetItems()) == 1): - - if source_panel_idx < idx: - self.GraphicPanels.insert(idx, source_panel) - self.GraphicPanels.pop(source_panel_idx) - elif source_panel_idx > idx: - self.GraphicPanels.pop(source_panel_idx) - self.GraphicPanels.insert(idx, source_panel) - else: - return - - else: - source_panel.RemoveItem(item) - source_size = source_panel.GetSize() - if item.IsNumVariable() and graph: - panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) - panel.SetCanvasHeight(source_size.height) - if self.CursorTick is not None: - panel.SetCursorTick(self.CursorTick) - - else: - panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item]) - - self.GraphicPanels.insert(idx, panel) - - if source_panel.ItemsIsEmpty(): - if source_panel.HasCapture(): - source_panel.ReleaseMouse() - source_panel.Destroy() - self.GraphicPanels.remove(source_panel) - - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - self.ForceRefresh() - - def MergeGraphs(self, source, target_idx, merge_type, force=False): - source_item = None - source_panel = None - for panel in self.GraphicPanels: - source_item = panel.GetItem(source) - if source_item is not None: - source_panel = panel - break - if source_item is None: - item = DebugVariableItem(self, source, True) - if item.IsNumVariable(): - result = self.AddDataConsumer(source.upper(), item) - if result is not None or force: - source_item = item - if source_item is not None and source_item.IsNumVariable(): - if source_panel is not None: - source_size = source_panel.GetSize() - else: - source_size = None - target_panel = self.GraphicPanels[target_idx] - graph_type = target_panel.GraphType - if target_panel != source_panel: - if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or - merge_type == GRAPH_ORTHOGONAL and - (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or - graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)): - return - - if source_panel is not None: - source_panel.RemoveItem(source_item) - if source_panel.ItemsIsEmpty(): - if source_panel.HasCapture(): - source_panel.ReleaseMouse() - source_panel.Destroy() - self.GraphicPanels.remove(source_panel) - elif (merge_type != graph_type and len(target_panel.Items) == 2): - target_panel.RemoveItem(source_item) - else: - target_panel = None - - if target_panel is not None: - target_panel.AddItem(source_item) - target_panel.GraphType = merge_type - size = target_panel.GetSize() - if merge_type == GRAPH_ORTHOGONAL: - target_panel.SetCanvasHeight(size.width) - elif source_size is not None and source_panel != target_panel: - target_panel.SetCanvasHeight(size.height + source_size.height) - else: - target_panel.SetCanvasHeight(size.height) - target_panel.ResetGraphics() - - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - self.ForceRefresh() - - def DeleteValue(self, source_panel, item=None): - source_idx = self.GetViewerIndex(source_panel) - if source_idx is not None: - - if item is None: - source_panel.ClearItems() - source_panel.Destroy() - self.GraphicPanels.remove(source_panel) - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - else: - source_panel.RemoveItem(item) - if source_panel.ItemsIsEmpty(): - source_panel.Destroy() - self.GraphicPanels.remove(source_panel) - self.ResetVariableNameMask() - self.RefreshGraphicsSizer() - if len(self.GraphicPanels) == 0: - self.Fixed = False - self.ResetCursorTick() - self.ForceRefresh() - - def ToggleViewerType(self, panel): - panel_idx = self.GetViewerIndex(panel) - if panel_idx is not None: - self.GraphicPanels.remove(panel) - items = panel.GetItems() - if isinstance(panel, DebugVariableGraphicViewer): - for idx, item in enumerate(items): - new_panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item]) - self.GraphicPanels.insert(panel_idx + idx, new_panel) - else: - new_panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, items, GRAPH_PARALLEL) - self.GraphicPanels.insert(panel_idx, new_panel) - panel.Destroy() - self.RefreshGraphicsSizer() - self.ForceRefresh() - - def ResetGraphicsValues(self): - self.Ticks = numpy.array([]) - self.StartTick = 0 - for panel in self.GraphicPanels: - panel.ResetItemsData() - self.ResetCursorTick() - - def RefreshGraphicsWindowScrollbars(self): - xstart, ystart = self.GraphicsWindow.GetViewStart() - window_size = self.GraphicsWindow.GetClientSize() - vwidth, vheight = self.GraphicsSizer.GetMinSize() - posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT)) - posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT)) - self.GraphicsWindow.Scroll(posx, posy) - self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy) - - def OnGraphicsWindowEraseBackground(self, event): - pass - - def OnGraphicsWindowPaint(self, event): - self.RefreshView() - event.Skip() - - def OnGraphicsWindowResize(self, event): - size = self.GetSize() - for panel in self.GraphicPanels: - panel_size = panel.GetSize() - if (isinstance(panel, DebugVariableGraphicViewer) and - panel.GraphType == GRAPH_ORTHOGONAL and - panel_size.width == panel_size.height): - panel.SetCanvasHeight(size.width) - self.RefreshGraphicsWindowScrollbars() - self.GraphicsSizer.Layout() - event.Skip() - - def OnGraphicsWindowMouseWheel(self, event): - if self.VetoScrollEvent: - self.VetoScrollEvent = False - else: - event.Skip()