dialogs/FBDVariableDialog.py
author Laurent Bessard
Tue, 11 Jun 2013 12:37:50 +0200
changeset 1244 336d515096b1
parent 1187 be891953958f
child 1246 101625efb1c1
permissions -rw-r--r--
Rewrite FBDVariableDialog
#!/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

from graphics.GraphicCommons import INPUT, INOUT, OUTPUT
from graphics.FBD_Objects import FBD_Variable
from BlockPreviewDialog import BlockPreviewDialog

#-------------------------------------------------------------------------------
#                                    Helpers
#-------------------------------------------------------------------------------

# Dictionaries containing correspondence between variable block class and string
# to be shown in Class combo box in both sense
VARIABLE_CLASSES_DICT = {INPUT : _("Input"),
                         INOUT : _("InOut"),
                         OUTPUT : _("Output")}
VARIABLE_CLASSES_DICT_REVERSE = dict(
    [(value, key) for key, value in VARIABLE_CLASSES_DICT.iteritems()])

#-------------------------------------------------------------------------------
#                        Set Variable Parameters Dialog
#-------------------------------------------------------------------------------

"""
Class that implements a dialog for defining parameters of a FBD variable graphic
element
"""

class FBDVariableDialog(BlockPreviewDialog):

    def __init__(self, parent, controller, tagname):
        """
        Constructor
        @param parent: Parent wx.Window of dialog for modal
        @param controller: Reference to project controller
        @param tagname: Tagname of project POU edited
        """
        BlockPreviewDialog.__init__(self, parent, controller, tagname,
              size=wx.Size(400, 380), title=_('Variable Properties'))
        
        # Create dialog main sizer
        main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=10)
        main_sizer.AddGrowableCol(0)
        main_sizer.AddGrowableRow(2)
        
        # Create a sizer for dividing FBD variable parameters in two columns
        column_sizer = wx.BoxSizer(wx.HORIZONTAL)
        main_sizer.AddSizer(column_sizer, border=20, 
              flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
        
        # Create a sizer for left column
        left_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=5)
        left_gridsizer.AddGrowableCol(0)
        column_sizer.AddSizer(left_gridsizer, 1, border=5, 
              flag=wx.GROW|wx.RIGHT)
        
        # Create label for variable class
        class_label = wx.StaticText(self, label=_('Class:'))
        left_gridsizer.AddWindow(class_label, flag=wx.GROW)
        
        # Create a combo box for defining variable class
        self.Class = wx.ComboBox(self, style=wx.CB_READONLY)
        self.Bind(wx.EVT_COMBOBOX, self.OnClassChanged, self.Class)
        left_gridsizer.AddWindow(self.Class, flag=wx.GROW)
        
        # Create label for variable execution order
        execution_order_label = wx.StaticText(self, 
              label=_('Execution Order:'))
        left_gridsizer.AddWindow(execution_order_label, flag=wx.GROW)
        
        # Create spin control for defining variable execution order
        self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS)
        self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, 
                  self.ExecutionOrder)
        left_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW)
        
        # Create a sizer for right column
        right_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0)
        right_gridsizer.AddGrowableCol(0)
        right_gridsizer.AddGrowableRow(2)
        column_sizer.AddSizer(right_gridsizer, 1, border=5, 
              flag=wx.GROW|wx.LEFT)
        
        # Create label for variable expression
        name_label = wx.StaticText(self, label=_('Expression:'))
        right_gridsizer.AddWindow(name_label, border=5, flag=wx.GROW|wx.BOTTOM)
        
        # Create text control for defining variable expression
        self.Expression = wx.TextCtrl(self)
        self.Bind(wx.EVT_TEXT, self.OnExpressionChanged, self.Expression)
        right_gridsizer.AddWindow(self.Expression, flag=wx.GROW)
        
        # Create a list box to selected variable expression in the list of
        # variables defined in POU
        self.VariableName = wx.ListBox(self, size=wx.Size(0, 120), 
              style=wx.LB_SINGLE|wx.LB_SORT)
        self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName)
        right_gridsizer.AddWindow(self.VariableName, flag=wx.GROW)
        
        # Add preview panel and associated label to sizers
        main_sizer.AddWindow(self.PreviewLabel, border=20,
              flag=wx.GROW|wx.LEFT|wx.RIGHT)
        main_sizer.AddWindow(self.Preview, border=20,
              flag=wx.GROW|wx.LEFT|wx.RIGHT)
        
        # Add buttons sizer to sizers
        main_sizer.AddSizer(self.ButtonSizer, border=20, 
              flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
        
        self.SetSizer(main_sizer)
        
        # Set options that can be selected in class combo box
        for choice in VARIABLE_CLASSES_DICT.itervalues():
            self.Class.Append(choice)
        self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[INPUT])
        
        # Get list of variables defined in POU
        self.VarList = {
            var["Name"]: (var["Class"], var["Type"])
            for var in self.Controller.GetEditedElementInterfaceVars(
                                                        self.TagName)
            if var["Edit"]}
        
        # Add POU name to variable list if POU is a function 
        returntype = self.Controller.GetEditedElementInterfaceReturnType(
                                                            self.TagName)
        if returntype is not None:
            self.VarList[
                self.Controller.GetEditedElementName(self.TagName)] = \
                 ("Output", returntype)
        
        # Add POU name if POU is a transition
        words = tagname.split("::")
        if words[0] == "T":
            self.VarList[words[2]] = ("Output", "BOOL")
        
        # Refresh values in name list box
        self.RefreshNameList()
        
        # Class combo box is default control having keyboard focus
        self.Class.SetFocus()

    def RefreshNameList(self):
        """
        Called to refresh names in name list box
        """
        # Get variable class to select POU variable applicable
        var_class = VARIABLE_CLASSES_DICT_REVERSE[
                            self.Class.GetStringSelection()]
        
        # Refresh names in name list box by selecting variables in POU variables
        # list that can be applied to variable class
        self.VariableName.Clear()
        for name, (var_type, value_type) in self.VarList.iteritems():
            if var_type != "Input" or var_class == INPUT:
                self.VariableName.Append(name)
        
        # Get variable expression and select corresponding value in name list
        # box if it exists
        selected = self.Expression.GetValue()
        if (selected != "" and 
            self.VariableName.FindString(selected) != wx.NOT_FOUND):
            self.VariableName.SetStringSelection(selected)
        else:
            self.VariableName.SetSelection(wx.NOT_FOUND)
        
        # Disable name list box if no name present inside
        self.VariableName.Enable(self.VariableName.GetCount() > 0)
            
    def SetValues(self, values):
        """
        Set default variable parameters
        @param values: Variable parameters values
        """
        
        # Get class parameter value
        var_class = values.get("class", None)
        if var_class is not None:
            # Set class selected in class combo box
            self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[var_class])
            # Refresh names in name list box according to var class
            self.RefreshNameList()
        
        # For each parameters defined, set corresponding control value
        for name, value in values.items():
            
            # Parameter is variable expression
            if name == "expression":
                # Set expression text control value
                self.Expression.ChangeValue(value)
                # Select corresponding text in name list box if it exists
                if self.VariableName.FindString(value) != wx.NOT_FOUND:
                    self.VariableName.SetStringSelection(value)
                else:
                    self.VariableName.SetSelection(wx.NOT_FOUND)
            
            # Parameter is variable execution order
            elif name == "executionOrder":
                self.ExecutionOrder.SetValue(value)
        
        # Refresh preview panel
        self.RefreshPreview()
        
    def GetValues(self):
        """
        Return block parameters defined in dialog
        @return: {parameter_name: parameter_value,...}
        """
        expression = self.Expression.GetValue()
        values = {
            "class": VARIABLE_CLASSES_DICT_REVERSE[
                        self.Class.GetStringSelection()],
            "expression": expression,
            "var_type": self.VarList.get(expression, (None, None))[1],
            "executionOrder": self.ExecutionOrder.GetValue()}
        values["width"], values["height"] = self.Element.GetSize()
        return values

    def OnOK(self, event):
        """
        Called when dialog OK button is pressed
        Test if parameters defined are valid
        @param event: wx.Event from OK button
        """
        message = None
        
        # Test that an expression have been selected or typed by user
        value = self.Expression.GetValue()
        if value == "":
            message = _("At least a variable or an expression must be selected!")
        
        # Show error message if an error is detected
        if message is not None:
            self.ShowErrorMessage(message)
        
        else:
            # Call BlockPreviewDialog function
            BlockPreviewDialog.OnOK(self, event)

    def OnClassChanged(self, event):
        """
        Called when variable class value changed
        @param event: wx.ComboBoxEvent
        """
        # Refresh name list box values
        self.RefreshNameList()
        
        self.RefreshPreview()
        event.Skip()

    def OnNameChanged(self, event):
        """
        Called when name selected in name list box changed
        @param event: wx.ListBoxEvent
        """
        # Change expression test control value to the value selected in name
        # list box if value selected is valid
        if self.VariableName.GetSelection() != wx.NOT_FOUND:
            self.Expression.ChangeValue(self.VariableName.GetStringSelection())
        
        self.RefreshPreview()
        event.Skip()
    
    def OnExpressionChanged(self, event):
        """
        Called when expression text control is changed by user
        @param event: wx.ListBoxEvent
        """
        # Select the corresponding value in name list box if it exists
        self.VariableName.SetSelection(
            self.VariableName.FindString(self.Expression.GetValue()))
        
        self.RefreshPreview()
        event.Skip()
    
    def OnExecutionOrderChanged(self, event):
        """
        Called when block execution control value changed
        @param event: wx.SpinEvent
        """
        self.RefreshPreview()
        event.Skip()
    
    def RefreshPreview(self):
        """
        Refresh preview panel of graphic element
        Override BlockPreviewDialog function
        """
        # Get expression value to put in FBD variable element
        name = self.Expression.GetValue()
        
        # Set graphic element displayed, creating a FBD variable element
        self.Element = FBD_Variable(self.Preview, 
                    VARIABLE_CLASSES_DICT_REVERSE[
                        self.Class.GetStringSelection()], 
                    name, 
                    self.VarList.get(name, ("", ""))[1], 
                    executionOrder = self.ExecutionOrder.GetValue())
        
        # Call BlockPreviewDialog function
        BlockPreviewDialog.RefreshPreview(self)