Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: Laurent@814: #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor Laurent@814: #based on the plcopen standard. Laurent@814: # Laurent@814: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD Laurent@814: # Laurent@814: #See COPYING file for copyrights details. Laurent@814: # Laurent@814: #This library is free software; you can redistribute it and/or Laurent@814: #modify it under the terms of the GNU General Public Laurent@814: #License as published by the Free Software Foundation; either Laurent@814: #version 2.1 of the License, or (at your option) any later version. Laurent@814: # Laurent@814: #This library is distributed in the hope that it will be useful, Laurent@814: #but WITHOUT ANY WARRANTY; without even the implied warranty of Laurent@814: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Laurent@814: #General Public License for more details. Laurent@814: # Laurent@814: #You should have received a copy of the GNU General Public Laurent@814: #License along with this library; if not, write to the Free Software Laurent@814: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Laurent@814: Laurent@1236: import re Laurent@1236: Laurent@814: import wx Laurent@814: Laurent@1236: from graphics.FBD_Objects import FBD_Block Laurent@814: from controls.LibraryPanel import LibraryPanel Laurent@1236: from BlockPreviewDialog import BlockPreviewDialog Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@1244: # Helpers Laurent@1244: #------------------------------------------------------------------------------- Laurent@1244: Laurent@1244: def GetBlockTypeDefaultNameModel(blocktype): Laurent@1244: return re.compile("%s[0-9]+" % blocktype if blocktype is not None else ".*") Laurent@1244: Laurent@1244: #------------------------------------------------------------------------------- Laurent@1242: # Set Block Parameters Dialog Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@1242: """ Laurent@1242: Class that implements a dialog for defining parameters of a FBD block graphic Laurent@1242: element Laurent@1242: """ Laurent@1242: Laurent@1236: class FBDBlockDialog(BlockPreviewDialog): Laurent@1236: Laurent@1236: def __init__(self, parent, controller, tagname): Laurent@1242: """ Laurent@1242: Constructor Laurent@1242: @param parent: Parent wx.Window of dialog for modal Laurent@1242: @param controller: Reference to project controller Laurent@1242: @param tagname: Tagname of project POU edited Laurent@1242: """ Laurent@1236: BlockPreviewDialog.__init__(self, parent, controller, tagname, Laurent@814: size=wx.Size(600, 450), title=_('Block Properties')) Laurent@814: Laurent@1250: # Init common sizers Laurent@1250: self._init_sizers(2, 0, 1, 0, 3, 2) Laurent@814: Laurent@1242: # Create static box around library panel Laurent@814: type_staticbox = wx.StaticBox(self, label=_('Type:')) Laurent@814: left_staticboxsizer = wx.StaticBoxSizer(type_staticbox, wx.VERTICAL) Laurent@1250: self.LeftGridSizer.AddSizer(left_staticboxsizer, border=5, flag=wx.GROW) Laurent@814: Laurent@1242: # Create Library panel and add it to static box Laurent@814: self.LibraryPanel = LibraryPanel(self) Laurent@1242: # Set function to call when selection in Library panel changed Laurent@814: setattr(self.LibraryPanel, "_OnTreeItemSelected", Laurent@814: self.OnLibraryTreeItemSelected) Laurent@814: left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, Laurent@814: flag=wx.GROW|wx.TOP) Laurent@814: Laurent@1242: # Create sizer for other block parameters Laurent@814: top_right_gridsizer = wx.FlexGridSizer(cols=2, hgap=0, rows=4, vgap=5) Laurent@814: top_right_gridsizer.AddGrowableCol(1) Laurent@1250: self.RightGridSizer.AddSizer(top_right_gridsizer, flag=wx.GROW) Laurent@814: Laurent@1242: # Create label for block name Laurent@814: name_label = wx.StaticText(self, label=_('Name:')) Laurent@814: top_right_gridsizer.AddWindow(name_label, Laurent@814: flag=wx.ALIGN_CENTER_VERTICAL) Laurent@814: Laurent@1242: # Create text control for defining block name Laurent@814: self.BlockName = wx.TextCtrl(self) Laurent@814: self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.BlockName) Laurent@814: top_right_gridsizer.AddWindow(self.BlockName, flag=wx.GROW) Laurent@814: Laurent@1242: # Create label for extended block input number Laurent@814: inputs_label = wx.StaticText(self, label=_('Inputs:')) Laurent@814: top_right_gridsizer.AddWindow(inputs_label, Laurent@814: flag=wx.ALIGN_CENTER_VERTICAL) Laurent@814: Laurent@1242: # Create spin control for defining extended block input number Laurent@814: self.Inputs = wx.SpinCtrl(self, min=2, max=20, Laurent@814: style=wx.SP_ARROW_KEYS) Laurent@814: self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, self.Inputs) Laurent@814: top_right_gridsizer.AddWindow(self.Inputs, flag=wx.GROW) Laurent@814: Laurent@1242: # Create label for block execution order Laurent@1242: execution_order_label = wx.StaticText(self, Laurent@1242: label=_('Execution Order:')) Laurent@814: top_right_gridsizer.AddWindow(execution_order_label, Laurent@814: flag=wx.ALIGN_CENTER_VERTICAL) Laurent@814: Laurent@1242: # Create spin control for defining block execution order Laurent@814: self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) Laurent@1242: self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, Laurent@1242: self.ExecutionOrder) Laurent@814: top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) Laurent@814: Laurent@1242: # Create label for block execution control Laurent@1242: execution_control_label = wx.StaticText(self, Laurent@1242: label=_('Execution Control:')) Laurent@814: top_right_gridsizer.AddWindow(execution_control_label, Laurent@814: flag=wx.ALIGN_CENTER_VERTICAL) Laurent@814: Laurent@1242: # Create check box to enable block execution control Laurent@814: self.ExecutionControl = wx.CheckBox(self) Laurent@1242: self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, Laurent@1242: self.ExecutionControl) Laurent@814: top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW) Laurent@814: Laurent@1242: # Add preview panel and associated label to sizers Laurent@1250: self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) Laurent@1250: self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) Laurent@814: Laurent@1244: # Add buttons sizer to sizers Laurent@1250: self.MainSizer.AddSizer(self.ButtonSizer, border=20, Laurent@814: flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) Laurent@814: Laurent@1244: # Dictionary containing correspondence between parameter exchanged and Laurent@1244: # control to fill with parameter value Laurent@1242: self.ParamsControl = { Laurent@1242: "extension": self.Inputs, Laurent@1242: "executionOrder": self.ExecutionOrder, Laurent@1242: "executionControl": self.ExecutionControl Laurent@1242: } Laurent@1242: Laurent@1242: # Init controls value and sensibility Laurent@814: self.BlockName.SetValue("") Laurent@814: self.BlockName.Enable(False) Laurent@814: self.Inputs.Enable(False) Laurent@1242: Laurent@1242: # Variable containing last name typed Laurent@1236: self.CurrentBlockName = None Laurent@1236: Laurent@1242: # Refresh Library panel values Laurent@1236: self.LibraryPanel.SetBlockList(controller.GetBlockTypes(tagname)) Laurent@814: self.LibraryPanel.SetFocus() Laurent@814: Laurent@814: def SetValues(self, values): Laurent@1242: """ Laurent@1242: Set default block parameters Laurent@1242: @param values: Block parameters values Laurent@1242: """ Laurent@1242: # Extract block type defined in parameters Laurent@814: blocktype = values.get("type", None) Laurent@1242: Laurent@1242: # Define regular expression for determine if block name is block Laurent@1242: # default name Laurent@1244: default_name_model = GetBlockTypeDefaultNameModel(blocktype) Laurent@1242: Laurent@1242: # For each parameters defined, set corresponding control value Laurent@814: for name, value in values.items(): Laurent@1244: Laurent@1244: # Parameter is block name Laurent@814: if name == "name": Laurent@1237: if value != "": Laurent@1244: # Set default graphic element name for testing Laurent@1244: self.DefaultElementName = value Laurent@1242: Laurent@1242: # Test if block name is type default block name and save Laurent@1242: # block name if not (name have been typed by user) Laurent@1237: if default_name_model.match(value) is None: Laurent@1237: self.CurrentBlockName = value Laurent@1242: Laurent@1236: self.BlockName.ChangeValue(value) Laurent@1242: Laurent@1244: # Set value of other controls Laurent@1242: else: Laurent@1242: control = self.ParamsControl.get(name, None) Laurent@1242: if control is not None: Laurent@1242: control.SetValue(value) Laurent@1242: Laurent@1244: # Select block type in library panel Laurent@1244: if blocktype is not None: Laurent@1244: self.LibraryPanel.SelectTreeItem(blocktype, Laurent@1244: values.get("inputs", None)) Laurent@1244: Laurent@1242: # Refresh preview panel Laurent@814: self.RefreshPreview() Laurent@814: Laurent@814: def GetValues(self): Laurent@1242: """ Laurent@1242: Return block parameters defined in dialog Laurent@1242: @return: {parameter_name: parameter_value,...} Laurent@1242: """ Laurent@814: values = self.LibraryPanel.GetSelectedBlock() Laurent@814: if self.BlockName.IsEnabled() and self.BlockName.GetValue() != "": Laurent@814: values["name"] = self.BlockName.GetValue() Laurent@1244: values["width"], values["height"] = self.Element.GetSize() Laurent@1242: values.update({ Laurent@1242: name: control.GetValue() Laurent@1242: for name, control in self.ParamsControl.iteritems()}) Laurent@814: return values Laurent@1250: Laurent@1250: def OnOK(self, event): Laurent@1250: """ Laurent@1250: Called when dialog OK button is pressed Laurent@1250: Test if parameters defined are valid Laurent@1250: @param event: wx.Event from OK button Laurent@1250: """ Laurent@1250: message = None Laurent@1250: Laurent@1250: # Get block type selected Laurent@1250: selected = self.LibraryPanel.GetSelectedBlock() Laurent@1250: Laurent@1250: # Get block type name and if block is a function block Laurent@1250: block_name = self.BlockName.GetValue() Laurent@1250: name_enabled = self.BlockName.IsEnabled() Laurent@1250: Laurent@1250: # Test that a type has been selected for block Laurent@1250: if selected is None: Laurent@1250: message = _("Form isn't complete. Valid block type must be selected!") Laurent@1250: Laurent@1250: # Test, if block is a function block, that a name have been defined Laurent@1250: elif name_enabled and block_name == "": Laurent@1250: message = _("Form isn't complete. Name must be filled!") Laurent@1250: Laurent@1250: # Show error message if an error is detected Laurent@1250: if message is not None: Laurent@1250: self.ShowErrorMessage(message) Laurent@1250: Laurent@1250: # Test block name validity if necessary Laurent@1250: elif not name_enabled or self.TestElementName(block_name): Laurent@1250: # Call BlockPreviewDialog function Laurent@1250: BlockPreviewDialog.OnOK(self, event) Laurent@1250: Laurent@814: def OnLibraryTreeItemSelected(self, event): Laurent@1242: """ Laurent@1242: Called when block type selected in library panel Laurent@1242: @param event: wx.TreeEvent Laurent@1242: """ Laurent@1242: # Get type selected in library panel Laurent@814: values = self.LibraryPanel.GetSelectedBlock() Laurent@1242: Laurent@1242: # Get block type informations Laurent@1236: blocktype = (self.Controller.GetBlockType(values["type"], Laurent@1236: values["inputs"]) Laurent@1236: if values is not None else None) Laurent@1236: Laurent@1242: # Set input number spin control according to block type informations Laurent@814: if blocktype is not None: Laurent@814: self.Inputs.SetValue(len(blocktype["inputs"])) Laurent@814: self.Inputs.Enable(blocktype["extensible"]) Laurent@1236: else: Laurent@1236: self.Inputs.SetValue(2) Laurent@1236: self.Inputs.Enable(False) Laurent@1236: Laurent@1242: # Update block name with default value if block type is a function and Laurent@1242: # current block name wasn't typed by user Laurent@1236: if blocktype is not None and blocktype["type"] != "function": Laurent@1236: self.BlockName.Enable(True) Laurent@1244: Laurent@1244: if self.CurrentBlockName is None: Laurent@1244: # Generate new block name according to block type, taking Laurent@1244: # default element name if it was already a default name for this Laurent@1244: # block type Laurent@1244: default_name_model = GetBlockTypeDefaultNameModel(values["type"]) Laurent@1244: block_name = ( Laurent@1244: self.DefaultElementName Laurent@1244: if (self.DefaultElementName is not None and Laurent@1244: default_name_model.match(self.DefaultElementName)) Laurent@1244: else self.Controller.GenerateNewName( Laurent@1244: self.TagName, None, values["type"]+"%d", 0)) Laurent@1244: else: Laurent@1244: block_name = self.CurrentBlockName Laurent@1244: Laurent@1244: self.BlockName.ChangeValue(block_name) Laurent@814: else: Laurent@814: self.BlockName.Enable(False) Laurent@1236: self.BlockName.ChangeValue("") Laurent@1236: Laurent@1242: # Refresh preview panel Laurent@1236: self.RefreshPreview() Laurent@814: Laurent@814: def OnNameChanged(self, event): Laurent@1242: """ Laurent@1242: Called when block name value changed Laurent@1242: @param event: wx.TextEvent Laurent@1242: """ Laurent@814: if self.BlockName.IsEnabled(): Laurent@1242: # Save block name typed by user Laurent@1236: self.CurrentBlockName = self.BlockName.GetValue() Laurent@814: self.RefreshPreview() Laurent@814: event.Skip() Laurent@814: Laurent@814: def OnInputsChanged(self, event): Laurent@1242: """ Laurent@1242: Called when block inputs number changed Laurent@1242: @param event: wx.SpinEvent Laurent@1242: """ Laurent@814: if self.Inputs.IsEnabled(): Laurent@814: self.RefreshPreview() Laurent@814: event.Skip() Laurent@814: Laurent@814: def OnExecutionOrderChanged(self, event): Laurent@1242: """ Laurent@1242: Called when block execution order value changed Laurent@1242: @param event: wx.SpinEvent Laurent@1242: """ Laurent@814: self.RefreshPreview() Laurent@814: event.Skip() Laurent@814: Laurent@814: def OnExecutionControlChanged(self, event): Laurent@1242: """ Laurent@1242: Called when block execution control value changed Laurent@1242: @param event: wx.SpinEvent Laurent@1242: """ Laurent@814: self.RefreshPreview() Laurent@814: event.Skip() Laurent@814: Laurent@814: def RefreshPreview(self): Laurent@1242: """ Laurent@1242: Refresh preview panel of graphic element Laurent@1242: Override BlockPreviewDialog function Laurent@1242: """ Laurent@1242: # Get type selected in library panel Laurent@814: values = self.LibraryPanel.GetSelectedBlock() Laurent@1242: Laurent@1242: # If a block type is selected in library panel Laurent@814: if values is not None: Laurent@1242: # Set graphic element displayed, creating a FBD block element Laurent@1244: self.Element = FBD_Block(self.Preview, values["type"], Laurent@1244: (self.BlockName.GetValue() Laurent@1244: if self.BlockName.IsEnabled() Laurent@1244: else ""), Laurent@814: extension = self.Inputs.GetValue(), Laurent@814: inputs = values["inputs"], Laurent@814: executionControl = self.ExecutionControl.GetValue(), Laurent@814: executionOrder = self.ExecutionOrder.GetValue()) Laurent@1242: Laurent@1242: # Reset graphic element displayed Laurent@814: else: Laurent@1244: self.Element = None Laurent@1242: Laurent@1242: # Call BlockPreviewDialog function Laurent@1236: BlockPreviewDialog.RefreshPreview(self)