lbessard@125: #!/usr/bin/env python lbessard@125: # -*- coding: utf-8 -*- lbessard@125: lbessard@125: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor lbessard@125: #based on the plcopen standard. lbessard@125: # lbessard@125: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD lbessard@125: # lbessard@125: #See COPYING file for copyrights details. lbessard@125: # lbessard@125: #This library is free software; you can redistribute it and/or lbessard@125: #modify it under the terms of the GNU General Public lbessard@125: #License as published by the Free Software Foundation; either lbessard@125: #version 2.1 of the License, or (at your option) any later version. lbessard@125: # lbessard@125: #This library is distributed in the hope that it will be useful, lbessard@125: #but WITHOUT ANY WARRANTY; without even the implied warranty of lbessard@125: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lbessard@125: #General Public License for more details. lbessard@125: # lbessard@125: #You should have received a copy of the GNU General Public lbessard@125: #License along with this library; if not, write to the Free Software lbessard@125: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lbessard@125: Laurent@714: import re Laurent@714: lbessard@125: import wx lbessard@125: import wx.grid Laurent@714: import wx.lib.buttons laurent@577: lbessard@295: from plcopen.structures import IEC_KEYWORDS, TestIdentifier laurent@566: from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD laurent@604: from controls import CustomEditableListBox, CustomGrid, CustomTable, EditorPanel Laurent@714: from utils.BitmapLibrary import GetBitmap Laurent@714: Laurent@714: #------------------------------------------------------------------------------- Laurent@714: # Helpers Laurent@714: #------------------------------------------------------------------------------- lbessard@207: lbessard@207: DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") lbessard@207: lbessard@295: def AppendMenu(parent, help, id, kind, text): Laurent@714: parent.Append(help=help, id=id, kind=kind, text=text) Laurent@714: Laurent@714: def GetElementsTableColnames(): Laurent@714: _ = lambda x : x Laurent@714: return ["#", _("Name"), _("Type"), _("Initial Value")] Laurent@714: Laurent@714: def GetDatatypeTypes(): Laurent@714: _ = lambda x : x Laurent@714: return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")] Laurent@714: DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()]) lbessard@295: lbessard@295: #------------------------------------------------------------------------------- lbessard@295: # Structure Elements Table lbessard@295: #------------------------------------------------------------------------------- lbessard@295: laurent@604: class ElementsTable(CustomTable): lbessard@295: lbessard@295: """ lbessard@295: A custom wx.grid.Grid Table using user supplied data lbessard@295: """ lbessard@295: def __init__(self, parent, data, colnames): lbessard@295: # The base class must be initialized *first* laurent@604: CustomTable.__init__(self, parent, data, colnames) lbessard@295: self.old_value = None laurent@604: lbessard@295: def GetValue(self, row, col): lbessard@295: if row < self.GetNumberRows(): lbessard@295: if col == 0: lbessard@295: return row + 1 laurent@391: name = str(self.data[row].get(self.GetColLabelValue(col, False), "")) lbessard@295: return name lbessard@295: lbessard@295: def SetValue(self, row, col, value): lbessard@295: if col < len(self.colnames): laurent@391: colname = self.GetColLabelValue(col, False) lbessard@295: if colname == "Name": lbessard@295: self.old_value = self.data[row][colname] lbessard@295: self.data[row][colname] = value lbessard@295: lbessard@295: def GetOldValue(self): lbessard@295: return self.old_value lbessard@295: lbessard@295: def _updateColAttrs(self, grid): lbessard@295: """ lbessard@295: wx.grid.Grid -> update the column attributes to add the lbessard@295: appropriate renderer given the column name. lbessard@295: lbessard@295: Otherwise default to the default renderer. lbessard@295: """ lbessard@295: lbessard@295: for row in range(self.GetNumberRows()): laurent@576: row_highlights = self.Highlights.get(row, {}) lbessard@295: for col in range(self.GetNumberCols()): lbessard@295: editor = None lbessard@295: renderer = None laurent@391: colname = self.GetColLabelValue(col, False) lbessard@295: if col != 0: lbessard@295: grid.SetReadOnly(row, col, False) lbessard@295: if colname == "Name": lbessard@295: editor = wx.grid.GridCellTextEditor() lbessard@295: renderer = wx.grid.GridCellStringRenderer() lbessard@295: elif colname == "Initial Value": lbessard@295: editor = wx.grid.GridCellTextEditor() lbessard@295: renderer = wx.grid.GridCellStringRenderer() lbessard@295: elif colname == "Type": lbessard@295: editor = wx.grid.GridCellTextEditor() lbessard@295: else: lbessard@295: grid.SetReadOnly(row, col, True) lbessard@295: lbessard@295: grid.SetCellEditor(row, col, editor) lbessard@295: grid.SetCellRenderer(row, col, renderer) lbessard@295: laurent@576: highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] laurent@576: grid.SetCellBackgroundColour(row, col, highlight_colours[0]) laurent@576: grid.SetCellTextColour(row, col, highlight_colours[1]) laurent@604: self.ResizeRow(grid, row) laurent@604: laurent@576: def AddHighlight(self, infos, highlight_type): laurent@576: row_highlights = self.Highlights.setdefault(infos[0], {}) laurent@576: if infos[1] == "initial": laurent@576: col_highlights = row_highlights.setdefault("initial value", []) laurent@576: else: laurent@576: col_highlights = row_highlights.setdefault(infos[1], []) laurent@576: col_highlights.append(highlight_type) laurent@576: lbessard@125: #------------------------------------------------------------------------------- laurent@379: # Datatype Editor class lbessard@125: #------------------------------------------------------------------------------- lbessard@125: laurent@586: class DataTypeEditor(EditorPanel): laurent@586: Laurent@714: def _init_Editor(self, parent): Laurent@714: self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER) Laurent@714: lbessard@125: self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) Laurent@714: self.MainSizer.AddGrowableCol(0) Laurent@714: self.MainSizer.AddGrowableRow(1) Laurent@714: Laurent@714: top_sizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: self.MainSizer.AddSizer(top_sizer, border=5, Laurent@714: flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) Laurent@714: Laurent@714: derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:')) Laurent@714: top_sizer.AddWindow(derivation_type_label, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT) Laurent@714: Laurent@714: self.DerivationType = wx.ComboBox(self.Editor, Laurent@714: size=wx.Size(200, -1), style=wx.CB_READONLY) Laurent@714: self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType) Laurent@714: top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT) Laurent@714: Laurent@714: typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:')) Laurent@714: typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL) Laurent@714: self.MainSizer.AddSizer(typeinfos_sizer, border=5, Laurent@714: flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) Laurent@714: lbessard@125: # Panel for Directly derived data types lbessard@125: Laurent@714: self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) Laurent@714: typeinfos_sizer.AddWindow(self.DirectlyPanel, 1) Laurent@714: Laurent@714: directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: Laurent@714: directly_basetype_label = wx.StaticText(self.DirectlyPanel, Laurent@714: label=_('Base Type:')) Laurent@714: directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY) Laurent@714: self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyPanel) Laurent@714: directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: directly_initialvalue_label = wx.StaticText(self.DirectlyPanel, Laurent@714: label=_('Initial Value:')) Laurent@714: directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, Laurent@714: style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH) Laurent@714: self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue) Laurent@714: directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, Laurent@714: flag=wx.ALL) Laurent@714: Laurent@714: self.DirectlyPanel.SetSizer(directly_panel_sizer) Laurent@714: lbessard@125: # Panel for Subrange data types lbessard@125: Laurent@714: self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) Laurent@714: typeinfos_sizer.AddWindow(self.SubrangePanel, 1) Laurent@714: Laurent@714: subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0) Laurent@714: Laurent@714: subrange_basetype_label = wx.StaticText(self.SubrangePanel, Laurent@714: label=_('Base Type:')) Laurent@714: subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY) Laurent@714: self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, Laurent@714: self.SubrangeBaseType) Laurent@714: subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: subrange_initialvalue_label = wx.StaticText(self.SubrangePanel, Laurent@714: label=_('Initial Value:')) Laurent@714: subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, Laurent@714: style=wx.TAB_TRAVERSAL) Laurent@714: self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue) Laurent@714: subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:')) Laurent@714: subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) Laurent@714: self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum) Laurent@714: subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: for i in xrange(2): Laurent@714: subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1) Laurent@714: Laurent@714: subrange_maximum_label = wx.StaticText(self.SubrangePanel, Laurent@714: label=_('Maximum:')) Laurent@714: subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) Laurent@714: self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum) Laurent@714: Laurent@714: subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: self.SubrangePanel.SetSizer(subrange_panel_sizer) Laurent@714: lbessard@125: # Panel for Enumerated data types Laurent@714: Laurent@714: self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) Laurent@714: typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1) Laurent@714: Laurent@714: enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: Laurent@714: self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, Laurent@714: label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| Laurent@714: wx.gizmos.EL_ALLOW_EDIT| Laurent@714: wx.gizmos.EL_ALLOW_DELETE) laurent@640: setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit) laurent@577: for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: laurent@577: setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged) Laurent@714: enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1) Laurent@714: Laurent@714: enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel, Laurent@714: label=_('Initial Value:')) Laurent@714: enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, Laurent@714: border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, Laurent@714: style=wx.CB_READONLY) Laurent@714: self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue) Laurent@714: enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, Laurent@714: border=5, flag=wx.ALL) Laurent@714: Laurent@714: self.EnumeratedPanel.SetSizer(enumerated_panel_sizer) lbessard@125: lbessard@125: # Panel for Array data types lbessard@125: Laurent@714: self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) Laurent@714: typeinfos_sizer.AddWindow(self.ArrayPanel, 1) Laurent@714: Laurent@714: array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0) Laurent@714: array_panel_sizer.AddGrowableCol(0) Laurent@714: array_panel_sizer.AddGrowableCol(1) Laurent@714: array_panel_sizer.AddGrowableRow(1) Laurent@714: Laurent@714: array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW) Laurent@714: Laurent@714: array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:')) Laurent@714: array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY) Laurent@714: self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType) Laurent@714: array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@714: array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW) Laurent@714: Laurent@714: array_initialvalue_label = wx.StaticText(self.ArrayPanel, Laurent@714: label=_('Initial Value:')) Laurent@714: array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, Laurent@714: flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) Laurent@714: Laurent@714: self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel, Laurent@714: style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH) Laurent@714: self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue) Laurent@714: array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, Laurent@714: flag=wx.ALL) Laurent@714: Laurent@714: self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, Laurent@714: label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW| Laurent@714: wx.gizmos.EL_ALLOW_EDIT| Laurent@714: wx.gizmos.EL_ALLOW_DELETE) Laurent@714: for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", Laurent@714: "_OnUpButton", "_OnDownButton"]: Laurent@730: setattr(self.ArrayDimensions, func, self.OnDimensionsChanged) Laurent@714: array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, Laurent@714: flag=wx.GROW|wx.ALL) Laurent@714: Laurent@714: self.ArrayPanel.SetSizer(array_panel_sizer) lbessard@125: lbessard@295: # Panel for Structure data types lbessard@295: Laurent@714: self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) Laurent@714: typeinfos_sizer.AddWindow(self.StructurePanel, 1) Laurent@714: Laurent@714: structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) Laurent@714: structure_panel_sizer.AddGrowableCol(0) Laurent@714: structure_panel_sizer.AddGrowableRow(1) Laurent@714: Laurent@714: structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) Laurent@714: structure_button_sizer.AddGrowableCol(0) Laurent@714: structure_button_sizer.AddGrowableRow(0) Laurent@714: structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, Laurent@714: flag=wx.ALL|wx.GROW) Laurent@714: Laurent@714: structure_elements_label = wx.StaticText(self.StructurePanel, Laurent@714: label=_('Elements :')) Laurent@714: structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM) Laurent@714: Laurent@714: for name, bitmap, help in [ Laurent@714: ("StructureAddButton", "add_element", _("Add element")), Laurent@714: ("StructureDeleteButton", "remove_element", _("Remove element")), Laurent@714: ("StructureUpButton", "up", _("Move element up")), Laurent@714: ("StructureDownButton", "down", _("Move element down"))]: Laurent@714: button = wx.lib.buttons.GenBitmapButton(self.StructurePanel, Laurent@714: bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) Laurent@714: button.SetToolTipString(help) Laurent@714: setattr(self, name, button) Laurent@714: structure_button_sizer.AddWindow(button) Laurent@714: Laurent@714: self.StructureElementsGrid = CustomGrid(self.StructurePanel, lbessard@295: size=wx.Size(0, 150), style=wx.VSCROLL) Laurent@714: self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, Laurent@714: self.OnStructureElementsGridCellChange) Laurent@714: self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, Laurent@714: self.OnStructureElementsGridEditorShown) Laurent@714: structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW) Laurent@714: Laurent@714: self.StructurePanel.SetSizer(structure_panel_sizer) Laurent@714: Laurent@714: self.Editor.SetSizer(self.MainSizer) Laurent@714: lbessard@125: def __init__(self, parent, tagname, window, controler): laurent@586: EditorPanel.__init__(self, parent, tagname, window, controler) lbessard@125: lbessard@295: self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""} laurent@391: self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames()) lbessard@295: self.StructureColSizes = [40, 150, 100, 250] lbessard@295: self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT] lbessard@295: lbessard@295: self.StructureElementsGrid.SetTable(self.StructureElementsTable) laurent@577: self.StructureElementsGrid.SetButtons({"Add": self.StructureAddButton, laurent@577: "Delete": self.StructureDeleteButton, laurent@577: "Up": self.StructureUpButton, laurent@577: "Down": self.StructureDownButton}) laurent@577: laurent@577: def _AddStructureElement(new_row): laurent@577: self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy()) laurent@577: self.RefreshTypeInfos() laurent@577: self.StructureElementsTable.ResetView(self.StructureElementsGrid) laurent@577: return new_row laurent@577: setattr(self.StructureElementsGrid, "_AddRow", _AddStructureElement) laurent@577: laurent@577: def _DeleteStructureElement(row): laurent@577: self.StructureElementsTable.RemoveRow(row) laurent@577: self.RefreshTypeInfos() laurent@577: self.StructureElementsTable.ResetView(self.StructureElementsGrid) laurent@577: setattr(self.StructureElementsGrid, "_DeleteRow", _DeleteStructureElement) laurent@577: laurent@577: def _MoveStructureElement(row, move): laurent@577: new_row = self.StructureElementsTable.MoveRow(row, move) laurent@577: if new_row != row: laurent@577: self.RefreshTypeInfos() laurent@577: self.StructureElementsTable.ResetView(self.StructureElementsGrid) laurent@577: return new_row laurent@577: setattr(self.StructureElementsGrid, "_MoveRow", _MoveStructureElement) laurent@577: lbessard@295: self.StructureElementsGrid.SetRowLabelSize(0) lbessard@295: for col in range(self.StructureElementsTable.GetNumberCols()): lbessard@295: attr = wx.grid.GridCellAttr() lbessard@295: attr.SetAlignment(self.StructureColAlignements[col], wx.ALIGN_CENTRE) lbessard@295: self.StructureElementsGrid.SetColAttr(col, attr) laurent@391: self.StructureElementsGrid.SetColMinimalWidth(col, self.StructureColSizes[col]) laurent@391: self.StructureElementsGrid.AutoSizeColumn(col, False) laurent@577: self.StructureElementsGrid.RefreshButtons() laurent@391: laurent@391: for datatype in GetDatatypeTypes(): laurent@391: self.DerivationType.Append(_(datatype)) lbessard@125: self.SubrangePanel.Hide() lbessard@125: self.EnumeratedPanel.Hide() lbessard@125: self.ArrayPanel.Hide() lbessard@295: self.StructurePanel.Hide() lbessard@125: self.CurrentPanel = "Directly" laurent@566: self.Highlights = [] lbessard@130: self.Initializing = False lbessard@125: laurent@576: self.HighlightControls = { laurent@576: ("Directly", "base"): self.DirectlyBaseType, laurent@576: ("Directly", "initial"): self.DirectlyInitialValue, laurent@576: ("Subrange", "base"): self.SubrangeBaseType, laurent@576: ("Subrange", "lower"): self.SubrangeMinimum, laurent@576: ("Subrange", "upper"): self.SubrangeMaximum, laurent@576: ("Subrange", "initial"): self.SubrangeInitialValue, laurent@576: ("Enumerated", "value"): self.EnumeratedValues, laurent@576: ("Enumerated", "initial"): self.EnumeratedInitialValue, laurent@576: ("Array", "initial"): self.ArrayInitialValue, laurent@576: ("Array", "base"): self.ArrayBaseType, laurent@576: ("Array", "range"): self.ArrayDimensions, laurent@576: } laurent@576: laurent@566: self.RefreshHighlightsTimer = wx.Timer(self, -1) laurent@566: self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) laurent@566: laurent@566: def __del__(self): laurent@566: self.RefreshHighlightsTimer.Stop() lbessard@125: laurent@586: def GetBufferState(self): laurent@586: return self.Controler.GetBufferState() laurent@586: laurent@586: def Undo(self): laurent@586: self.Controler.LoadPrevious() laurent@586: self.ParentWindow.CloseTabsWithoutModel() laurent@586: laurent@586: def Redo(self): laurent@586: self.Controler.LoadNext() laurent@586: self.ParentWindow.CloseTabsWithoutModel() laurent@586: laurent@586: def HasNoModel(self): laurent@586: return self.Controler.GetEditedElement(self.TagName) is None laurent@586: lbessard@125: def RefreshView(self): lbessard@130: self.Initializing = True lbessard@125: self.DirectlyBaseType.Clear() lbessard@125: self.ArrayBaseType.Clear() lbessard@125: for datatype in self.Controler.GetDataTypes(self.TagName): lbessard@125: self.DirectlyBaseType.Append(datatype) lbessard@125: self.ArrayBaseType.Append(datatype) lbessard@125: self.DirectlyBaseType.SetSelection(0) lbessard@125: self.SubrangeBaseType.Clear() lbessard@125: words = self.TagName.split("::") lbessard@231: for base_type in self.Controler.GetSubrangeBaseTypes(words[1]): lbessard@125: self.SubrangeBaseType.Append(base_type) lbessard@125: self.SubrangeBaseType.SetSelection(0) lbessard@125: self.RefreshBoundsRange() lbessard@125: type_infos = self.Controler.GetDataTypeInfos(self.TagName) lbessard@125: if type_infos is not None: laurent@391: datatype = type_infos["type"] laurent@391: self.DerivationType.SetStringSelection(_(datatype)) lbessard@125: if type_infos["type"] == "Directly": lbessard@125: self.DirectlyBaseType.SetStringSelection(type_infos["base_type"]) lbessard@125: self.DirectlyInitialValue.SetValue(type_infos["initial"]) lbessard@125: elif type_infos["type"] == "Subrange": lbessard@125: self.SubrangeBaseType.SetStringSelection(type_infos["base_type"]) lbessard@125: self.RefreshBoundsRange() laurent@389: self.SubrangeMinimum.SetValue(int(type_infos["min"])) laurent@389: self.SubrangeMaximum.SetValue(int(type_infos["max"])) lbessard@125: self.RefreshSubrangeInitialValueRange() lbessard@125: if type_infos["initial"] != "": lbessard@125: self.SubrangeInitialValue.SetValue(int(type_infos["initial"])) lbessard@125: else: lbessard@125: self.SubrangeInitialValue.SetValue(type_infos["min"]) lbessard@125: elif type_infos["type"] == "Enumerated": lbessard@125: self.EnumeratedValues.SetStrings(type_infos["values"]) lbessard@125: self.RefreshEnumeratedValues() lbessard@125: self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"]) lbessard@125: elif type_infos["type"] == "Array": lbessard@125: self.ArrayBaseType.SetStringSelection(type_infos["base_type"]) laurent@389: self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"])) lbessard@125: self.ArrayInitialValue.SetValue(type_infos["initial"]) lbessard@295: elif type_infos["type"] == "Structure": lbessard@295: self.StructureElementsTable.SetData(type_infos["elements"]) lbessard@125: self.RefreshDisplayedInfos() laurent@576: self.ShowHighlights() laurent@576: self.StructureElementsTable.ResetView(self.StructureElementsGrid) laurent@577: self.StructureElementsGrid.RefreshButtons() lbessard@130: self.Initializing = False laurent@586: lbessard@125: def OnDerivationTypeChanged(self, event): laurent@636: wx.CallAfter(self.RefreshDisplayedInfos) laurent@636: wx.CallAfter(self.RefreshTypeInfos) lbessard@125: event.Skip() lbessard@125: lbessard@231: def OnReturnKeyPressed(self, event): lbessard@231: self.RefreshTypeInfos() lbessard@231: lbessard@125: def OnInfosChanged(self, event): lbessard@125: self.RefreshTypeInfos() lbessard@125: event.Skip() lbessard@125: lbessard@125: def OnSubrangeBaseTypeChanged(self, event): lbessard@125: self.RefreshBoundsRange() lbessard@125: self.RefreshTypeInfos() lbessard@125: event.Skip() lbessard@125: lbessard@125: def OnSubrangeMinimumChanged(self, event): etisserant@215: if not self.Initializing: lbessard@130: wx.CallAfter(self.SubrangeMinimum.SetValue, min(self.SubrangeMaximum.GetValue(), self.SubrangeMinimum.GetValue())) lbessard@130: wx.CallAfter(self.RefreshSubrangeInitialValueRange) lbessard@130: wx.CallAfter(self.RefreshTypeInfos) lbessard@125: event.Skip() lbessard@125: lbessard@125: def OnSubrangeMaximumChanged(self, event): etisserant@215: if not self.Initializing: lbessard@130: wx.CallAfter(self.SubrangeMaximum.SetValue, max(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue())) lbessard@130: wx.CallAfter(self.RefreshSubrangeInitialValueRange) lbessard@130: wx.CallAfter(self.RefreshTypeInfos) lbessard@125: event.Skip() lbessard@125: lbessard@125: def OnDimensionsChanged(self, event): lbessard@125: wx.CallAfter(self.RefreshTypeInfos) lbessard@125: event.Skip() lbessard@125: lbessard@295: def OnEnumeratedValueEndEdit(self, event): lbessard@295: text = event.GetText() lbessard@295: values = self.EnumeratedValues.GetStrings() lbessard@295: index = event.GetIndex() lbessard@295: if index >= len(values) or values[index].upper() != text.upper(): lbessard@295: if text.upper() in [value.upper() for value in values]: laurent@391: message = wx.MessageDialog(self, _("\"%s\" value already defined!")%text, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@295: message.ShowModal() lbessard@295: message.Destroy() lbessard@295: event.Veto() lbessard@295: elif text.upper() in IEC_KEYWORDS: laurent@391: message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%text, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@295: message.ShowModal() lbessard@295: message.Destroy() lbessard@295: else: laurent@610: initial_selected = None laurent@636: if index < len(values) and self.EnumeratedInitialValue.GetStringSelection() == values[index]: laurent@610: initial_selected = text laurent@610: wx.CallAfter(self.RefreshEnumeratedValues, initial_selected) lbessard@295: wx.CallAfter(self.RefreshTypeInfos) lbessard@295: event.Skip() lbessard@295: else: lbessard@295: event.Skip() lbessard@295: lbessard@295: def OnEnumeratedValuesChanged(self, event): lbessard@295: wx.CallAfter(self.RefreshEnumeratedValues) lbessard@295: wx.CallAfter(self.RefreshTypeInfos) lbessard@295: event.Skip() lbessard@295: lbessard@295: def OnStructureElementsGridCellChange(self, event): lbessard@295: row, col = event.GetRow(), event.GetCol() lbessard@295: colname = self.StructureElementsTable.GetColLabelValue(col) lbessard@295: value = self.StructureElementsTable.GetValue(row, col) lbessard@295: if colname == "Name": lbessard@295: if not TestIdentifier(value): laurent@391: message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@295: message.ShowModal() lbessard@295: message.Destroy() lbessard@295: event.Veto() lbessard@295: elif value.upper() in IEC_KEYWORDS: laurent@391: message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@295: message.ShowModal() lbessard@295: message.Destroy() lbessard@295: event.Veto() lbessard@295: ## elif value.upper() in self.PouNames: lbessard@295: ## message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%value, "Error", wx.OK|wx.ICON_ERROR) lbessard@295: ## message.ShowModal() lbessard@295: ## message.Destroy() lbessard@295: ## event.Veto() lbessard@295: elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]: b@427: message = wx.MessageDialog(self, _("An element named \"%s\" already exists in this structure!")%value, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@295: message.ShowModal() lbessard@295: message.Destroy() lbessard@295: event.Veto() lbessard@295: else: lbessard@295: self.RefreshTypeInfos() laurent@577: wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) lbessard@295: ## old_value = self.Table.GetOldValue() lbessard@295: ## if old_value != "": lbessard@295: ## self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) lbessard@295: ## self.Controler.BufferProject() lbessard@295: event.Skip() lbessard@295: else: lbessard@295: self.RefreshTypeInfos() laurent@577: wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) lbessard@295: event.Skip() lbessard@295: laurent@577: def OnStructureElementsGridSelectCell(self, event): laurent@577: wx.CallAfter(self.RefreshStructureButtons) laurent@577: event.Skip() laurent@577: lbessard@295: def OnStructureElementsGridEditorShown(self, event): lbessard@295: row, col = event.GetRow(), event.GetCol() lbessard@295: if self.StructureElementsTable.GetColLabelValue(col) == "Type": lbessard@295: type_menu = wx.Menu(title='') lbessard@295: base_menu = wx.Menu(title='') lbessard@295: for base_type in self.Controler.GetBaseTypes(): lbessard@295: new_id = wx.NewId() lbessard@295: AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type) lbessard@295: self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(base_type), id=new_id) laurent@391: type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu) lbessard@295: datatype_menu = wx.Menu(title='') laurent@462: for datatype in self.Controler.GetDataTypes(self.TagName, False): lbessard@295: new_id = wx.NewId() lbessard@295: AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) lbessard@295: self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(datatype), id=new_id) laurent@391: type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu) lbessard@295: ## functionblock_menu = wx.Menu(title='') laurent@462: ## bodytype = self.Controler.GetEditedElementBodyType(self.TagName) laurent@462: ## pouname, poutype = self.Controler.GetEditedElementType(self.TagName) lbessard@295: ## if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]: laurent@462: ## for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): lbessard@295: ## new_id = wx.NewId() lbessard@295: ## AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) lbessard@295: ## self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) laurent@391: ## type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) lbessard@295: rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col)) lbessard@295: self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) laurent@666: type_menu.Destroy() lbessard@295: event.Veto() lbessard@295: else: lbessard@295: event.Skip() lbessard@295: lbessard@295: def GetElementTypeFunction(self, base_type): lbessard@295: def ElementTypeFunction(event): lbessard@295: row = self.StructureElementsGrid.GetGridCursorRow() lbessard@295: self.StructureElementsTable.SetValueByName(row, "Type", base_type) lbessard@295: self.RefreshTypeInfos() lbessard@295: self.StructureElementsTable.ResetView(self.StructureElementsGrid) lbessard@295: return ElementTypeFunction lbessard@295: lbessard@125: def RefreshDisplayedInfos(self): laurent@391: selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] lbessard@125: if selected != self.CurrentPanel: lbessard@125: if self.CurrentPanel == "Directly": lbessard@125: self.DirectlyPanel.Hide() lbessard@125: elif self.CurrentPanel == "Subrange": lbessard@125: self.SubrangePanel.Hide() lbessard@125: elif self.CurrentPanel == "Enumerated": lbessard@125: self.EnumeratedPanel.Hide() lbessard@125: elif self.CurrentPanel == "Array": lbessard@125: self.ArrayPanel.Hide() lbessard@295: elif self.CurrentPanel == "Structure": lbessard@295: self.StructurePanel.Hide() lbessard@125: self.CurrentPanel = selected lbessard@125: if selected == "Directly": lbessard@125: self.DirectlyPanel.Show() lbessard@125: elif selected == "Subrange": lbessard@125: self.SubrangePanel.Show() lbessard@125: elif selected == "Enumerated": lbessard@125: self.EnumeratedPanel.Show() lbessard@125: elif selected == "Array": lbessard@125: self.ArrayPanel.Show() lbessard@295: elif selected == "Structure": lbessard@295: self.StructurePanel.Show() lbessard@125: self.MainSizer.Layout() lbessard@125: laurent@610: def RefreshEnumeratedValues(self, initial_selected=None): laurent@610: if initial_selected is None: laurent@610: initial_selected = self.EnumeratedInitialValue.GetStringSelection() lbessard@125: self.EnumeratedInitialValue.Clear() lbessard@125: self.EnumeratedInitialValue.Append("") lbessard@125: for value in self.EnumeratedValues.GetStrings(): lbessard@125: self.EnumeratedInitialValue.Append(value) laurent@610: self.EnumeratedInitialValue.SetStringSelection(initial_selected) lbessard@125: lbessard@125: def RefreshBoundsRange(self): lbessard@231: range = self.Controler.GetDataTypeRange(self.SubrangeBaseType.GetStringSelection()) lbessard@125: if range is not None: lbessard@125: min_value, max_value = range lbessard@125: self.SubrangeMinimum.SetRange(min_value, max_value) lbessard@125: self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value)) lbessard@125: self.SubrangeMaximum.SetRange(min_value, max_value) lbessard@125: self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value)) lbessard@125: lbessard@125: def RefreshSubrangeInitialValueRange(self): lbessard@125: self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()) lbessard@125: lbessard@125: def RefreshTypeInfos(self): laurent@391: selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] lbessard@125: infos = {"type" : selected} lbessard@125: if selected == "Directly": lbessard@125: infos["base_type"] = self.DirectlyBaseType.GetStringSelection() lbessard@125: infos["initial"] = self.DirectlyInitialValue.GetValue() lbessard@125: elif selected == "Subrange": lbessard@125: infos["base_type"] = self.SubrangeBaseType.GetStringSelection() laurent@389: infos["min"] = str(self.SubrangeMinimum.GetValue()) laurent@389: infos["max"] = str(self.SubrangeMaximum.GetValue()) lbessard@125: initial_value = self.SubrangeInitialValue.GetValue() lbessard@125: if initial_value == infos["min"]: lbessard@125: infos["initial"] = "" lbessard@125: else: lbessard@125: infos["initial"] = str(initial_value) lbessard@125: elif selected == "Enumerated": lbessard@125: infos["values"] = self.EnumeratedValues.GetStrings() lbessard@125: infos["initial"] = self.EnumeratedInitialValue.GetStringSelection() lbessard@125: elif selected == "Array": lbessard@125: infos["base_type"] = self.ArrayBaseType.GetStringSelection() lbessard@207: infos["dimensions"] = [] lbessard@207: for dimensions in self.ArrayDimensions.GetStrings(): lbessard@207: result = DIMENSION_MODEL.match(dimensions) lbessard@207: if result is None: laurent@391: message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@207: message.ShowModal() lbessard@207: message.Destroy() lbessard@207: self.RefreshView() lbessard@207: return lbessard@207: bounds = result.groups() laurent@460: if int(bounds[0]) >= int(bounds[1]): laurent@391: message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) lbessard@207: message.ShowModal() lbessard@207: message.Destroy() lbessard@207: self.RefreshView() lbessard@207: return laurent@389: infos["dimensions"].append(bounds) lbessard@125: infos["initial"] = self.ArrayInitialValue.GetValue() lbessard@295: elif selected == "Structure": lbessard@295: infos["elements"] = self.StructureElementsTable.GetData() lbessard@295: infos["initial"] = "" lbessard@125: self.Controler.SetDataTypeInfos(self.TagName, infos) lbessard@125: self.ParentWindow.RefreshTitle() laurent@494: self.ParentWindow.RefreshFileMenu() lbessard@125: self.ParentWindow.RefreshEditMenu() lbessard@125: lbessard@231: #------------------------------------------------------------------------------- laurent@566: # Highlights showing functions lbessard@231: #------------------------------------------------------------------------------- lbessard@231: laurent@566: def OnRefreshHighlightsTimer(self, event): lbessard@231: self.RefreshView() laurent@566: event.Skip() laurent@566: laurent@566: def ClearHighlights(self, highlight_type=None): laurent@566: if highlight_type is None: laurent@566: self.Highlights = [] laurent@566: else: laurent@566: self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] laurent@576: for control in self.HighlightControls.itervalues(): laurent@576: if isinstance(control, (wx.ComboBox, wx.SpinCtrl)): laurent@576: control.SetBackgroundColour(wx.NullColour) laurent@576: control.SetForegroundColour(wx.NullColour) laurent@576: elif isinstance(control, wx.TextCtrl): laurent@576: value = control.GetValue() laurent@576: control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour)) laurent@576: elif isinstance(control, wx.gizmos.EditableListBox): laurent@576: listctrl = control.GetListCtrl() laurent@576: for i in xrange(listctrl.GetItemCount()): laurent@576: listctrl.SetItemBackgroundColour(i, wx.NullColour) laurent@576: listctrl.SetItemTextColour(i, wx.NullColour) laurent@576: self.StructureElementsTable.ClearHighlights(highlight_type) laurent@566: self.RefreshView() laurent@566: laurent@566: def AddHighlight(self, infos, start, end ,highlight_type): laurent@566: self.Highlights.append((infos, start, end, highlight_type)) laurent@566: self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) lbessard@231: laurent@576: def ShowHighlights(self): lbessard@231: type_infos = self.Controler.GetDataTypeInfos(self.TagName) laurent@566: for infos, start, end, highlight_type in self.Highlights: laurent@576: if infos[0] == "struct": laurent@576: self.StructureElementsTable.AddHighlight(infos[1:], highlight_type) laurent@576: else: laurent@576: control = self.HighlightControls.get((type_infos["type"], infos[0]), None) laurent@576: if control is not None: laurent@576: if isinstance(control, (wx.ComboBox, wx.SpinCtrl)): laurent@576: control.SetBackgroundColour(highlight_type[0]) laurent@576: control.SetForegroundColour(highlight_type[1]) laurent@576: elif isinstance(control, wx.TextCtrl): laurent@576: control.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0])) laurent@576: elif isinstance(control, wx.gizmos.EditableListBox): laurent@576: listctrl = control.GetListCtrl() laurent@576: listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) laurent@576: listctrl.SetItemTextColour(infos[1], highlight_type[1]) laurent@576: listctrl.Select(listctrl.FocusedItem, False) laurent@576: