editors/ResourceEditor.py
changeset 814 5743cbdff669
child 831 dec885ba1f2b
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
       
    26 import wx.lib.buttons
       
    27 import wx.grid
       
    28 
       
    29 from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
       
    30 from controls import CustomGrid, CustomTable, DurationCellEditor
       
    31 from EditorPanel import EditorPanel
       
    32 from util.BitmapLibrary import GetBitmap
       
    33 
       
    34 #-------------------------------------------------------------------------------
       
    35 #                          Configuration Editor class
       
    36 #-------------------------------------------------------------------------------
       
    37 
       
    38 [ID_CONFIGURATIONEDITOR, 
       
    39 ] = [wx.NewId() for _init_ctrls in range(1)]
       
    40 
       
    41 class ConfigurationEditor(EditorPanel):
       
    42     
       
    43     ID = ID_CONFIGURATIONEDITOR
       
    44     VARIABLE_PANEL_TYPE = "config"
       
    45     
       
    46     def GetBufferState(self):
       
    47         return self.Controler.GetBufferState()
       
    48         
       
    49     def Undo(self):
       
    50         self.Controler.LoadPrevious()
       
    51         self.ParentWindow.CloseTabsWithoutModel()
       
    52             
       
    53     def Redo(self):
       
    54         self.Controler.LoadNext()
       
    55         self.ParentWindow.CloseTabsWithoutModel()
       
    56     
       
    57     def HasNoModel(self):
       
    58         return self.Controler.GetEditedElement(self.TagName) is None
       
    59 
       
    60 
       
    61 #-------------------------------------------------------------------------------
       
    62 #                            Resource Editor class
       
    63 #-------------------------------------------------------------------------------
       
    64 
       
    65 def GetTasksTableColnames():
       
    66     _ = lambda x : x
       
    67     return [_("Name"), _("Triggering"), _("Single"), _("Interval"), _("Priority")]
       
    68 
       
    69 def GetTaskTriggeringOptions():
       
    70     _ = lambda x : x
       
    71     return [_("Interrupt"), _("Cyclic")]
       
    72 TASKTRIGGERINGOPTIONS_DICT = dict([(_(option), option) for option in GetTaskTriggeringOptions()])
       
    73 
       
    74 def GetInstancesTableColnames():
       
    75     _ = lambda x : x
       
    76     return [_("Name"), _("Type"), _("Task")]
       
    77 
       
    78 class ResourceTable(CustomTable):
       
    79     
       
    80     """
       
    81     A custom wx.grid.Grid Table using user supplied data
       
    82     """
       
    83     def __init__(self, parent, data, colnames):
       
    84         # The base class must be initialized *first*
       
    85         CustomTable.__init__(self, parent, data, colnames)
       
    86         self.ColAlignements = []
       
    87         self.ColSizes = []
       
    88         
       
    89     def GetColAlignements(self):
       
    90         return self.ColAlignements
       
    91     
       
    92     def SetColAlignements(self, list):
       
    93         self.ColAlignements = list
       
    94 
       
    95     def GetColSizes(self):
       
    96         return self.ColSizes
       
    97     
       
    98     def SetColSizes(self, list):
       
    99         self.ColSizes = list
       
   100 
       
   101     def GetValue(self, row, col):
       
   102         if row < self.GetNumberRows():
       
   103             colname = self.GetColLabelValue(col, False)
       
   104             value = str(self.data[row].get(colname, ""))
       
   105             if colname == "Triggering":
       
   106                 return _(value)
       
   107             return value
       
   108         
       
   109     def SetValue(self, row, col, value):
       
   110         if col < len(self.colnames):
       
   111             colname = self.GetColLabelValue(col, False)
       
   112             if colname == "Triggering":
       
   113                 value = TASKTRIGGERINGOPTIONS_DICT[value]
       
   114             self.data[row][colname] = value
       
   115     
       
   116     def _updateColAttrs(self, grid):
       
   117         """
       
   118         wx.grid.Grid -> update the column attributes to add the
       
   119         appropriate renderer given the column name.
       
   120 
       
   121         Otherwise default to the default renderer.
       
   122         """
       
   123         
       
   124         for col in range(self.GetNumberCols()):
       
   125             attr = wx.grid.GridCellAttr()
       
   126             attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE)
       
   127             grid.SetColAttr(col, attr)
       
   128             grid.SetColSize(col, self.ColSizes[col])
       
   129         
       
   130         for row in range(self.GetNumberRows()):
       
   131             row_highlights = self.Highlights.get(row, {})
       
   132             for col in range(self.GetNumberCols()):
       
   133                 editor = None
       
   134                 renderer = None
       
   135                 colname = self.GetColLabelValue(col, False)
       
   136                 grid.SetReadOnly(row, col, False)
       
   137                 if colname == "Name":
       
   138                     editor = wx.grid.GridCellTextEditor()
       
   139                     renderer = wx.grid.GridCellStringRenderer()
       
   140                 elif colname == "Interval":
       
   141                     editor = DurationCellEditor(self)
       
   142                     renderer = wx.grid.GridCellStringRenderer()
       
   143                     if self.GetValueByName(row, "Triggering") != "Cyclic":
       
   144                         grid.SetReadOnly(row, col, True)
       
   145                 elif colname == "Single":
       
   146                     editor = wx.grid.GridCellChoiceEditor()
       
   147                     editor.SetParameters(self.Parent.VariableList)
       
   148                     if self.GetValueByName(row, "Triggering") != "Interrupt":
       
   149                         grid.SetReadOnly(row, col, True)
       
   150                 elif colname == "Triggering":
       
   151                     editor = wx.grid.GridCellChoiceEditor()
       
   152                     editor.SetParameters(",".join([""] + map(_, GetTaskTriggeringOptions())))
       
   153                 elif colname == "Type":
       
   154                     editor = wx.grid.GridCellChoiceEditor()
       
   155                     editor.SetParameters(self.Parent.TypeList)
       
   156                 elif colname == "Priority":
       
   157                     editor = wx.grid.GridCellNumberEditor()
       
   158                     editor.SetParameters("0,65535")
       
   159                 elif colname == "Task":
       
   160                     editor = wx.grid.GridCellChoiceEditor()
       
   161                     editor.SetParameters(self.Parent.TaskList)
       
   162                     
       
   163                 grid.SetCellEditor(row, col, editor)
       
   164                 grid.SetCellRenderer(row, col, renderer)
       
   165                 
       
   166                 highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
       
   167                 grid.SetCellBackgroundColour(row, col, highlight_colours[0])
       
   168                 grid.SetCellTextColour(row, col, highlight_colours[1])
       
   169             self.ResizeRow(grid, row)
       
   170 
       
   171     
       
   172 #-------------------------------------------------------------------------------
       
   173 #                        Highlights showing functions
       
   174 #-------------------------------------------------------------------------------
       
   175 
       
   176     def AddHighlight(self, infos, highlight_type):
       
   177         row_highlights = self.Highlights.setdefault(infos[0], {})
       
   178         col_highlights = row_highlights.setdefault(infos[1], [])
       
   179         col_highlights.append(highlight_type)
       
   180     
       
   181     def ClearHighlights(self, highlight_type=None):
       
   182         if highlight_type is None:
       
   183             self.Highlights = {}
       
   184         else:
       
   185             for row, row_highlights in self.Highlights.iteritems():
       
   186                 row_items = row_highlights.items()
       
   187                 for col, col_highlights in row_items:
       
   188                     if highlight_type in col_highlights:
       
   189                         col_highlights.remove(highlight_type)
       
   190                     if len(col_highlights) == 0:
       
   191                         row_highlights.pop(col)
       
   192 
       
   193 
       
   194 
       
   195 class ResourceEditor(EditorPanel):
       
   196     
       
   197     VARIABLE_PANEL_TYPE = "resource"
       
   198 
       
   199     def _init_Editor(self, parent):
       
   200         self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER|wx.TAB_TRAVERSAL)
       
   201         
       
   202         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
       
   203         main_sizer.AddGrowableCol(0)
       
   204         main_sizer.AddGrowableRow(0)
       
   205         main_sizer.AddGrowableRow(1)
       
   206         
       
   207         tasks_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
       
   208         tasks_sizer.AddGrowableCol(0)
       
   209         tasks_sizer.AddGrowableRow(1)
       
   210         main_sizer.AddSizer(tasks_sizer, border=5, 
       
   211               flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
       
   212         
       
   213         tasks_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
       
   214         tasks_buttons_sizer.AddGrowableCol(0)
       
   215         tasks_buttons_sizer.AddGrowableRow(0)
       
   216         tasks_sizer.AddSizer(tasks_buttons_sizer, flag=wx.GROW)
       
   217         
       
   218         tasks_label = wx.StaticText(self.Editor, label=_(u'Tasks:'))
       
   219         tasks_buttons_sizer.AddWindow(tasks_label, flag=wx.ALIGN_BOTTOM)
       
   220         
       
   221         for name, bitmap, help in [
       
   222                 ("AddTaskButton", "add_element", _("Add task")),
       
   223                 ("DeleteTaskButton", "remove_element", _("Remove task")),
       
   224                 ("UpTaskButton", "up", _("Move task up")),
       
   225                 ("DownTaskButton", "down", _("Move task down"))]:
       
   226             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   227                   bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   228             button.SetToolTipString(help)
       
   229             setattr(self, name, button)
       
   230             tasks_buttons_sizer.AddWindow(button)
       
   231         
       
   232         self.TasksGrid = CustomGrid(self.Editor, style=wx.VSCROLL)
       
   233         self.TasksGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnTasksGridCellChange)
       
   234         tasks_sizer.AddWindow(self.TasksGrid, flag=wx.GROW)
       
   235         
       
   236         instances_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
       
   237         instances_sizer.AddGrowableCol(0)
       
   238         instances_sizer.AddGrowableRow(1)
       
   239         main_sizer.AddSizer(instances_sizer, border=5, 
       
   240               flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
       
   241         
       
   242         instances_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
       
   243         instances_buttons_sizer.AddGrowableCol(0)
       
   244         instances_buttons_sizer.AddGrowableRow(0)
       
   245         instances_sizer.AddSizer(instances_buttons_sizer, flag=wx.GROW)
       
   246         
       
   247         instances_label = wx.StaticText(self.Editor, label=_(u'Instances:'))
       
   248         instances_buttons_sizer.AddWindow(instances_label, flag=wx.ALIGN_BOTTOM)
       
   249         
       
   250         for name, bitmap, help in [
       
   251                 ("AddInstanceButton", "add_element", _("Add instance")),
       
   252                 ("DeleteInstanceButton", "remove_element", _("Remove instance")),
       
   253                 ("UpInstanceButton", "up", _("Move instance up")),
       
   254                 ("DownInstanceButton", "down", _("Move instance down"))]:
       
   255             button = wx.lib.buttons.GenBitmapButton(self.Editor, 
       
   256                   bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   257             button.SetToolTipString(help)
       
   258             setattr(self, name, button)
       
   259             instances_buttons_sizer.AddWindow(button)
       
   260         
       
   261         self.InstancesGrid = CustomGrid(self.Editor, style=wx.VSCROLL)
       
   262         self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, 
       
   263               self.OnInstancesGridCellChange)
       
   264         instances_sizer.AddWindow(self.InstancesGrid, flag=wx.GROW)
       
   265         
       
   266         self.Editor.SetSizer(main_sizer)
       
   267         
       
   268     def __init__(self, parent, tagname, window, controler):
       
   269         EditorPanel.__init__(self, parent, tagname, window, controler)
       
   270         
       
   271         self.RefreshHighlightsTimer = wx.Timer(self, -1)
       
   272         self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
       
   273         
       
   274         self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0}
       
   275         self.TasksTable = ResourceTable(self, [], GetTasksTableColnames())
       
   276         self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT])
       
   277         self.TasksTable.SetColSizes([200, 100, 100, 150, 100])
       
   278         self.TasksGrid.SetTable(self.TasksTable)
       
   279         self.TasksGrid.SetButtons({"Add": self.AddTaskButton,
       
   280                                    "Delete": self.DeleteTaskButton,
       
   281                                    "Up": self.UpTaskButton,
       
   282                                    "Down": self.DownTaskButton})
       
   283         
       
   284         def _AddTask(new_row):
       
   285             self.TasksTable.InsertRow(new_row, self.TasksDefaultValue.copy())
       
   286             self.RefreshModel()
       
   287             self.RefreshView()
       
   288             return new_row
       
   289         setattr(self.TasksGrid, "_AddRow", _AddTask)
       
   290         
       
   291         def _DeleteTask(row):
       
   292             self.TasksTable.RemoveRow(row)
       
   293             self.RefreshModel()
       
   294             self.RefreshView()
       
   295         setattr(self.TasksGrid, "_DeleteRow", _DeleteTask)
       
   296         
       
   297         def _MoveTask(row, move):
       
   298             new_row = self.TasksTable.MoveRow(row, move)
       
   299             if new_row != row:
       
   300                 self.RefreshModel()
       
   301                 self.RefreshView()
       
   302             return new_row
       
   303         setattr(self.TasksGrid, "_MoveRow", _MoveTask)
       
   304         
       
   305         self.TasksGrid.SetRowLabelSize(0)
       
   306         self.TasksTable.ResetView(self.TasksGrid)
       
   307         self.TasksGrid.RefreshButtons()
       
   308         
       
   309         self.InstancesDefaultValue = {"Name" : "", "Type" : "", "Task" : ""}
       
   310         self.InstancesTable = ResourceTable(self, [], GetInstancesTableColnames())
       
   311         self.InstancesTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT])
       
   312         self.InstancesTable.SetColSizes([200, 150, 150])
       
   313         self.InstancesGrid.SetTable(self.InstancesTable)
       
   314         self.InstancesGrid.SetButtons({"Add": self.AddInstanceButton,
       
   315                                        "Delete": self.DeleteInstanceButton,
       
   316                                        "Up": self.UpInstanceButton,
       
   317                                        "Down": self.DownInstanceButton})
       
   318         
       
   319         def _AddInstance(new_row):
       
   320             self.InstancesTable.InsertRow(new_row, self.InstancesDefaultValue.copy())
       
   321             self.RefreshModel()
       
   322             self.RefreshView()
       
   323             return new_row
       
   324         setattr(self.InstancesGrid, "_AddRow", _AddInstance)
       
   325         
       
   326         def _DeleteInstance(row):
       
   327             self.InstancesTable.RemoveRow(row)
       
   328             self.RefreshModel()
       
   329             self.RefreshView()
       
   330         setattr(self.InstancesGrid, "_DeleteRow", _DeleteInstance)
       
   331         
       
   332         def _MoveInstance(row, move):
       
   333             new_row = max(0, min(row + move, self.InstancesTable.GetNumberRows() - 1))
       
   334             if new_row != row:
       
   335                 if self.InstancesTable.GetValueByName(row, "Task") != self.InstancesTable.GetValueByName(new_row, "Task"):
       
   336                     return row 
       
   337                 self.InstancesTable.MoveRow(row, move)
       
   338                 self.RefreshModel()
       
   339                 self.RefreshView()
       
   340             return new_row
       
   341         setattr(self.InstancesGrid, "_MoveRow", _MoveInstance)
       
   342         
       
   343         def _RefreshInstanceButtons():
       
   344             if self:
       
   345                 rows = self.InstancesTable.GetNumberRows()
       
   346                 row = self.InstancesGrid.GetGridCursorRow()
       
   347                 self.DeleteInstanceButton.Enable(rows > 0)
       
   348                 self.UpInstanceButton.Enable(row > 0 and 
       
   349                     self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row - 1, "Task"))
       
   350                 self.DownInstanceButton.Enable(0 <= row < rows - 1 and 
       
   351                     self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row + 1, "Task"))
       
   352         setattr(self.InstancesGrid, "RefreshButtons", _RefreshInstanceButtons)
       
   353         
       
   354         self.InstancesGrid.SetRowLabelSize(0)
       
   355         self.InstancesTable.ResetView(self.InstancesGrid)
       
   356         self.InstancesGrid.RefreshButtons()
       
   357         
       
   358         self.TasksGrid.SetFocus()
       
   359         
       
   360     def __del__(self):
       
   361         self.RefreshHighlightsTimer.Stop()
       
   362 
       
   363     def RefreshTypeList(self):
       
   364         self.TypeList = ""
       
   365         blocktypes = self.Controler.GetBlockResource()
       
   366         for blocktype in blocktypes:
       
   367             self.TypeList += ",%s"%blocktype
       
   368 
       
   369     def RefreshTaskList(self):
       
   370         self.TaskList = ""
       
   371         for row in xrange(self.TasksTable.GetNumberRows()):
       
   372             self.TaskList += ",%s"%self.TasksTable.GetValueByName(row, "Name")
       
   373 
       
   374     def RefreshVariableList(self):
       
   375         self.VariableList = ""
       
   376         for variable in self.Controler.GetEditedResourceVariables(self.TagName):
       
   377             self.VariableList += ",%s"%variable
       
   378         
       
   379     def RefreshModel(self):
       
   380         self.Controler.SetEditedResourceInfos(self.TagName, self.TasksTable.GetData(), self.InstancesTable.GetData())
       
   381         self.RefreshBuffer()
       
   382         
       
   383     # Buffer the last model state
       
   384     def RefreshBuffer(self):
       
   385         self.Controler.BufferProject()
       
   386         self.ParentWindow.RefreshTitle()
       
   387         self.ParentWindow.RefreshFileMenu()
       
   388         self.ParentWindow.RefreshEditMenu()
       
   389 
       
   390     def GetBufferState(self):
       
   391         return self.Controler.GetBufferState()
       
   392         
       
   393     def Undo(self):
       
   394         self.Controler.LoadPrevious()
       
   395         self.ParentWindow.CloseTabsWithoutModel()
       
   396             
       
   397     def Redo(self):
       
   398         self.Controler.LoadNext()
       
   399         self.ParentWindow.CloseTabsWithoutModel()
       
   400     
       
   401     def HasNoModel(self):
       
   402         return self.Controler.GetEditedElement(self.TagName) is None
       
   403 
       
   404     def RefreshView(self, variablepanel=True):
       
   405         EditorPanel.RefreshView(self, variablepanel)
       
   406         
       
   407         tasks, instances = self.Controler.GetEditedResourceInfos(self.TagName)
       
   408         self.TasksTable.SetData(tasks)
       
   409         self.InstancesTable.SetData(instances)
       
   410         self.RefreshTypeList()
       
   411         self.RefreshTaskList()
       
   412         self.RefreshVariableList()
       
   413         self.TasksTable.ResetView(self.TasksGrid)
       
   414         self.InstancesTable.ResetView(self.InstancesGrid)
       
   415         self.TasksGrid.RefreshButtons()
       
   416         self.InstancesGrid.RefreshButtons()
       
   417         
       
   418     def OnTasksGridCellChange(self, event):
       
   419         row, col = event.GetRow(), event.GetCol()
       
   420         if self.TasksTable.GetColLabelValue(col) == "Name":
       
   421             tasklist = [name for name in self.TaskList.split(",") if name != ""]
       
   422             for i in xrange(self.TasksTable.GetNumberRows()):
       
   423                 task = self.TasksTable.GetValueByName(i, "Name")
       
   424                 if task in tasklist:
       
   425                     tasklist.remove(task)
       
   426             if len(tasklist) > 0:
       
   427                 old_name = tasklist[0]
       
   428                 new_name = self.TasksTable.GetValue(row, col)
       
   429                 for i in xrange(self.InstancesTable.GetNumberRows()):
       
   430                     if self.InstancesTable.GetValueByName(i, "Task") == old_name:
       
   431                         self.InstancesTable.SetValueByName(i, "Task", new_name)
       
   432         self.RefreshModel()
       
   433         colname = self.TasksTable.GetColLabelValue(col, False)
       
   434         if colname in ["Triggering", "Name"]:
       
   435             wx.CallAfter(self.RefreshView, False)
       
   436         event.Skip()
       
   437 
       
   438     def OnInstancesGridCellChange(self, event):
       
   439         self.RefreshModel()
       
   440         self.ParentWindow.RefreshPouInstanceVariablesPanel()
       
   441         self.InstancesGrid.RefreshButtons()
       
   442         event.Skip()
       
   443 
       
   444 #-------------------------------------------------------------------------------
       
   445 #                        Highlights showing functions
       
   446 #-------------------------------------------------------------------------------
       
   447     
       
   448     def OnRefreshHighlightsTimer(self, event):
       
   449         self.RefreshView()
       
   450         event.Skip()
       
   451     
       
   452     def AddHighlight(self, infos, start, end, highlight_type):
       
   453         if infos[0] == "task":
       
   454             self.TasksTable.AddHighlight(infos[1:], highlight_type)
       
   455         elif infos[0] == "instance":
       
   456             self.InstancesTable.AddHighlight(infos[1:], highlight_type)
       
   457         self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
       
   458 
       
   459     def ClearHighlights(self, highlight_type=None):
       
   460         EditorPanel.ClearHighlights(self, highlight_type)
       
   461         
       
   462         self.TasksTable.ClearHighlights(highlight_type)
       
   463         self.InstancesTable.ClearHighlights(highlight_type)
       
   464         self.TasksTable.ResetView(self.TasksGrid)
       
   465         self.InstancesTable.ResetView(self.InstancesGrid)