Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1571: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1696: # Copyright (C) 2017: Andrey Skvortsov andrej@1571: # andrej@1571: # See COPYING file for copyrights details. andrej@1571: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. andrej@1571: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. andrej@1571: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: andrej@1853: andrej@1853: from __future__ import absolute_import 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 andrej@1853: from dialogs.BlockPreviewDialog import BlockPreviewDialog Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@1244: # Helpers andrej@1782: # ------------------------------------------------------------------------------- Laurent@1244: andrej@1736: Laurent@1244: def GetBlockTypeDefaultNameModel(blocktype): Laurent@1244: return re.compile("%s[0-9]+" % blocktype if blocktype is not None else ".*") Laurent@1244: andrej@1782: # ------------------------------------------------------------------------------- Laurent@1242: # Set Block Parameters Dialog andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@1242: Laurent@1236: class FBDBlockDialog(BlockPreviewDialog): andrej@1736: """ andrej@1736: Class that implements a dialog for defining parameters of a FBD block graphic andrej@1736: element andrej@1736: """ andrej@1730: 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, andrej@1768: title=_('Block Properties')) andrej@1730: Laurent@1250: # Init common sizers Laurent@1250: self._init_sizers(2, 0, 1, 0, 3, 2) andrej@1730: 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) andrej@1730: Laurent@1242: # Create Library panel and add it to static box Laurent@814: self.LibraryPanel = LibraryPanel(self) andrej@1696: self.LibraryPanel.SetInitialSize(wx.Size(-1, 400)) andrej@1730: Laurent@1242: # Set function to call when selection in Library panel changed andrej@1730: setattr(self.LibraryPanel, "_OnTreeItemSelected", andrej@1768: self.OnLibraryTreeItemSelected) andrej@1730: left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, andrej@1768: flag=wx.GROW | wx.TOP) andrej@1730: 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) andrej@1730: Laurent@1242: # Create label for block name Laurent@814: name_label = wx.StaticText(self, label=_('Name:')) andrej@1730: top_right_gridsizer.AddWindow(name_label, andrej@1768: flag=wx.ALIGN_CENTER_VERTICAL) andrej@1730: 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) andrej@1730: Laurent@1242: # Create label for extended block input number Laurent@814: inputs_label = wx.StaticText(self, label=_('Inputs:')) andrej@1730: top_right_gridsizer.AddWindow(inputs_label, andrej@1768: flag=wx.ALIGN_CENTER_VERTICAL) andrej@1730: Laurent@1242: # Create spin control for defining extended block input number Laurent@814: self.Inputs = wx.SpinCtrl(self, min=2, max=20, andrej@1768: 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) andrej@1730: Laurent@1242: # Create label for block execution order andrej@1730: execution_order_label = wx.StaticText(self, andrej@1768: label=_('Execution Order:')) andrej@1730: top_right_gridsizer.AddWindow(execution_order_label, andrej@1768: flag=wx.ALIGN_CENTER_VERTICAL) andrej@1730: Laurent@1242: # Create spin control for defining block execution order Laurent@814: self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) andrej@1730: self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, Laurent@1242: self.ExecutionOrder) Laurent@814: top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) andrej@1730: Laurent@1242: # Create label for block execution control andrej@1730: execution_control_label = wx.StaticText(self, andrej@1768: label=_('Execution Control:')) andrej@1730: top_right_gridsizer.AddWindow(execution_control_label, andrej@1768: flag=wx.ALIGN_CENTER_VERTICAL) andrej@1730: Laurent@1242: # Create check box to enable block execution control Laurent@814: self.ExecutionControl = wx.CheckBox(self) andrej@1730: self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, Laurent@1242: self.ExecutionControl) Laurent@814: top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW) andrej@1730: 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) andrej@1730: Laurent@1244: # Add buttons sizer to sizers andrej@1730: self.MainSizer.AddSizer(self.ButtonSizer, border=20, andrej@1768: flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) andrej@1730: 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: } andrej@1730: Laurent@1242: # Init controls value and sensibility Laurent@814: self.BlockName.SetValue("") Laurent@814: self.BlockName.Enable(False) Laurent@814: self.Inputs.Enable(False) andrej@1730: Laurent@1242: # Variable containing last name typed Laurent@1236: self.CurrentBlockName = None andrej@1730: Laurent@1242: # Refresh Library panel values Laurent@1236: self.LibraryPanel.SetBlockList(controller.GetBlockTypes(tagname)) andrej@1696: self.Fit() Laurent@814: self.LibraryPanel.SetFocus() andrej@1730: 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) andrej@1730: andrej@1730: # Select block type in library panel Laurent@1327: if blocktype is not None: andrej@1730: self.LibraryPanel.SelectTreeItem(blocktype, Laurent@1327: values.get("inputs", None)) andrej@1730: Laurent@1242: # Define regular expression for determine if block name is block Laurent@1242: # default name Laurent@1244: default_name_model = GetBlockTypeDefaultNameModel(blocktype) andrej@1730: Laurent@1242: # For each parameters defined, set corresponding control value Laurent@814: for name, value in values.items(): andrej@1730: 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 andrej@1730: 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 andrej@1730: Laurent@1236: self.BlockName.ChangeValue(value) andrej@1730: 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) andrej@1730: Laurent@1242: # Refresh preview panel Edouard@2591: 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 andrej@1730: 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 andrej@1730: Laurent@1250: # Get block type selected Laurent@1250: selected = self.LibraryPanel.GetSelectedBlock() andrej@1730: 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() andrej@1730: 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!") andrej@1730: 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!") andrej@1730: Laurent@1250: # Show error message if an error is detected Laurent@1250: if message is not None: Laurent@1250: self.ShowErrorMessage(message) andrej@1730: 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) andrej@1730: 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() andrej@1730: Laurent@1242: # Get block type informations andrej@1730: blocktype = (self.Controller.GetBlockType(values["type"], Laurent@1236: values["inputs"]) Laurent@1236: if values is not None else None) andrej@1730: 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) andrej@1730: 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) andrej@1730: 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 andrej@1730: 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 andrej@1730: Laurent@1244: self.BlockName.ChangeValue(block_name) Laurent@814: else: Laurent@814: self.BlockName.Enable(False) Laurent@1236: self.BlockName.ChangeValue("") andrej@1730: Laurent@1242: # Refresh preview panel Edouard@2591: self.RefreshPreview() andrej@1730: 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() Edouard@2591: self.RefreshPreview() Laurent@814: event.Skip() andrej@1730: 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(): Edouard@2591: self.RefreshPreview() Laurent@814: event.Skip() andrej@1730: 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: """ Edouard@2591: self.RefreshPreview() Laurent@814: event.Skip() andrej@1730: 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: """ Edouard@2591: self.RefreshPreview() Laurent@814: event.Skip() andrej@1730: Edouard@2587: def DrawPreview(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() andrej@1730: 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 andrej@1768: self.Element = FBD_Block( andrej@1768: self.Preview, values["type"], andrej@1768: (self.BlockName.GetValue() if self.BlockName.IsEnabled() else ""), andrej@1768: extension=self.Inputs.GetValue(), andrej@1768: inputs=values["inputs"], andrej@1768: executionControl=self.ExecutionControl.GetValue(), andrej@1768: executionOrder=self.ExecutionOrder.GetValue()) andrej@1730: Laurent@1242: # Reset graphic element displayed Laurent@814: else: andrej@1730: self.Element = None andrej@1730: Laurent@1242: # Call BlockPreviewDialog function Edouard@2587: BlockPreviewDialog.DrawPreview(self)