--- a/controls/DebugVariablePanel.py Tue Jan 29 18:01:51 2013 +0100
+++ b/controls/DebugVariablePanel.py Tue Jan 29 23:41:00 2013 +0100
@@ -22,7 +22,7 @@
#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, FloatType
+from types import TupleType, ListType, FloatType from time import time as gettime
@@ -35,7 +35,6 @@
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
- #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
from mpl_toolkits.mplot3d import Axes3D
@@ -46,18 +45,6 @@
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)]
-GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
def AppendMenu(parent, help, id, kind, text):
parent.Append(help=help, id=id, kind=kind, text=text)
@@ -81,20 +68,26 @@
if self.Parent and self.Variable != variable:
self.RefreshVariableType()
- self.Parent.RefreshGrid()
+ self.Parent.RefreshView()
+ def GetVariable(self, max_size=None): + variable = self.Variable + if max_size is not None: + max_size = max(max_size, 10) + if len(variable) > max_size: + variable = "..." + variable[-(max_size - 3):] def RefreshVariableType(self):
self.VariableType = self.Parent.GetDataType(self.Variable)
def GetVariableType(self):
def GetData(self, start_tick=None, end_tick=None):
- if self.IsNumVariable():
+ if USE_MPL and self.IsNumVariable(): @@ -127,7 +120,7 @@
return self.Parent.IsNumType(self.VariableType)
def NewValue(self, tick, value, forced=False):
- if self.IsNumVariable():
+ if USE_MPL and self.IsNumVariable(): num_value = {True:1., False:0.}.get(value, float(value))
if self.MinValue is None:
self.MinValue = num_value
@@ -164,7 +157,7 @@
def GetNearestData(self, tick, adjust):
- if self.IsNumVariable():
+ if USE_MPL and self.IsNumVariable(): new_cursor = numpy.argmin(abs(ticks - tick))
if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
@@ -246,11 +239,15 @@
class DebugVariableDropTarget(wx.TextDropTarget):
- def __init__(self, parent, control):
+ def __init__(self, parent, control=None): wx.TextDropTarget.__init__(self)
self.ParentWindow = parent
self.ParentControl = control
+ self.ParentWindow = None + self.ParentControl = None def OnDropText(self, x, y, data):
@@ -265,167 +262,509 @@
wx.CallAfter(self.ShowMessage, message)
elif values is not None and values[1] == "debug":
- if self.ParentControl == self.ParentWindow.VariablesGrid:
- x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
- row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
+ if isinstance(self.ParentControl, CustomGrid): + x, y = self.ParentControl.CalcUnscrolledPosition(x, y) + row = self.ParentControl.YToRow(y - self.ParentControl.GetColLabelSize()) row = self.ParentWindow.Table.GetNumberRows()
self.ParentWindow.InsertValue(values[0], row, force=True)
- width, height = self.ParentWindow.GraphicsCanvas.GetSize()
+ elif self.ParentControl is not None: + width, height = self.ParentControl.Canvas.GetSize() + target_idx = self.ParentControl.GetIndex() merge_type = GRAPH_PARALLEL
- for infos in self.ParentWindow.GraphicsAxes:
- if infos["axes"] != self.ParentWindow.Graphics3DAxes:
- ax, ay, aw, ah = infos["axes"].get_position().bounds
- rect = wx.Rect(ax * width, height - (ay + ah) * height,
- aw * width, ah * height)
- if rect.InsideXY(x, y):
- merge_rect = wx.Rect(ax * width, height - (ay + ah) * height,
- aw * width / 2., ah * height)
- if merge_rect.InsideXY(x, y):
- merge_type = GRAPH_ORTHOGONAL
- self.ParentWindow.MergeGraphs(values[0], target, merge_type, force=True)
+ if self.ParentControl.Is3DCanvas(): + self.ParentWindow.InsertValue(values[0], target_idx, force=True) + ax, ay, aw, ah = self.ParentControl.Axes.get_position().bounds + rect = wx.Rect(ax * width, height - (ay + ah) * height, + aw * width, ah * height) + if rect.InsideXY(x, y): + merge_rect = wx.Rect(ax * width, height - (ay + ah) * height, + aw * width / 2., ah * height) + if merge_rect.InsideXY(x, y): + merge_type = GRAPH_ORTHOGONAL + wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True) + self.ParentWindow.InsertValue(values[0], target_idx, force=True) + self.ParentWindow.InsertValue(values[0], force=True) def ShowMessage(self, message):
dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
-def NextTick(variables):
- for var_name, data in variables:
+ 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)] + GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2) + def NextTick(variables): + for var_name, data in variables: + next_tick = min(next_tick, data[0][0]) + def OrthogonalData(item, start_tick, end_tick): + 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: + center = (min_value + max_value) / 2. + range = max(1.0, max_value - min_value) + return data, center - range * 0.55, center + range * 0.55 + class DebugVariableViewer(wx.Panel): + def __init__(self, parent, window, items=[]): + wx.Panel.__init__(self, parent) + self.SetBackgroundColour(wx.WHITE) + self.SetDropTarget(DebugVariableDropTarget(window, self)) + self.ParentWindow = window + self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) + self.MainSizer.AddGrowableCol(0) + self.SetSizer(self.MainSizer) + self.ParentWindow = None + return self.ParentWindow.GetViewerIndex(self) + def GetItem(self, variable): + for item in self.Items: + if item.GetVariable() == variable: + def GetVariables(self): + if len(self.Items) > 1: + variables = [item.GetVariable() for item in self.Items] + if self.GraphType == GRAPH_ORTHOGONAL: + return tuple(variables) + return self.Items[0].GetVariable() + def AddItem(self, item): + self.Items.append(item) + def RemoveItem(self, item): + self.Items.remove(item) + for item in self.Items: + self.ParentWindow.RemoveDataConsumer(item) + return len(self.Items) == 0 + def UnregisterObsoleteData(self): + for item in self.Items[:]: + iec_path = item.GetVariable().upper() + if self.ParentWindow.GetDataType(iec_path) is None: + self.ParentWindow.RemoveDataConsumer(item) + self.ParentWindow.AddDataConsumer(iec_path, item) + item.RefreshVariableType() + for item in self.Items: + def OnForceButton(self, event): + if len(self.Items) == 1: + wx.CallAfter(self.ForceValue, self.Items[0]) + menu = wx.Menu(title='') + for item in self.Items: + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, + text=item.GetVariable(20)) + self.GetForceVariableMenuFunction(item), + def OnReleaseButton(self, event): + if len(self.Items) == 1: + wx.CallAfter(self.ReleaseValue, self.Items[0]) + menu = wx.Menu(title='') + for item in self.Items: + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, + text=item.GetVariable(20)) + self.GetReleaseVariableMenuFunction(item), + def OnDeleteButton(self, event): + if len(self.Items) == 1: + wx.CallAfter(self.ParentWindow.DeleteValue, self, self.Items[0]) - next_tick = min(next_tick, data[0][0])
-def OrthogonalData(item, start_tick, end_tick):
- 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:
- center = (min_value + max_value) / 2.
- range = max(1.0, max_value - min_value)
- return data, center - range * 0.55, center + range * 0.55
+ menu = wx.Menu(title='') + for item in self.Items: + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, + text=item.GetVariable(20)) + self.GetDeleteValueMenuFunction(item), + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All")) + self.Bind(wx.EVT_MENU, self.OnDeleteAllValuesMenu, id=new_id) + def GetForceVariableMenuFunction(self, item): + def ForceVariableFunction(event): + return ForceVariableFunction + def GetReleaseVariableMenuFunction(self, item): + def ReleaseVariableFunction(event): + self.ReleaseValue(item) + return ReleaseVariableFunction + def GetDeleteValueMenuFunction(self, item): + def DeleteValueFunction(event): + self.ParentWindow.DeleteValue(self, item) + return DeleteValueFunction + def ForceValue(self, item): + iec_path = item.GetVariable().upper() + iec_type = self.ParentWindow.GetDataType(iec_path) + if iec_type is not None: + dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) + if dialog.ShowModal() == wx.ID_OK: + self.ParentWindow.ForceDataValue(iec_path, dialog.GetValue()) + def ReleaseValue(self, item): + iec_path = item.GetVariable().upper() + self.ParentWindow.ReleaseDataValue(iec_path) + def OnDeleteAllValuesMenu(self, event): + wx.CallAfter(self.ParentWindow.DeleteValue, self) + class DebugVariableText(DebugVariableViewer): + viewer_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) + viewer_sizer.AddGrowableCol(0) + self.MainSizer.AddSizer(viewer_sizer, border=5, + flag=wx.ALL|wx.GROW|wx.ALIGN_CENTER_VERTICAL) + variable_name_label = wx.TextCtrl(self, size=wx.Size(0, -1), + value=self.Items[0].GetVariable(), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER) + variable_name_label.SetSelection(variable_name_label.GetLastPosition(), -1) + viewer_sizer.AddWindow(variable_name_label, flag=wx.GROW) + self.ValueLabel = wx.TextCtrl(self, + size=wx.Size(100, -1), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER) + viewer_sizer.AddWindow(self.ValueLabel, + border=5, flag=wx.LEFT) + button_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.MainSizer.AddSizer(button_sizer, border=5, + flag=wx.TOP|wx.BOTTOM|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + ("ForceButton", "force", _("Force value")), + ("ReleaseButton", "release", _("Release value")), + ("DeleteButton", "remove_element", _("Remove debug variable"))] + for name, bitmap, help in buttons: + 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) + button_sizer.AddWindow(button, border=5, flag=wx.LEFT) + self.ValueLabel.ChangeValue(self.Items[0].GetValue()) + if self.Items[0].IsForced(): + self.ValueLabel.SetForegroundColour(wx.BLUE) + self.ValueLabel.SetForegroundColour(wx.BLACK) + self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1) + class DebugVariableGraphic(DebugVariableViewer): + def __init__(self, parent, window, items, graph_type): + DebugVariableViewer.__init__(self, parent, window, items) + self.GraphType = graph_type + self.Figure = matplotlib.figure.Figure(facecolor='w') + self.Canvas = FigureCanvas(self, -1, self.Figure) + self.Canvas.SetMinSize(wx.Size(200, 200)) + self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self)) + self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick) + self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW) + button_sizer = wx.BoxSizer(wx.VERTICAL) + self.MainSizer.AddSizer(button_sizer, border=5, + flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + ("ForceButton", "force", _("Force value")), + ("ReleaseButton", "release", _("Release value")), + ("SplitButton", "split", _("Split graphs")), + ("DeleteButton", "remove_element", _("Remove debug variable"))] + for name, bitmap, help in buttons: + 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) + button_sizer.AddWindow(button, border=5, flag=wx.LEFT) -class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
+ def OnCanvasClick(self, event): + if len(self.Items) == 1: + width, height = self.Canvas.GetSize() + x, y = event.GetPosition() + ax, ay, aw, ah = self.Axes.get_position().bounds + rect = wx.Rect(ax * width, height - (ay + ah) * height, + aw * width, ah * height) + if rect.InsideXY(x, y): + data = wx.TextDataObject(str((self.Items[0].GetVariable(), "debug"))) + dragSource = wx.DropSource(self.Canvas) + dragSource.SetData(data) + dragSource.DoDragDrop() + def OnMotion(self, event): + current_time = gettime() + if current_time - self.LastMotionTime > REFRESH_PERIOD: + self.LastMotionTime = current_time + Axes3D._on_move(self.Axes, event) + def OnSplitButton(self, event): + if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL: + wx.CallAfter(self.ParentWindow.SplitGraphs, self) + menu = wx.Menu(title='') + for item in self.Items: + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, + text=item.GetVariable(20)) + self.GetSplitGraphMenuFunction(item), + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All")) + self.Bind(wx.EVT_MENU, self.OnSplitAllGraphsMenu, id=new_id) + def ResetGraphics(self): + self.Axes = self.Figure.gca(projection='3d') + self.Axes.set_color_cycle(['b']) + self.LastMotionTime = gettime() + setattr(self.Axes, "_on_move", self.OnMotion) + self.Axes = self.Figure.gca() + if self.GraphType == GRAPH_ORTHOGONAL: + self.Figure.subplotpars.update(bottom=0.15) + self.SplitButton.Enable(len(self.Items) > 1) + def AddItem(self, item): + DebugVariableViewer.AddItem(self, item) + def RemoveItem(self, item): + DebugVariableViewer.RemoveItem(self, item) + def UnregisterObsoleteData(self): + DebugVariableViewer.UnregisterObsoleteData(self) + return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 + def Refresh(self, refresh_graphics=True): + start_tick, end_tick = self.ParentWindow.GetRange() + if self.GraphType == GRAPH_PARALLEL: + min_value = max_value = None + for idx, item in enumerate(self.Items): + data = item.GetData(start_tick, end_tick) + item_min_value, item_max_value = item.GetRange() + min_value = item_min_value + elif item_min_value is not None: + min_value = min(min_value, item_min_value) + max_value = item_max_value + elif item_max_value is not None: + max_value = max(max_value, item_max_value) + if len(self.Plots) <= idx: + self.Axes.plot(data[:, 0], data[:, 1])[0]) + self.Plots[idx].set_data(data[:, 0], data[:, 1]) + 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) + x_min, x_max = start_tick, end_tick + y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55 + min_start_tick = reduce(max, [item.GetData()[0, 0] + if len(item.GetData()) > 0], 0) + start_tick = max(start_tick, min_start_tick) + end_tick = max(end_tick, min_start_tick) + x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick) + y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick) + if x_data is not None and y_data is not None: + length = min(len(x_data), len(y_data)) + if len(self.Items) < 3: + if x_data is not None and y_data is not None: + if len(self.Plots) == 0: + self.Axes.plot(x_data[:, 1][:length], + y_data[:, 1][:length])[0]) + self.Plots[0].set_data( + while len(self.Axes.lines) > 0: + z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick) + if x_data is not None and y_data is not None and z_data is not None: + length = min(length, len(z_data)) + self.Axes.plot(x_data[:, 1][:length], + zs = z_data[:, 1][:length]) + self.Axes.set_zlim(z_min, z_max) + self.Axes.set_xlim(x_min, x_max) + self.Axes.set_ylim(y_min, y_max) + labels = ["%s: %s" % (item.GetVariable(), item.GetValue()) + for item in self.Items] + colors = [{True: 'b', False: 'k'}[item.IsForced()] for item in self.Items] + if self.GraphType == GRAPH_PARALLEL: + legend = self.Axes.legend(self.Plots, labels, + loc="upper left", frameon=False, + for t, color in zip(legend.get_texts(), colors): + self.Axes.set_xlabel(labels[0], fontdict={'size':'small','color':colors[0]}) + self.Axes.set_ylabel(labels[1], fontdict={'size':'small','color':colors[1]}) + self.Axes.set_zlabel(labels[2], fontdict={'size':'small','color':colors[2]}) + def GetSplitGraphMenuFunction(self, item): + def SplitGraphFunction(event): + self.ParentWindow.SplitGraphs(self, item) + return SplitGraphFunction + def OnSplitAllGraphsMenu(self, event): + self.ParentWindow.SplitGraphs(self) +class DebugVariablePanel(wx.Panel, DebugViewer): def __init__(self, parent, producer, window):
- wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
+ wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) self.ParentWindow = window
DebugViewer.__init__(self, producer, True)
- self.SetSashGravity(0.5)
- self.SetNeedUpdating(True)
- self.SetMinimumPaneSize(1)
- self.MainPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
- main_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
- main_panel_sizer.AddGrowableCol(0)
- main_panel_sizer.AddGrowableRow(1)
- button_sizer = wx.BoxSizer(wx.HORIZONTAL)
- main_panel_sizer.AddSizer(button_sizer, border=5,
- flag=wx.ALIGN_RIGHT|wx.ALL)
- for name, bitmap, help in [
- ("DeleteButton", "remove_element", _("Remove debug variable")),
- ("UpButton", "up", _("Move debug variable up")),
- ("DownButton", "down", _("Move debug variable down"))]:
- button = wx.lib.buttons.GenBitmapButton(self.MainPanel, bitmap=GetBitmap(bitmap),
- size=wx.Size(28, 28), style=wx.NO_BORDER)
- button.SetToolTipString(help)
- setattr(self, name, button)
- button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
- self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL)
- self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK,
- self.OnVariablesGridCellRightClick)
- self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK,
- self.OnVariablesGridCellLeftClick)
- main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
- self.MainPanel.SetSizer(main_panel_sizer)
- self.Ticks = numpy.array([])
- self.RangeValues = None
- self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
- self.VariablesGrid.SetTable(self.Table)
- self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
- "Down": self.DownButton})
- def _AddVariable(new_row):
- return self.VariablesGrid.GetGridCursorRow()
- setattr(self.VariablesGrid, "_AddRow", _AddVariable)
- def _DeleteVariable(row):
- item = self.Table.GetItem(row)
- self.RemoveDataConsumer(item)
- for infos in self.GraphicsAxes:
- if item in infos["items"]:
- if len(infos["items"]) == 1:
- self.GraphicsAxes.remove(infos)
- infos["items"].remove(item)
- self.Table.RemoveItem(row)
- setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
- def _MoveVariable(row, move):
- new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
- self.Table.MoveItem(row, new_row)
- setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
- self.VariablesGrid.SetRowLabelSize(0)
- self.GridColSizes = [200, 100]
- for col in range(self.Table.GetNumberCols()):
- attr = wx.grid.GridCellAttr()
- attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
- self.VariablesGrid.SetColAttr(col, attr)
- self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
- self.Table.ResetView(self.VariablesGrid)
- self.VariablesGrid.RefreshButtons()
- self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
+ main_sizer = wx.BoxSizer(wx.VERTICAL) - self.GraphicsPanelSizer = wx.BoxSizer(wx.VERTICAL)
+ self.Ticks = numpy.array([]) + self.RangeValues = None + self.GraphicPanels = [] graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.GraphicsPanelSizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
+ main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) - range_label = wx.StaticText(self.GraphicsPanel, label=_('Range:'))
+ range_label = wx.StaticText(self, label=_('Range:')) graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
- self.CanvasRange = wx.ComboBox(self.GraphicsPanel, style=wx.CB_READONLY)
+ 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)
@@ -434,7 +773,7 @@
("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,
+ button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
size=wx.Size(28, 28), style=wx.NO_BORDER)
button.SetToolTipString(help)
@@ -442,7 +781,7 @@
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,
+ 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)
@@ -454,211 +793,185 @@
self.OnPositionChanging, self.CanvasPosition)
self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN,
self.OnPositionChanging, self.CanvasPosition)
- self.GraphicsPanelSizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
+ main_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)
- self.GraphicsPanelSizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
+ self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) + self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) + self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) + main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) - graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) + self.GraphicsWindow.SetSizer(self.GraphicsSizer) + self.RefreshCanvasRange() - self.GraphicsFigure = matplotlib.figure.Figure()
+ main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) + main_sizer.AddGrowableCol(0) + main_sizer.AddGrowableRow(1) + button_sizer = wx.BoxSizer(wx.HORIZONTAL) + main_sizer.AddSizer(button_sizer, border=5, + flag=wx.ALIGN_RIGHT|wx.ALL) - self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure)
- self.GraphicsCanvas.mpl_connect("button_press_event", self.OnGraphicsCanvasClick)
- self.GraphicsCanvas.SetDropTarget(DebugVariableDropTarget(self, self.GraphicsCanvas))
- graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW)
- self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer)
- self.Graphics3DFigure = matplotlib.figure.Figure()
- self.Graphics3DFigure.subplotpars.update(left=0.0, right=1.0, bottom=0.0, top=1.0)
+ for name, bitmap, help in [ + ("DeleteButton", "remove_element", _("Remove debug variable")), + ("UpButton", "up", _("Move debug variable up")), + ("DownButton", "down", _("Move debug variable down"))]: + 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) + button_sizer.AddWindow(button, border=5, flag=wx.LEFT) - self.LastMotionTime = gettime()
- self.Graphics3DAxes = self.Graphics3DFigure.gca(projection='3d')
- self.Graphics3DAxes.set_color_cycle(['b'])
- setattr(self.Graphics3DAxes, "_on_move", self.OnGraphics3DMotion)
- self.Graphics3DCanvas = FigureCanvas(self.GraphicsPanel, -1, self.Graphics3DFigure)
- self.Graphics3DCanvas.SetMinSize(wx.Size(1, 1))
- self.GraphicsPanelSizer.AddWindow(self.Graphics3DCanvas, 1, flag=wx.GROW)
- self.Graphics3DAxes.mouse_init()
+ self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL) + self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid)) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, + self.OnVariablesGridCellRightClick) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, + self.OnVariablesGridCellLeftClick) + main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW) + self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames()) + self.VariablesGrid.SetTable(self.Table) + self.VariablesGrid.SetButtons({"Delete": self.DeleteButton, + "Down": self.DownButton}) + def _AddVariable(new_row): + return self.VariablesGrid.GetGridCursorRow() + setattr(self.VariablesGrid, "_AddRow", _AddVariable) + def _DeleteVariable(row): + item = self.Table.GetItem(row) + self.RemoveDataConsumer(item) + self.Table.RemoveItem(row) + setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) - self.GraphicsPanel.SetSizer(self.GraphicsPanelSizer)
+ def _MoveVariable(row, move): + new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1)) + self.Table.MoveItem(row, new_row) + setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) + self.VariablesGrid.SetRowLabelSize(0) - self.SplitHorizontally(self.MainPanel, self.GraphicsPanel, -200)
+ self.GridColSizes = [200, 100] + for col in range(self.Table.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER) + self.VariablesGrid.SetColAttr(col, attr) + self.VariablesGrid.SetColSize(col, self.GridColSizes[col]) + self.Table.ResetView(self.VariablesGrid) + self.VariablesGrid.RefreshButtons()
- self.Initialize(self.MainPanel)
- self.RefreshCanvasRange()
- self.RefreshScrollBar()
+ self.SetSizer(main_sizer) def SetDataProducer(self, producer):
DebugViewer.SetDataProducer(self, producer)
- if self.DataProducer is not None:
- self.Ticktime = self.DataProducer.GetTicktime()
- self.RefreshCanvasRange()
+ if self.DataProducer is not None: + self.Ticktime = self.DataProducer.GetTicktime() + self.RefreshCanvasRange() def RefreshNewData(self, *args, **kwargs):
if self.HasNewData or self.Force:
- self.RefreshGrid(only_values=True)
+ self.RefreshView(only_values=True) DebugViewer.RefreshNewData(self, *args, **kwargs)
def NewDataAvailable(self, tick, *args, **kwargs):
+ if USE_MPL and tick is not None: 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):
- for col in xrange(self.Table.GetNumberCols()):
- if self.Table.GetColLabelValue(col, False) == "Value":
- for row in xrange(self.Table.GetNumberRows()):
- self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
- self.Table.ResetView(self.VariablesGrid)
- self.VariablesGrid.RefreshButtons()
+ def RefreshGraphicsSizer(self): + self.GraphicsSizer.Clear() - self.RefreshScrollBar()
+ for panel in self.GraphicPanels: + self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) + self.GraphicsSizer.Layout() + self.RefreshGraphicsWindowScrollbars() + def RefreshView(self, only_values=False):
+ self.RefreshCanvasPosition() if not self.Fixed or self.Force:
- start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
- for infos in self.GraphicsAxes:
- if infos["type"] == GRAPH_PARALLEL:
- min_value = max_value = None
- for idx, item in enumerate(infos["items"]):
- data = item.GetData(start_tick, end_tick)
- item_min_value, item_max_value = item.GetRange()
- min_value = item_min_value
- elif item_min_value is not None:
- min_value = min(min_value, item_min_value)
- max_value = item_max_value
- elif item_max_value is not None:
- max_value = max(max_value, item_max_value)
- if len(infos["plots"]) <= idx:
- infos["axes"].plot(data[:, 0], data[:, 1])[0])
- infos["plots"][idx].set_data(data[:, 0], data[:, 1])
- 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)
- x_min, x_max = start_tick, end_tick
- y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
- min_start_tick = reduce(max, [item.GetData()[0, 0]
- for item in infos["items"]
- if len(item.GetData()) > 0], 0)
- start_tick = max(self.StartTick, min_start_tick)
- end_tick = max(self.StartTick + self.CurrentRange, min_start_tick)
- x_data, x_min, x_max = OrthogonalData(infos["items"][0], start_tick, end_tick)
- y_data, y_min, y_max = OrthogonalData(infos["items"][1], start_tick, end_tick)
- if x_data is not None and y_data is not None:
- length = min(len(x_data), len(y_data))
- if len(infos["items"]) < 3:
- if x_data is not None and y_data is not None:
- if len(infos["plots"]) == 0:
- infos["axes"].plot(x_data[:, 1][:length],
- y_data[:, 1][:length])[0])
- infos["plots"][0].set_data(
- while len(infos["axes"].lines) > 0:
- infos["axes"].lines.pop()
- z_data, z_min, z_max = OrthogonalData(infos["items"][2], start_tick, end_tick)
- if x_data is not None and y_data is not None and z_data is not None:
- length = min(length, len(z_data))
- infos["axes"].plot(x_data[:, 1][:length],
- zs = z_data[:, 1][:length])
- infos["axes"].set_zlim(z_min, z_max)
- infos["axes"].set_xlim(x_min, x_max)
- infos["axes"].set_ylim(y_min, y_max)
- for infos in self.GraphicsAxes:
- labels = ["%s: %s" % (item.GetVariable(), item.GetValue())
- for item in infos["items"]]
- if infos["type"] == GRAPH_PARALLEL:
- infos["axes"].legend(infos["plots"], labels,
- loc="upper left", frameon=False,
+ refresh_graphics = True + refresh_graphics = False + for panel in self.GraphicPanels: + if isinstance(panel, DebugVariableGraphic): + panel.Refresh(refresh_graphics) - infos["axes"].set_xlabel(labels[0], fontdict={'size':'small'})
- infos["axes"].set_ylabel(labels[1], fontdict={'size':'small'})
- infos["axes"].set_zlabel(labels[2], fontdict={'size':'small'})
- self.GraphicsCanvas.draw()
- self.Graphics3DCanvas.draw()
+ for col in xrange(self.Table.GetNumberCols()): + if self.Table.GetColLabelValue(col, False) == "Value": + for row in xrange(self.Table.GetNumberRows()): + self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value"))) + self.Table.ResetView(self.VariablesGrid) + self.VariablesGrid.RefreshButtons() def UnregisterObsoleteData(self):
- items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
- for idx, item in items:
- iec_path = item.GetVariable().upper()
- if self.GetDataType(iec_path) is None:
- self.RemoveDataConsumer(item)
- self.Table.RemoveItem(idx)
- self.AddDataConsumer(iec_path, item)
- item.RefreshVariableType()
- self.Table.ResetView(self.VariablesGrid)
- self.VariablesGrid.RefreshButtons()
- if self.DataProducer is not None:
- self.Ticktime = self.DataProducer.GetTicktime()
- self.RefreshCanvasRange()
+ if self.DataProducer is not None: + self.Ticktime = self.DataProducer.GetTicktime() + self.RefreshCanvasRange() + for panel in self.GraphicPanels: + panel.UnregisterObsoleteData() + items = [(idx, item) for idx, item in enumerate(self.Table.GetData())] + for idx, item in items: + iec_path = item.GetVariable().upper() + if self.GetDataType(iec_path) is None: + self.RemoveDataConsumer(item) + self.Table.RemoveItem(idx) + self.AddDataConsumer(iec_path, item) + item.RefreshVariableType() + self.Table.ResetView(self.VariablesGrid) + self.VariablesGrid.RefreshButtons()
self.DeleteDataConsumers()
- self.Table.ResetView(self.VariablesGrid)
- self.VariablesGrid.RefreshButtons()
+ for panel in self.GraphicPanels: + self.GraphicPanels = [] + self.RefreshGraphicsSizer() + self.Table.ResetView(self.VariablesGrid) + self.VariablesGrid.RefreshButtons() def RefreshCanvasRange(self):
if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
@@ -668,7 +981,7 @@
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
@@ -676,9 +989,9 @@
self.CanvasRange.Append(text)
self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
- def RefreshScrollBar(self):
+ def RefreshCanvasPosition(self): pos = int(self.StartTick - self.Ticks[0])
range = int(self.Ticks[-1] - self.Ticks[0])
@@ -744,7 +1057,7 @@
self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
def OnRangeChanged(self, event):
@@ -760,9 +1073,9 @@
def OnResetButton(self, event):
- for item in self.Table.GetData():
+ for panel in self.GraphicPanels: def OnCurrentButton(self, event):
@@ -770,7 +1083,7 @@
self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
def CopyDataToClipboard(self, variables):
@@ -806,142 +1119,183 @@
wx.CallAfter(self.NewDataAvailable, None, True)
+ return self.StartTick, self.StartTick + self.CurrentRange + def GetViewerIndex(self, viewer): + if viewer in self.GraphicPanels: + return self.GraphicPanels.index(viewer) def InsertValue(self, iec_path, idx = None, force=False):
- idx = self.Table.GetNumberRows()
- for item in self.Table.GetData():
- if iec_path == item.GetVariable():
+ for panel in self.GraphicPanels: + if panel.GetItem(iec_path) is not None: + idx = len(self.GraphicPanels) + for item in self.Table.GetData(): + if iec_path == item.GetVariable(): + idx = self.Table.GetNumberRows() item = VariableTableItem(self, iec_path)
result = self.AddDataConsumer(iec_path.upper(), item)
if result is not None or force:
- self.Table.InsertItem(idx, item)
- if item.IsNumVariable():
- self.GraphicsAxes.append({
- "type": GRAPH_PARALLEL,
+ if item.IsNumVariable(): + panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) + panel = DebugVariableText(self.GraphicsWindow, self, [item]) + self.GraphicPanels.insert(idx, panel) + self.GraphicPanels.append(panel) + self.RefreshGraphicsSizer() + self.Table.InsertItem(idx, item) - def MergeGraphs(self, source, target_infos, merge_type, force=False):
+ def SplitGraphs(self, source_panel, item=None): + source_idx = self.GetViewerIndex(source_panel) + if source_idx is not None: + source_items = source_panel.GetItems() + while len(source_items) > 1: + item = source_items.pop(-1) + if item.IsNumVariable(): + panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) + panel = DebugVariableText(self.GraphicsWindow, self, [item]) + self.GraphicPanels.insert(source_idx + 1, panel) + if isinstance(source_panel, DebugVariableGraphic): + source_panel.GraphType = GRAPH_PARALLEL + source_panel.ResetGraphics() + source_panel.RemoveItem(item) + if item.IsNumVariable(): + panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) + panel = DebugVariableText(self.GraphicsWindow, self, [item]) + self.GraphicPanels.insert(source_idx + 1, panel) + self.RefreshGraphicsSizer() + def MergeGraphs(self, source, target_idx, merge_type, force=False): - for item in self.Table.GetData():
- if item.GetVariable() == source:
+ for panel in self.GraphicPanels: + source_item = panel.GetItem(source) + if source_item is not None: item = VariableTableItem(self, source)
result = self.AddDataConsumer(source.upper(), item)
if result is not None or force:
- self.Table.InsertItem(self.Table.GetNumberRows(), item)
if source_item is not None:
- for infos in self.GraphicsAxes:
- if source_item in infos["items"]:
- if target_infos is None and source_infos is None:
- self.GraphicsAxes.append({
- "items": [source_item],
- "type": GRAPH_PARALLEL,
- elif target_infos is not None and target_infos != source_infos:
- if (merge_type == GRAPH_PARALLEL and target_infos["type"] != merge_type or
+ 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
- (target_infos["type"] == GRAPH_PARALLEL and len(target_infos["items"]) > 1 or
- target_infos["type"] == GRAPH_ORTHOGONAL and len(target_infos["items"]) >= 3)):
- print "Graphs not compatible"
+ (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or + graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)): - if source_infos is not None:
- source_infos["items"].remove(source_item)
- if len(source_infos["items"]) == 0:
- self.GraphicsAxes.remove(source_infos)
+ if source_panel is not None: + source_panel.RemoveItem(source_item) + if source_panel.IsEmpty(): + if source_panel.Canvas.HasCapture(): + source_panel.Canvas.ReleaseMouse() + self.GraphicPanels.remove(source_panel) + target_panel.AddItem(source_item) + target_panel.GraphType = merge_type + target_panel.ResetGraphics() - target_infos["items"].append(source_item)
- target_infos["type"] = merge_type
- print "No modification to do"
+ self.RefreshGraphicsSizer() + def DeleteValue(self, source_panel, item=None): + source_idx = self.GetViewerIndex(source_panel) + if source_idx is not None: + self.GraphicPanels.remove(source_panel) + self.RefreshGraphicsSizer() + source_panel.RemoveItem(item) + if source_panel.IsEmpty(): + self.GraphicPanels.remove(source_panel) + self.RefreshGraphicsSizer() def GetDebugVariables(self):
- return [item.GetVariable() for item in self.Table.GetData()]
+ return [panel.GetVariables() for panel in self.GraphicPanels] + return [item.GetVariable() for item in self.Table.GetData()] - def OnGraphicsCanvasClick(self, event):
- for infos in self.GraphicsAxes:
- if infos["axes"] == event.inaxes:
- if len(infos["items"]) == 1:
- data = wx.TextDataObject(str((infos["items"][0].GetVariable(), "debug")))
- dragSource = wx.DropSource(self)
- dragSource.SetData(data)
- dragSource.DoDragDrop()
- if self.GraphicsCanvas.HasCapture():
- self.GraphicsCanvas.ReleaseMouse()
+ def SetDebugVariables(self, variables): + for variable in variables: + if isinstance(variable, (TupleType, ListType)): + for iec_path in variable: + item = VariableTableItem(self, iec_path) + if not item.IsNumVariable(): + self.AddDataConsumer(iec_path.upper(), item) + if isinstance(variable, ListType): + panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_PARALLEL) + elif isinstance(variable, TupleType) and len(items) <= 3: + panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_ORTHOGONAL) + self.GraphicPanels.append(panel) + self.RefreshGraphicsSizer() + self.InsertValue(variable, force=True) + for variable in variables: + if isinstance(variable, (ListType, TupleType)): + for iec_path in variable: + self.InsertValue(iec_path, force=True) + self.InsertValue(variable, force=True) def ResetGraphicsValues(self):
- self.Ticks = numpy.array([])
- for item in self.Table.GetData():
- def ResetGraphics(self):
- self.GraphicsFigure.clear()
- for infos in self.GraphicsAxes:
- if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
- if axes_num == len(self.GraphicsAxes):
- self.Graphics3DCanvas.Hide()
- self.Graphics3DCanvas.Show()
- self.GraphicsPanelSizer.Layout()
- for infos in self.GraphicsAxes:
- if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
- axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx)
- infos["axes"] = self.Graphics3DAxes
- self.RefreshGraphicsCanvasWindowScrollbars()
- self.GraphicsCanvas.draw()
- def OnGraphics3DMotion(self, event):
- current_time = gettime()
- 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()
- for infos in self.GraphicsAxes:
- if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
- vwidth, vheight = (window_size[0], (axes_num + 1) * 80)
- self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
+ self.Ticks = numpy.array([]) + for panel in self.GraphicPanels: + 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.GraphicsCanvasWindow.Scroll(posx, posy)
- self.GraphicsCanvasWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
+ self.GraphicsWindow.Scroll(posx, posy) + self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
- def OnGraphicsCanvasWindowResize(self, event):
- self.RefreshGraphicsCanvasWindowScrollbars()
+ def OnGraphicsWindowResize(self, event): + self.RefreshGraphicsWindowScrollbars() \ No newline at end of file
--- a/images/plcopen_icons.svg Tue Jan 29 18:01:51 2013 +0100
+++ b/images/plcopen_icons.svg Tue Jan 29 23:41:00 2013 +0100
@@ -14,13 +14,13 @@
- inkscape:version="0.48.2 r9819"
+ inkscape:version="0.48.3.1 r9886" inkscape:export-filename="/taf/Pim/workspace_laurent/plcopeneditor/Images/SFC.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
- sodipodi:docname="icons.svg"
+ sodipodi:docname="plcopen_icons.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape">
@@ -5872,7 +5872,7 @@
inkscape:vp_x="0 : 495 : 1"
sodipodi:type="inkscape:persp3d" />
- gradientTransform="matrix(0.2149522,0,0,0.2369714,30.871779,266.01932)"
+ gradientTransform="matrix(0.2149522,0,0,0.23697141,30.871774,266.01933)" gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient3989"
@@ -5881,7 +5881,7 @@
- gradientTransform="matrix(0.3505596,0,0,0.3701598,22.984703,226.27699)"
+ gradientTransform="matrix(0.35055961,0,0,0.37015982,22.984703,226.27699)" gradientUnits="userSpaceOnUse"
xlink:href="#linearGradient2190"
@@ -9143,6 +9143,897 @@
id="linearGradient3099-5"
xlink:href="#linearGradient6388"
inkscape:collect="always" />
+ gradientTransform="matrix(0.35055961,0,0,0.37015982,1.605601,315.86283)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient2190-4" + id="linearGradient9474-8" + id="linearGradient2190-4"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + gradientTransform="matrix(0.08119916,0,0,0.0857391,-8.6386314,-24.685007)" + gradientUnits="userSpaceOnUse" + id="linearGradient24816" + xlink:href="#linearGradient2190-4" + inkscape:collect="always" /> + gradientTransform="matrix(0.35055961,0,0,0.37015982,4.7663433,300.94733)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient2190-10" + id="linearGradient9474-5" + id="linearGradient2190-10"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + gradientTransform="matrix(0.08119916,0,0,0.0857391,-8.6386314,-24.685007)" + gradientUnits="userSpaceOnUse" + id="linearGradient24816-7" + xlink:href="#linearGradient2190-10" + inkscape:collect="always" /> + gradientTransform="matrix(0.35055961,0,0,0.37015982,24.809797,319.63945)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient2190-8" + id="linearGradient9474-2" + id="linearGradient2190-8"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + gradientTransform="matrix(0.08119916,0,0,0.0857391,-8.6386314,-24.685007)" + gradientUnits="userSpaceOnUse" + id="linearGradient24816-0" + xlink:href="#linearGradient2190-8" + inkscape:collect="always" /> + inkscape:collect="always" + xlink:href="#linearGradient2190-10" + id="linearGradient24871" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,4.7663433,300.94733)" + inkscape:collect="always" + xlink:href="#linearGradient2190-4" + id="linearGradient24873" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,1.605601,315.86283)" + inkscape:collect="always" + xlink:href="#linearGradient2190-8" + id="linearGradient24875" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,24.809797,319.63945)" + id="linearGradient2190-10-0"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + id="linearGradient2190-4-6"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + inkscape:collect="always" + xlink:href="#linearGradient2190-8-4" + id="linearGradient24875-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,24.809797,319.63945)" + id="linearGradient2190-8-4"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + inkscape:collect="always" + xlink:href="#linearGradient2190-10-7" + id="linearGradient24871-3" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,4.7663433,300.94733)" + id="linearGradient2190-10-7"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + inkscape:collect="always" + xlink:href="#linearGradient2190-4-8" + id="linearGradient24873-3" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.35055961,0,0,0.37015982,1.605601,315.86283)" + id="linearGradient2190-4-8"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + id="linearGradient2190-8-46"> + style="stop-color:#acbbff;stop-opacity:1" + style="stop-color:#acbbff;stop-opacity:0" + gradientTransform="matrix(0.35055961,0,0,0.37015982,24.809797,319.63945)" + gradientUnits="userSpaceOnUse" + id="linearGradient24903-0" + xlink:href="#linearGradient2190-8-46" + inkscape:collect="always" /> + gradientTransform="matrix(0.2149522,0,0,0.23697141,116.82519,271.27723)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient3989-3" + id="linearGradient9477-6" + id="linearGradient3989-3"> + style="stop-color:#000000;stop-opacity:1" + style="stop-color:#000000;stop-opacity:0" + gradientTransform="matrix(0.04978878,0,0,0.05488903,-7.7565112,-16.251556)" + gradientUnits="userSpaceOnUse" + id="linearGradient25064" + xlink:href="#linearGradient3989-3" + inkscape:collect="always" /> + gradientTransform="matrix(0.2149522,0,0,0.23697141,116.82519,271.27723)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient3989-6" + id="linearGradient9477-8" + id="linearGradient3989-6"> + style="stop-color:#000000;stop-opacity:1" + style="stop-color:#000000;stop-opacity:0" + gradientTransform="matrix(0.04978878,0,0,0.05488903,-7.7565112,-16.251556)" + gradientUnits="userSpaceOnUse" + id="linearGradient25064-4" + xlink:href="#linearGradient3989-6" + inkscape:collect="always" /> + gradientTransform="matrix(0.2149522,0,0,0.23697141,116.82519,271.27723)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient3989-2" + id="linearGradient9477-5" + id="linearGradient3989-2"> + style="stop-color:#000000;stop-opacity:1" + style="stop-color:#000000;stop-opacity:0" + gradientTransform="matrix(0.04978878,0,0,0.05488903,-7.7565112,-16.251556)" + gradientUnits="userSpaceOnUse" + id="linearGradient25064-6" + xlink:href="#linearGradient3989-2" + inkscape:collect="always" /> + id="radialGradient2670" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.50982,0,0,0.10274,-0.46743,17.132)" + id="linearGradient2667" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4858,0,0,0.56676,0.34231,-3.7558)" + style="stop-color:#fee7b1" + style="stop-color:#ebd4b4" + style="stop-color:#c8a775" + style="stop-color:#b0935b" + style="stop-color:#fcebbf" + id="linearGradient2654" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.45413,0,0,0.50788,1.1076,-0.99312)" + style="stop-color:#fff" + style="stop-color:#fff;stop-opacity:.49485" + id="linearGradient2651" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.47059,0,0,0.80748,0.70588,-5.1887)" + style="stop-color:#d6c8a7" + style="stop-color:#d0bd99" + id="linearGradient2648" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.45455,0,0,0.25159,1.0909,6.0971)" + style="stop-color:#fff" + style="stop-color:#fff;stop-opacity:0" + color-interpolation-filters="sRGB" + id="feGaussianBlur5959" + stdDeviation="0.69878785" /> + id="radialGradient3446" + xlink:href="#linearGradient6075" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.75472,0,0,0.37736,-4.5283,1.5849)" + id="linearGradient6075"> + id="radialGradient3444" + xlink:href="#linearGradient6075" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.75472,0,0,0.37736,4.4717,1.5849)" + id="linearGradient3066"> + id="linearGradient2638" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.40937,0,0,0.47152,2.1465,-1.81)" + style="stop-color:#cad0c6" + style="stop-color:#eaece9" + style="stop-color:#c5cbc0" + id="linearGradient2623" + xlink:href="#linearGradient6075" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(1.6824,1.125)" + id="linearGradient3077"> + color-interpolation-filters="sRGB" + id="feGaussianBlur6253" + stdDeviation="0.24444548" /> + id="linearGradient2625" + xlink:href="#linearGradient6075" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-19.533,1.7437)" + id="linearGradient3086"> + color-interpolation-filters="sRGB" + id="feGaussianBlur3094" + stdDeviation="0.24444548" /> + color-interpolation-filters="sRGB"> + id="feGaussianBlur5747" + stdDeviation="0.8362597" /> + id="radialGradient2670-3" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.50982,0,0,0.10274,-0.46743,17.132)" + id="linearGradient2667-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4858,0,0,0.56676,0.34231,-3.7558)" + style="stop-color:#fee7b1" + style="stop-color:#ebd4b4" + style="stop-color:#c8a775" + style="stop-color:#b0935b" + style="stop-color:#fcebbf" + id="linearGradient2654-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.45413,0,0,0.50788,1.1076,-0.99312)" + style="stop-color:#fff" + style="stop-color:#fff;stop-opacity:.49485" + id="linearGradient2651-2" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.47059,0,0,0.80748,0.70588,-5.1887)" + style="stop-color:#d6c8a7" + style="stop-color:#d0bd99" + id="linearGradient2648-2" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.45455,0,0,0.25159,1.0909,6.0971)" + style="stop-color:#fff" + style="stop-color:#fff;stop-opacity:0" + color-interpolation-filters="sRGB" + id="feGaussianBlur5959-6" + stdDeviation="0.69878785" /> + id="radialGradient3446-5" + xlink:href="#linearGradient6075-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.75472,0,0,0.37736,-4.5283,1.5849)" + id="linearGradient6075-0"> + id="radialGradient3444-4" + xlink:href="#linearGradient6075-0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.75472,0,0,0.37736,4.4717,1.5849)" + id="linearGradient5667"> + id="linearGradient2638-4" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.40937,0,0,0.47152,10.941992,-1.81)" + style="stop-color:#cad0c6" + style="stop-color:#eaece9" + style="stop-color:#c5cbc0" + id="linearGradient2623-6" + xlink:href="#linearGradient6075-0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(1.6824,1.125)" + id="linearGradient5678"> + color-interpolation-filters="sRGB" + id="feGaussianBlur6253-6" + stdDeviation="0.24444548" /> + id="linearGradient2625-2" + xlink:href="#linearGradient6075-0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-19.533,1.7437)" + id="linearGradient5687"> + color-interpolation-filters="sRGB" + id="feGaussianBlur5695" + stdDeviation="0.24444548" /> + color-interpolation-filters="sRGB"> + id="feGaussianBlur5747-4" + stdDeviation="0.8362597" /> + gradientTransform="matrix(-1.6199036,0.64359508,0.87746896,1.8299675,548.24021,-170.70599)" + gradientUnits="userSpaceOnUse" + xlink:href="#XMLID_897_-8" + id="linearGradient6541-3" + gradientUnits="userSpaceOnUse" + style="stop-color:#e8e8e5;stop-opacity:1" + style="stop-color:#f0f0f0;stop-opacity:1" + gradientTransform="matrix(0.30002427,-0.15885894,0.1590255,0.44281237,66.323263,29.078644)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient2229-2" + id="linearGradient6538-5" + id="linearGradient2229-2"> + style="stop-color:#e2e2e2;stop-opacity:1" + style="stop-color:#d8d8d8;stop-opacity:1" + gradientTransform="matrix(1.3432519,-0.79619135,0.98307194,1.7344871,-329.42724,256.04227)" + gradientUnits="userSpaceOnUse" + xlink:href="#XMLID_897_-8" + id="linearGradient6534-2" + gradientUnits="userSpaceOnUse" + id="linearGradient5893" + style="stop-color:#e8e8e5;stop-opacity:1" + style="stop-color:#f0f0f0;stop-opacity:1" + gradientTransform="matrix(0.32429489,-0.1897767,0.23187135,0.40393918,63.404032,30.47844)" + gradientUnits="userSpaceOnUse" + xlink:href="#linearGradient2229-2" + id="linearGradient6531-0" + id="linearGradient5900"> + style="stop-color:#e2e2e2;stop-opacity:1" + style="stop-color:#d8d8d8;stop-opacity:1" + gradientTransform="matrix(0.32937464,-0.18440234,0.2272317,0.42503946,63.110644,29.8148)" + gradientUnits="userSpaceOnUse" + xlink:href="#XMLID_45_-4" + id="linearGradient6543-2" + gradientUnits="userSpaceOnUse" + style="stop-color:#babdb6;stop-opacity:1" + style="stop-color:#eeeeec;stop-opacity:1" @@ -9151,9 +10042,9 @@
- inkscape:zoom="7.9999996"
- inkscape:cx="-9.9597451"
- inkscape:cy="-165.16747"
+ inkscape:zoom="2.828427" + inkscape:cx="-47.411844" + inkscape:cy="-252.32358" inkscape:document-units="px"
inkscape:current-layer="layer1"
@@ -9193,6 +10084,13 @@
+ style="opacity:0.53909463;color:#000000;fill:#c8c8c8;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.46720701;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.40162106, 1.40162106;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" inkscape:label="#rect3636"
@@ -12782,21 +13680,21 @@
- transform="matrix(0.23162724,0,0,0.23162724,-154.66454,145.99446)">
+ transform="matrix(0.23162724,0,0,0.23162724,-155.29777,145.99446)"> style="opacity:0.74621211;fill:url(#linearGradient9477);fill-opacity:1;fill-rule:evenodd;stroke:none"
- transform="matrix(0.9981479,-0.0608342,0.1048727,0.9944857,0,0)"
+ transform="matrix(0.99814788,-0.0608342,0.1048727,0.99448565,0,0)"
style="fill:url(#linearGradient9474);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.31597084;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- transform="matrix(0.9981479,-0.0608342,0.1048727,0.9944857,0,0)"
+ transform="matrix(0.99814788,-0.0608342,0.1048727,0.99448565,0,0)"
@@ -13640,5 +14538,302 @@
style="fill:url(#linearGradient3099-5)"
d="m 15,20.243 1.4142,-1.4142 1.4142,1.4142 1.4142,-1.4142 -1.414,-1.415 1.415,-1.414 h -4.243 v 4.2426 z" />
+ inkscape:label="#rect3636" + style="opacity:0;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="font-size:4.49727678px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans" + id="text3638-3-3-2-0-9-84"><tspan + id="tspan3640-1-8-0-6-8-0" + y="215.125">%%force%%</tspan></text> + transform="translate(-90.439343,218.49737)" + inkscape:connector-curvature="0" + style="opacity:0.4;fill:url(#radialGradient2670)" + d="m 22,21.947 c 0,1.113 -4.4771,2.0152 -9.9999,2.0152 -5.5228,0 -9.9999,-0.90224 -9.9999,-2.0152 0,-1.11296 4.4771,-2.0152 9.9999,-2.0152 5.5228,0 9.9999,0.90224 9.9999,2.0152 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2667);fill-rule:evenodd;stroke:#a2824e;stroke-linecap:round;stroke-linejoin:round" + d="m 4.5857,8.0018 c 4.8977,-0.72404 9.8475,-0.61163 14.828,0 0.60151,0 1.0857,0.52347 1.0857,1.1737 v 11.698 c 0,0.65024 -0.5155,1.0797 -1.0857,1.1737 -5.164,0.60626 -9.5919,0.60147 -14.828,0 -0.6955,-0.22 -1.086,-0.524 -1.086,-1.175 V 9.1742 c 0,-0.65024 0.48424,-1.1737 1.0857,-1.1737 z" /> + inkscape:connector-curvature="0" + style="opacity:0.38000039;fill:none;stroke:url(#linearGradient2654);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.60109002" + d="m 5.0857,9.5 h 13.829 c 0.32446,0 0.58567,0.26635 0.58567,0.5972 v 10.396 c 0,0.33085 -0.23192,0.48359 -0.58567,0.5972 -4.5217,0.54696 -8.9848,0.5457 -13.829,0 C 4.76124,20.97679 4.50003,20.82405 4.50003,20.4932 V 10.0972 C 4.50003,9.76635 4.76124,9.5 5.0857,9.5 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2651);fill-rule:evenodd" + d="m 4.7794,8.5385 c 4.8137,-0.78635 9.6274,-0.64629 14.441,0 0.4318,0 0.77943,0.386 0.77943,0.86548 v 1.499 c 0,0.47947 -0.34763,0.86548 -0.77943,0.86548 -5.0234,0.3715 -9.6454,0.23891 -14.441,0 -0.4314,0 -0.779,-0.386 -0.779,-0.865 v -1.499 c 0,-0.4795 0.3476,-0.8655 0.7794,-0.8655 z" /> + inkscape:connector-curvature="0" + style="opacity:0.6;fill:none;stroke:url(#linearGradient2648);stroke-linecap:round;stroke-linejoin:round" + d="m 5.0455,10.5 h 13.909 c 0.30223,0 0.54554,0.1293 0.54554,0.2899 v 0.17499 c 0,0.1606 -0.24331,0.2899 -0.54554,0.2899 -4.593,0.31718 -9.238,0.33659 -13.909,0 -0.30223,0 -0.54554,-0.1293 -0.54554,-0.2899 V 10.7899 C 4.49996,10.6293 4.74327,10.5 5.0455,10.5 z" /> + style="opacity:0.2;fill:#ffffff;fill-rule:evenodd;filter:url(#filter5957)" + transform="matrix(0.9234,0,0,0.57774,-7.227,-2.3565)" + inkscape:connector-curvature="0" + style="opacity:0.3;fill:url(#radialGradient3446);fill-rule:evenodd" + d="M 10,9.25 C 10,9.9404 8.8807,10.5 7.5,10.5 6.1193,10.5 5,9.9404 5,9.25 5,8.5596 6.1193,8 7.5,8 8.8807,8 10,8.5596 10,9.25 z" /> + inkscape:connector-curvature="0" + style="opacity:0.3;fill:url(#radialGradient3444);fill-rule:evenodd" + d="M 19,9.25 C 19,9.9404 17.881,10.5 16.5,10.5 15.119,10.5 14,9.9404 14,9.25 14,8.5596 15.119,8 16.5,8 17.881,8 19,8.5596 19,9.25 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2638);fill-rule:evenodd;stroke:#888a85" + d="M 6.5022,8.6395 V 5.9354 c 0,-3.711 2.1522,-5.4739 5.4773,-5.4276 3.3431,0.046344 5.5183,1.673 5.5183,5.4276 v 2.822 c 0,0.92147 -2.2477,1.0387 -2.2477,0 v -1.879 c 0,-0.94305 0.2327,-3.9947 -3.2471,-3.9947 -3.4511,0 -3.1997,3.0704 -3.1863,3.9909 v 1.7807 c 0,1.1103 -2.3144,1.1055 -2.3144,-0.01588 z" /> + inkscape:connector-curvature="0" + style="opacity:0.18235001;fill:url(#linearGradient2623);fill-rule:evenodd;filter:url(#filter6251)" + d="m 34.687,10.837 1.2639,0.125 c 0.92724,2.8227 0.73605,9.5104 0.73605,9.5104 -0.0625,1.125 -2.0312,0.53125 -2,0 V 10.837 z" + transform="matrix(0.40937,0,0,0.47152,2.1465,-0.90174)" /> + inkscape:connector-curvature="0" + style="opacity:0.14118;fill:url(#linearGradient2625);fill-rule:evenodd;filter:url(#filter6251)" + d="m 12.927,11.544 0.37172,0.1692 c 1.7203,1.055 2.1735,9.3778 2.1735,9.3778 -0.0625,1.125 -2.0312,0.53125 -2,0 0,0 0.37822,-6.8706 -0.54525,-9.547 z" + transform="matrix(-0.40937,0,0,0.47152,14.042,-1.1935)" /> + inkscape:connector-curvature="0" + style="opacity:0.62352995;fill:none;stroke:#ffffff;stroke-width:4.29829979;stroke-linecap:round;filter:url(#filter5745)" + d="m 13.877,17.722 0.125,-7.5 c 0,-9.8764 18.688,-10.676 18.688,0.875 v 6.875" + transform="matrix(0.45915,0,0,0.47152,0.97831,0.22752)" /> + style="font-size:4.49727678px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans" + id="text3638-3-3-2-0-9-84-1"><tspan + id="tspan3640-1-8-0-6-8-0-5" + y="215">%%release%%</tspan></text> + inkscape:label="#rect3636" + style="opacity:0;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="translate(-58.267759,218.481)" + inkscape:connector-curvature="0" + style="opacity:0.4;fill:url(#radialGradient2670-3)" + d="m 22,21.947 c 0,1.113 -4.4771,2.0152 -9.9999,2.0152 -5.5228,0 -9.9999,-0.90224 -9.9999,-2.0152 0,-1.11296 4.4771,-2.0152 9.9999,-2.0152 5.5228,0 9.9999,0.90224 9.9999,2.0152 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2667-8);fill-rule:evenodd;stroke:#a2824e;stroke-linecap:round;stroke-linejoin:round" + d="m 4.5857,8.0018 c 4.8977,-0.72404 9.8475,-0.61163 14.828,0 0.60151,0 1.0857,0.52347 1.0857,1.1737 v 11.698 c 0,0.65024 -0.5155,1.0797 -1.0857,1.1737 -5.164,0.60626 -9.5919,0.60147 -14.828,0 -0.6955,-0.22 -1.086,-0.524 -1.086,-1.175 V 9.1742 c 0,-0.65024 0.48424,-1.1737 1.0857,-1.1737 z" /> + inkscape:connector-curvature="0" + style="opacity:0.38000039;fill:none;stroke:url(#linearGradient2654-8);stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.60109002" + d="m 5.0857,9.5 h 13.829 c 0.32446,0 0.58567,0.26635 0.58567,0.5972 v 10.396 c 0,0.33085 -0.23192,0.48359 -0.58567,0.5972 -4.5217,0.54696 -8.9848,0.5457 -13.829,0 C 4.76124,20.97679 4.50003,20.82405 4.50003,20.4932 V 10.0972 C 4.50003,9.76635 4.76124,9.5 5.0857,9.5 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2651-2);fill-rule:evenodd" + d="m 4.7794,8.5385 c 4.8137,-0.78635 9.6274,-0.64629 14.441,0 0.4318,0 0.77943,0.386 0.77943,0.86548 v 1.499 c 0,0.47947 -0.34763,0.86548 -0.77943,0.86548 -5.0234,0.3715 -9.6454,0.23891 -14.441,0 -0.4314,0 -0.779,-0.386 -0.779,-0.865 v -1.499 c 0,-0.4795 0.3476,-0.8655 0.7794,-0.8655 z" /> + inkscape:connector-curvature="0" + style="opacity:0.6;fill:none;stroke:url(#linearGradient2648-2);stroke-linecap:round;stroke-linejoin:round" + d="m 5.0455,10.5 h 13.909 c 0.30223,0 0.54554,0.1293 0.54554,0.2899 v 0.17499 c 0,0.1606 -0.24331,0.2899 -0.54554,0.2899 -4.593,0.31718 -9.238,0.33659 -13.909,0 -0.30223,0 -0.54554,-0.1293 -0.54554,-0.2899 V 10.7899 C 4.49996,10.6293 4.74327,10.5 5.0455,10.5 z" /> + style="opacity:0.2;fill:#ffffff;fill-rule:evenodd;filter:url(#filter5957-1)" + transform="matrix(0.9234,0,0,0.57774,-7.227,-2.3565)" + inkscape:connector-curvature="0" + style="opacity:0.3;fill:url(#radialGradient3446-5);fill-rule:evenodd" + d="M 10,9.25 C 10,9.9404 8.8807,10.5 7.5,10.5 6.1193,10.5 5,9.9404 5,9.25 5,8.5596 6.1193,8 7.5,8 8.8807,8 10,8.5596 10,9.25 z" /> + inkscape:connector-curvature="0" + style="opacity:0.3;fill:url(#radialGradient3444-4);fill-rule:evenodd" + d="M 19,9.25 C 19,9.9404 17.881,10.5 16.5,10.5 15.119,10.5 14,9.9404 14,9.25 14,8.5596 15.119,8 16.5,8 17.881,8 19,8.5596 19,9.25 z" /> + inkscape:connector-curvature="0" + style="fill:url(#linearGradient2638-4);fill-rule:evenodd;stroke:#888a85" + d="M 15.297692,8.6395 V 5.9354 c 0,-3.711 2.1522,-5.4739 5.4773,-5.4276 3.3431,0.046344 5.5183,1.673 5.5183,5.4276 v 2.822 c 0,0.92147 -2.2477,1.0387 -2.2477,0 v -1.879 c 0,-0.94305 0.2327,-3.9947 -3.2471,-3.9947 -3.4511,0 -3.1997,3.0704 -3.1863,3.9909 v 1.7807 c 0,1.1103 -2.3144,1.1055 -2.3144,-0.01588 z" /> + inkscape:connector-curvature="0" + style="opacity:0.18235001;fill:url(#linearGradient2623-6);fill-rule:evenodd;filter:url(#filter6251-7)" + d="m 34.687,10.837 1.2639,0.125 c 0.92724,2.8227 0.73605,9.5104 0.73605,9.5104 -0.0625,1.125 -2.0312,0.53125 -2,0 V 10.837 z" + transform="matrix(0.40937,0,0,0.47152,10.941992,-0.90174)" /> + inkscape:connector-curvature="0" + style="opacity:0.14118;fill:url(#linearGradient2625-2);fill-rule:evenodd;filter:url(#filter6251-7)" + d="m 12.927,11.544 0.37172,0.1692 c 1.7203,1.055 2.1735,9.3778 2.1735,9.3778 -0.0625,1.125 -2.0312,0.53125 -2,0 0,0 0.37822,-6.8706 -0.54525,-9.547 z" + transform="matrix(-0.40937,0,0,0.47152,22.837492,-1.1935)" /> + inkscape:connector-curvature="0" + style="opacity:0.62352995;fill:none;stroke:#ffffff;stroke-width:4.29829979;stroke-linecap:round;filter:url(#filter5745-0)" + d="m 13.877,17.722 0.125,-7.5 c 0,-9.8764 18.688,-10.676 18.688,0.875 v 6.875" + transform="matrix(0.45915,0,0,0.47152,9.7738056,0.22752)" /> + style="font-size:4.49727678px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans" + id="text3638-3-3-2-0-9-84-1-0"><tspan + id="tspan3640-1-8-0-6-8-0-5-0" + y="215.11613">%%split%%</tspan></text> + inkscape:label="#rect3636" + style="opacity:0;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + transform="scale(0.9,1)"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + sodipodi:nodetypes="ccccccc" + inkscape:connector-curvature="0" + d="m -20,226 5,-2.99448 L -10,226 -7.2318059,224.53596 -5,222.32583 -2.1213204,226 0,225" + style="fill:none;stroke:#f20000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + transform="matrix(0.01312661,-0.55960772,0.55960772,0.01312661,-15.624053,237.13593)"> + transform="matrix(0.8930733,0.44991119,-0.44991119,0.8930733,-40.088761,-46.216687)"> + style="fill:url(#linearGradient6541-3);stroke:#888a85;stroke-width:1.04343462;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 76.142125,23.986492 c 0.09772,-0.0027 2.66917,1.381244 2.682584,2.528199 -0.147983,3.419111 -0.267287,6.871404 -0.393387,10.308303 -0.261504,0.181262 -0.539755,0.333954 -0.831783,0.459341 -0.614034,0.265138 -1.302926,0.415227 -2.008258,0.41128 0.168253,-4.570471 0.333331,-9.149623 0.550844,-13.707123 z" + inkscape:connector-curvature="0" /> + style="fill:url(#linearGradient6538-5);fill-opacity:1;stroke:none" + d="m 76.69305,36.496033 c 0.02208,0.121701 0.561324,0.736373 0.847087,0.430344 0.13909,-3.350207 0.161691,-6.756579 0.29782,-10.10639 0.04116,-0.225898 -0.524997,-0.756728 -0.812014,-1.01595 0,0 -0.183607,7.068972 -0.332893,10.691996 z" + inkscape:connector-curvature="0" /> + style="fill:#d1524c;fill-opacity:1;stroke:#973137;stroke-width:0.64594996;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + transform="matrix(1.3838457,-0.5995106,0.7577226,1.5096437,-342.70934,195.72431)" + points="297.04443,12.300293 297.04443,12.300293 296.39941,13.384766 295.13281,14.71875 294.73242,13.672852 295.74658,11.960449 " /> + style="fill:url(#linearGradient6534-2);stroke:#888a85;stroke-width:0.98886794;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 67.639697,28.266191 c -0.05269,0.06962 -0.296019,2.772516 0.59766,3.468394 2.761317,1.93736 5.533849,3.915495 8.297817,5.879457 0.277594,-0.08225 0.541414,-0.193806 0.79092,-0.331734 0.525799,-0.289138 0.999978,-0.701673 1.361708,-1.218336 -3.675876,-2.611319 -7.356932,-5.230144 -11.048105,-7.797781 z" + inkscape:connector-curvature="0" /> + style="fill:url(#linearGradient6531-0);fill-opacity:1;stroke:none" + d="m 68.970807,31.046041 c 2.685267,1.87904 5.347419,3.832107 8.030235,5.708814 0.448513,-0.123082 0.941956,-0.157475 0.685348,-0.384296 -2.78652,-1.914994 -6.157886,-4.301651 -8.935065,-6.23079 -0.03991,-0.05846 0.02717,0.371107 0.219482,0.906272 z" + inkscape:connector-curvature="0" /> + style="fill:url(#linearGradient6543-2);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.31858397;marker:none;visibility:visible;display:inline;overflow:visible" + d="m 76.496347,35.488424 c 0.123486,0.230684 0.07861,0.498967 -0.100205,0.599078 -0.178816,0.100111 -0.423792,-0.0059 -0.547032,-0.236718 -0.123485,-0.230684 -0.07861,-0.498966 0.100205,-0.599078 0.178816,-0.100112 0.423792,0.0059 0.547031,0.236718 z" + inkscape:connector-curvature="0" /> + style="fill:#d1524c;fill-opacity:1;stroke:#973137;stroke-width:0.97633934;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 74.811562,39.139626 c 1.667108,-0.01317 3.313785,2.564256 3.427111,4.773436 0.11403,2.210116 -0.82558,3.652505 -2.491127,3.668822 -1.667657,0.01347 -3.110985,-1.765159 -3.224307,-3.974334 -0.113885,-2.208867 0.624172,-4.449706 2.288323,-4.467924 z m 0.0741,1.407319 c -0.739442,0.007 -0.989158,1.561088 -0.911477,3.046366 0.0762,1.486096 0.961232,2.590125 1.699529,2.582531 0.740197,-0.005 1.191745,-0.763213 1.114065,-2.248495 -0.0757,-1.48637 -1.162699,-3.387404 -1.902117,-3.380402 z" + inkscape:connector-curvature="0" /> + style="fill:#d1524c;fill-opacity:1;stroke:#973137;stroke-width:0.6332444;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + transform="matrix(1.1476031,-0.79527758,0.96691724,1.4548542,-275.37886,255.51909)" + points="296.95605,12.300293 297.6001,13.384766 298.86719,14.71875 299.26807,13.672852 298.25391,11.960449 296.95605,12.300293 " /> + style="fill:#d1524c;fill-opacity:1;stroke:#973137;stroke-width:0.99573755;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + d="m 80.776122,36.5167 c -0.795059,1.33926 0.03789,3.562041 1.862156,4.96416 1.824731,1.403245 3.948421,1.456187 4.745424,0.120053 0.79558,-1.339521 -0.03741,-3.560912 -1.861673,-4.963023 -1.823738,-1.402384 -3.947941,-1.455065 -4.745907,-0.12119 z m 1.161227,0.894731 c 0.35364,-0.593323 1.664559,-0.55346 2.890349,0.390418 1.227185,0.943163 1.749342,2.313239 1.395742,2.905303 -0.352296,0.595128 -1.665019,0.553695 -2.89081,-0.390181 -1.227653,-0.942927 -1.748909,-2.312216 -1.395281,-2.90554 z" + inkscape:connector-curvature="0" /> + style="fill:#d1524c;fill-opacity:1;stroke:none" + d="m 74.811562,39.139626 c 1.667108,-0.01317 3.313785,2.564256 3.427111,4.773436 0.11403,2.210116 -0.82558,3.652505 -2.491127,3.668822 -1.667657,0.01347 -3.110985,-1.765159 -3.224307,-3.974334 -0.113885,-2.208867 0.624172,-4.449706 2.288323,-4.467924 z m 0.0741,1.407319 c -0.739442,0.007 -0.989158,1.561088 -0.911477,3.046366 0.0762,1.486096 0.961232,2.590125 1.699529,2.582531 0.740197,-0.005 1.191745,-0.763213 1.114065,-2.248495 -0.0757,-1.48637 -1.162699,-3.387404 -1.902117,-3.380402 z" + inkscape:connector-curvature="0" /> + style="fill:#d1524c;fill-opacity:1;stroke:none" + d="m 80.776122,36.5167 c -0.795059,1.33926 0.03789,3.562041 1.862156,4.96416 1.824731,1.403245 3.948421,1.456187 4.745424,0.120053 0.79558,-1.339521 -0.03741,-3.560912 -1.861673,-4.963023 -1.823738,-1.402384 -3.947941,-1.455065 -4.745907,-0.12119 z m 1.161227,0.894731 c 0.35364,-0.593323 1.664559,-0.55346 2.890349,0.390418 1.227185,0.943163 1.749342,2.313239 1.395742,2.905303 -0.352296,0.595128 -1.665019,0.553695 -2.89081,-0.390181 -1.227653,-0.942927 -1.748909,-2.312216 -1.395281,-2.90554 z" + inkscape:connector-curvature="0" /> + transform="scale(0.9,1)"> + style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + sodipodi:nodetypes="cccccc" + inkscape:connector-curvature="0" + d="m -20,234 3.427184,4 5.08233,-2.66431 L -10,237 -5.3695924,233.56793 0,236" + style="fill:none;stroke:#ff0000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />