diff -r 1460273f40ed -r 5743cbdff669 editors/ResourceEditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/editors/ResourceEditor.py Fri Sep 07 16:45:55 2012 +0200 @@ -0,0 +1,465 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import wx +import wx.lib.buttons +import wx.grid + +from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD +from controls import CustomGrid, CustomTable, DurationCellEditor +from EditorPanel import EditorPanel +from util.BitmapLibrary import GetBitmap + +#------------------------------------------------------------------------------- +# Configuration Editor class +#------------------------------------------------------------------------------- + +[ID_CONFIGURATIONEDITOR, +] = [wx.NewId() for _init_ctrls in range(1)] + +class ConfigurationEditor(EditorPanel): + + ID = ID_CONFIGURATIONEDITOR + VARIABLE_PANEL_TYPE = "config" + + def GetBufferState(self): + return self.Controler.GetBufferState() + + def Undo(self): + self.Controler.LoadPrevious() + self.ParentWindow.CloseTabsWithoutModel() + + def Redo(self): + self.Controler.LoadNext() + self.ParentWindow.CloseTabsWithoutModel() + + def HasNoModel(self): + return self.Controler.GetEditedElement(self.TagName) is None + + +#------------------------------------------------------------------------------- +# Resource Editor class +#------------------------------------------------------------------------------- + +def GetTasksTableColnames(): + _ = lambda x : x + return [_("Name"), _("Triggering"), _("Single"), _("Interval"), _("Priority")] + +def GetTaskTriggeringOptions(): + _ = lambda x : x + return [_("Interrupt"), _("Cyclic")] +TASKTRIGGERINGOPTIONS_DICT = dict([(_(option), option) for option in GetTaskTriggeringOptions()]) + +def GetInstancesTableColnames(): + _ = lambda x : x + return [_("Name"), _("Type"), _("Task")] + +class ResourceTable(CustomTable): + + """ + A custom wx.grid.Grid Table using user supplied data + """ + def __init__(self, parent, data, colnames): + # The base class must be initialized *first* + CustomTable.__init__(self, parent, data, colnames) + self.ColAlignements = [] + self.ColSizes = [] + + def GetColAlignements(self): + return self.ColAlignements + + def SetColAlignements(self, list): + self.ColAlignements = list + + def GetColSizes(self): + return self.ColSizes + + def SetColSizes(self, list): + self.ColSizes = list + + def GetValue(self, row, col): + if row < self.GetNumberRows(): + colname = self.GetColLabelValue(col, False) + value = str(self.data[row].get(colname, "")) + if colname == "Triggering": + return _(value) + return value + + def SetValue(self, row, col, value): + if col < len(self.colnames): + colname = self.GetColLabelValue(col, False) + if colname == "Triggering": + value = TASKTRIGGERINGOPTIONS_DICT[value] + self.data[row][colname] = value + + def _updateColAttrs(self, grid): + """ + wx.grid.Grid -> update the column attributes to add the + appropriate renderer given the column name. + + Otherwise default to the default renderer. + """ + + for col in range(self.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE) + grid.SetColAttr(col, attr) + grid.SetColSize(col, self.ColSizes[col]) + + for row in range(self.GetNumberRows()): + row_highlights = self.Highlights.get(row, {}) + for col in range(self.GetNumberCols()): + editor = None + renderer = None + colname = self.GetColLabelValue(col, False) + grid.SetReadOnly(row, col, False) + if colname == "Name": + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + elif colname == "Interval": + editor = DurationCellEditor(self) + renderer = wx.grid.GridCellStringRenderer() + if self.GetValueByName(row, "Triggering") != "Cyclic": + grid.SetReadOnly(row, col, True) + elif colname == "Single": + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(self.Parent.VariableList) + if self.GetValueByName(row, "Triggering") != "Interrupt": + grid.SetReadOnly(row, col, True) + elif colname == "Triggering": + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(",".join([""] + map(_, GetTaskTriggeringOptions()))) + elif colname == "Type": + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(self.Parent.TypeList) + elif colname == "Priority": + editor = wx.grid.GridCellNumberEditor() + editor.SetParameters("0,65535") + elif colname == "Task": + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(self.Parent.TaskList) + + grid.SetCellEditor(row, col, editor) + grid.SetCellRenderer(row, col, renderer) + + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] + grid.SetCellBackgroundColour(row, col, highlight_colours[0]) + grid.SetCellTextColour(row, col, highlight_colours[1]) + self.ResizeRow(grid, row) + + +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def AddHighlight(self, infos, highlight_type): + row_highlights = self.Highlights.setdefault(infos[0], {}) + col_highlights = row_highlights.setdefault(infos[1], []) + col_highlights.append(highlight_type) + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + for row, row_highlights in self.Highlights.iteritems(): + row_items = row_highlights.items() + for col, col_highlights in row_items: + if highlight_type in col_highlights: + col_highlights.remove(highlight_type) + if len(col_highlights) == 0: + row_highlights.pop(col) + + + +class ResourceEditor(EditorPanel): + + VARIABLE_PANEL_TYPE = "resource" + + def _init_Editor(self, parent): + self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER|wx.TAB_TRAVERSAL) + + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) + main_sizer.AddGrowableCol(0) + main_sizer.AddGrowableRow(0) + main_sizer.AddGrowableRow(1) + + tasks_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) + tasks_sizer.AddGrowableCol(0) + tasks_sizer.AddGrowableRow(1) + main_sizer.AddSizer(tasks_sizer, border=5, + flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + + tasks_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) + tasks_buttons_sizer.AddGrowableCol(0) + tasks_buttons_sizer.AddGrowableRow(0) + tasks_sizer.AddSizer(tasks_buttons_sizer, flag=wx.GROW) + + tasks_label = wx.StaticText(self.Editor, label=_(u'Tasks:')) + tasks_buttons_sizer.AddWindow(tasks_label, flag=wx.ALIGN_BOTTOM) + + for name, bitmap, help in [ + ("AddTaskButton", "add_element", _("Add task")), + ("DeleteTaskButton", "remove_element", _("Remove task")), + ("UpTaskButton", "up", _("Move task up")), + ("DownTaskButton", "down", _("Move task down"))]: + button = wx.lib.buttons.GenBitmapButton(self.Editor, + bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + button.SetToolTipString(help) + setattr(self, name, button) + tasks_buttons_sizer.AddWindow(button) + + self.TasksGrid = CustomGrid(self.Editor, style=wx.VSCROLL) + self.TasksGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnTasksGridCellChange) + tasks_sizer.AddWindow(self.TasksGrid, flag=wx.GROW) + + instances_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) + instances_sizer.AddGrowableCol(0) + instances_sizer.AddGrowableRow(1) + main_sizer.AddSizer(instances_sizer, border=5, + flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + + instances_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) + instances_buttons_sizer.AddGrowableCol(0) + instances_buttons_sizer.AddGrowableRow(0) + instances_sizer.AddSizer(instances_buttons_sizer, flag=wx.GROW) + + instances_label = wx.StaticText(self.Editor, label=_(u'Instances:')) + instances_buttons_sizer.AddWindow(instances_label, flag=wx.ALIGN_BOTTOM) + + for name, bitmap, help in [ + ("AddInstanceButton", "add_element", _("Add instance")), + ("DeleteInstanceButton", "remove_element", _("Remove instance")), + ("UpInstanceButton", "up", _("Move instance up")), + ("DownInstanceButton", "down", _("Move instance down"))]: + button = wx.lib.buttons.GenBitmapButton(self.Editor, + bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + button.SetToolTipString(help) + setattr(self, name, button) + instances_buttons_sizer.AddWindow(button) + + self.InstancesGrid = CustomGrid(self.Editor, style=wx.VSCROLL) + self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.OnInstancesGridCellChange) + instances_sizer.AddWindow(self.InstancesGrid, flag=wx.GROW) + + self.Editor.SetSizer(main_sizer) + + def __init__(self, parent, tagname, window, controler): + EditorPanel.__init__(self, parent, tagname, window, controler) + + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + + self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0} + self.TasksTable = ResourceTable(self, [], GetTasksTableColnames()) + self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT]) + self.TasksTable.SetColSizes([200, 100, 100, 150, 100]) + self.TasksGrid.SetTable(self.TasksTable) + self.TasksGrid.SetButtons({"Add": self.AddTaskButton, + "Delete": self.DeleteTaskButton, + "Up": self.UpTaskButton, + "Down": self.DownTaskButton}) + + def _AddTask(new_row): + self.TasksTable.InsertRow(new_row, self.TasksDefaultValue.copy()) + self.RefreshModel() + self.RefreshView() + return new_row + setattr(self.TasksGrid, "_AddRow", _AddTask) + + def _DeleteTask(row): + self.TasksTable.RemoveRow(row) + self.RefreshModel() + self.RefreshView() + setattr(self.TasksGrid, "_DeleteRow", _DeleteTask) + + def _MoveTask(row, move): + new_row = self.TasksTable.MoveRow(row, move) + if new_row != row: + self.RefreshModel() + self.RefreshView() + return new_row + setattr(self.TasksGrid, "_MoveRow", _MoveTask) + + self.TasksGrid.SetRowLabelSize(0) + self.TasksTable.ResetView(self.TasksGrid) + self.TasksGrid.RefreshButtons() + + self.InstancesDefaultValue = {"Name" : "", "Type" : "", "Task" : ""} + self.InstancesTable = ResourceTable(self, [], GetInstancesTableColnames()) + self.InstancesTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]) + self.InstancesTable.SetColSizes([200, 150, 150]) + self.InstancesGrid.SetTable(self.InstancesTable) + self.InstancesGrid.SetButtons({"Add": self.AddInstanceButton, + "Delete": self.DeleteInstanceButton, + "Up": self.UpInstanceButton, + "Down": self.DownInstanceButton}) + + def _AddInstance(new_row): + self.InstancesTable.InsertRow(new_row, self.InstancesDefaultValue.copy()) + self.RefreshModel() + self.RefreshView() + return new_row + setattr(self.InstancesGrid, "_AddRow", _AddInstance) + + def _DeleteInstance(row): + self.InstancesTable.RemoveRow(row) + self.RefreshModel() + self.RefreshView() + setattr(self.InstancesGrid, "_DeleteRow", _DeleteInstance) + + def _MoveInstance(row, move): + new_row = max(0, min(row + move, self.InstancesTable.GetNumberRows() - 1)) + if new_row != row: + if self.InstancesTable.GetValueByName(row, "Task") != self.InstancesTable.GetValueByName(new_row, "Task"): + return row + self.InstancesTable.MoveRow(row, move) + self.RefreshModel() + self.RefreshView() + return new_row + setattr(self.InstancesGrid, "_MoveRow", _MoveInstance) + + def _RefreshInstanceButtons(): + if self: + rows = self.InstancesTable.GetNumberRows() + row = self.InstancesGrid.GetGridCursorRow() + self.DeleteInstanceButton.Enable(rows > 0) + self.UpInstanceButton.Enable(row > 0 and + self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row - 1, "Task")) + self.DownInstanceButton.Enable(0 <= row < rows - 1 and + self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row + 1, "Task")) + setattr(self.InstancesGrid, "RefreshButtons", _RefreshInstanceButtons) + + self.InstancesGrid.SetRowLabelSize(0) + self.InstancesTable.ResetView(self.InstancesGrid) + self.InstancesGrid.RefreshButtons() + + self.TasksGrid.SetFocus() + + def __del__(self): + self.RefreshHighlightsTimer.Stop() + + def RefreshTypeList(self): + self.TypeList = "" + blocktypes = self.Controler.GetBlockResource() + for blocktype in blocktypes: + self.TypeList += ",%s"%blocktype + + def RefreshTaskList(self): + self.TaskList = "" + for row in xrange(self.TasksTable.GetNumberRows()): + self.TaskList += ",%s"%self.TasksTable.GetValueByName(row, "Name") + + def RefreshVariableList(self): + self.VariableList = "" + for variable in self.Controler.GetEditedResourceVariables(self.TagName): + self.VariableList += ",%s"%variable + + def RefreshModel(self): + self.Controler.SetEditedResourceInfos(self.TagName, self.TasksTable.GetData(), self.InstancesTable.GetData()) + self.RefreshBuffer() + + # Buffer the last model state + def RefreshBuffer(self): + self.Controler.BufferProject() + self.ParentWindow.RefreshTitle() + self.ParentWindow.RefreshFileMenu() + self.ParentWindow.RefreshEditMenu() + + def GetBufferState(self): + return self.Controler.GetBufferState() + + def Undo(self): + self.Controler.LoadPrevious() + self.ParentWindow.CloseTabsWithoutModel() + + def Redo(self): + self.Controler.LoadNext() + self.ParentWindow.CloseTabsWithoutModel() + + def HasNoModel(self): + return self.Controler.GetEditedElement(self.TagName) is None + + def RefreshView(self, variablepanel=True): + EditorPanel.RefreshView(self, variablepanel) + + tasks, instances = self.Controler.GetEditedResourceInfos(self.TagName) + self.TasksTable.SetData(tasks) + self.InstancesTable.SetData(instances) + self.RefreshTypeList() + self.RefreshTaskList() + self.RefreshVariableList() + self.TasksTable.ResetView(self.TasksGrid) + self.InstancesTable.ResetView(self.InstancesGrid) + self.TasksGrid.RefreshButtons() + self.InstancesGrid.RefreshButtons() + + def OnTasksGridCellChange(self, event): + row, col = event.GetRow(), event.GetCol() + if self.TasksTable.GetColLabelValue(col) == "Name": + tasklist = [name for name in self.TaskList.split(",") if name != ""] + for i in xrange(self.TasksTable.GetNumberRows()): + task = self.TasksTable.GetValueByName(i, "Name") + if task in tasklist: + tasklist.remove(task) + if len(tasklist) > 0: + old_name = tasklist[0] + new_name = self.TasksTable.GetValue(row, col) + for i in xrange(self.InstancesTable.GetNumberRows()): + if self.InstancesTable.GetValueByName(i, "Task") == old_name: + self.InstancesTable.SetValueByName(i, "Task", new_name) + self.RefreshModel() + colname = self.TasksTable.GetColLabelValue(col, False) + if colname in ["Triggering", "Name"]: + wx.CallAfter(self.RefreshView, False) + event.Skip() + + def OnInstancesGridCellChange(self, event): + self.RefreshModel() + self.ParentWindow.RefreshPouInstanceVariablesPanel() + self.InstancesGrid.RefreshButtons() + event.Skip() + +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.RefreshView() + event.Skip() + + def AddHighlight(self, infos, start, end, highlight_type): + if infos[0] == "task": + self.TasksTable.AddHighlight(infos[1:], highlight_type) + elif infos[0] == "instance": + self.InstancesTable.AddHighlight(infos[1:], highlight_type) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ClearHighlights(self, highlight_type=None): + EditorPanel.ClearHighlights(self, highlight_type) + + self.TasksTable.ClearHighlights(highlight_type) + self.InstancesTable.ClearHighlights(highlight_type) + self.TasksTable.ResetView(self.TasksGrid) + self.InstancesTable.ResetView(self.InstancesGrid)