beremiz

64d8f52bc8c8
Parents d51af006fa6b
Children 6ebd9c40b2be
clean-up for PEP8: fix W291 trailing whitespace
  • +2 -2
    Beremiz.py
  • +7 -7
    BeremizIDE.py
  • +1 -1
    Beremiz_service.py
  • +28 -29
    CodeFileTreeNode.py
  • +52 -53
    ConfigTreeNode.py
  • +4 -5
    IDEFrame.py
  • +1 -2
    NativeLib.py
  • +1 -2
    PLCGenerator.py
  • +3 -4
    PLCOpenEditor.py
  • +2 -2
    POULibrary.py
  • +13 -13
    ProjectController.py
  • +10 -13
    c_ext/CFileEditor.py
  • +24 -24
    canfestival/NetworkEditor.py
  • +17 -18
    canfestival/SlaveEditor.py
  • +73 -74
    canfestival/canfestival.py
  • +99 -99
    canfestival/config_utils.py
  • +30 -30
    controls/CustomToolTip.py
  • +22 -22
    controls/CustomTree.py
  • +269 -271
    controls/DebugVariablePanel/DebugVariableGraphicViewer.py
  • +63 -63
    controls/DebugVariablePanel/DebugVariableItem.py
  • +152 -152
    controls/DebugVariablePanel/DebugVariablePanel.py
  • +45 -45
    controls/DebugVariablePanel/DebugVariableTextViewer.py
  • +58 -58
    controls/DebugVariablePanel/DebugVariableViewer.py
  • +18 -18
    controls/DebugVariablePanel/GraphButton.py
  • +16 -16
    controls/DurationCellEditor.py
  • +21 -21
    controls/EnhancedStatusBar.py
  • +27 -27
    controls/FolderTree.py
  • +93 -93
    controls/LibraryPanel.py
  • +19 -20
    controls/LocationCellEditor.py
  • +64 -65
    controls/PouInstanceVariablesPanel.py
  • +56 -56
    controls/ProjectPropertiesPanel.py
  • +41 -41
    controls/SearchResultPanel.py
  • +4 -4
    controls/VariablePanel.py
  • +26 -26
    dialogs/ActionBlockDialog.py
  • +23 -23
    dialogs/ArrayTypeDialog.py
  • +55 -56
    dialogs/BlockPreviewDialog.py
  • +43 -43
    dialogs/BrowseLocationsDialog.py
  • +8 -9
    dialogs/BrowseValuesLibraryDialog.py
  • +27 -28
    dialogs/ConnectionDialog.py
  • +33 -34
    dialogs/DiscoveryDialog.py
  • +21 -21
    dialogs/DurationEditorDialog.py
  • +72 -72
    dialogs/FBDBlockDialog.py
  • +44 -46
    dialogs/FBDVariableDialog.py
  • +35 -35
    dialogs/FindInPouDialog.py
  • +12 -12
    dialogs/ForceVariableDialog.py
  • +30 -31
    dialogs/LDElementDialog.py
  • +20 -20
    dialogs/LDPowerRailDialog.py
  • +22 -23
    dialogs/PouActionDialog.py
  • +5 -6
    dialogs/PouNameDialog.py
  • +17 -17
    dialogs/PouTransitionDialog.py
  • +13 -13
    dialogs/ProjectDialog.py
  • +18 -19
    dialogs/SFCDivergenceDialog.py
  • +29 -29
    dialogs/SFCStepDialog.py
  • +5 -5
    dialogs/SFCStepNameDialog.py
  • +27 -27
    dialogs/SFCTransitionDialog.py
  • +24 -24
    dialogs/SearchInProjectDialog.py
  • +53 -53
    editors/DebugViewer.py
  • +41 -42
    editors/FileManagementPanel.py
  • +26 -27
    editors/LDViewer.py
  • +24 -25
    editors/SFCViewer.py
  • +2 -3
    editors/TextViewer.py
  • +13 -15
    editors/Viewer.py
  • +32 -32
    graphics/DebugDataConsumer.py
  • +127 -128
    graphics/FBD_Objects.py
  • +254 -255
    graphics/GraphicCommons.py
  • +115 -117
    graphics/LD_Objects.py
  • +28 -28
    graphics/RubberBand.py
  • +221 -222
    graphics/SFC_Objects.py
  • +18 -18
    graphics/ToolTipProducer.py
  • +72 -72
    i18n/mki18n.py
  • +172 -173
    plcopen/plcopen.py
  • +21 -23
    plcopen/structures.py
  • +5 -6
    py_ext/PythonEditor.py
  • +6 -8
    py_ext/py_ext.py
  • +2 -4
    runtime/PLCObject.py
  • +9 -9
    runtime/ServicePublisher.py
  • +8 -9
    svgui/pyjs/build.py
  • +3 -4
    svgui/pyjs/jsonrpc/django/jsonrpc.py
  • +1 -2
    svgui/pyjs/jsonrpc/jsonrpc.py
  • +5 -6
    svgui/pyjs/lib/pyjslib.py
  • +3 -3
    svgui/pyjs/lib/sys.py
  • +7 -8
    svgui/pyjs/pyjs.py
  • +11 -11
    svgui/svgui.py
  • +10 -11
    svgui/svgui_server.py
  • +15 -17
    svgui/svguilib.py
  • +5 -6
    targets/__init__.py
  • +5 -6
    targets/toolchain_makefile.py
  • +5 -6
    util/ProcessLogger.py
  • +1 -1
    util/TranslationCatalogs.py
  • +43 -43
    util/Zeroconf.py
  • +14 -15
    wxglade_hmi/wxglade_hmi.py
  • +100 -101
    xmlclass/xmlclass.py
  • +247 -248
    xmlclass/xsdschema.py
  • --- a/Beremiz.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/Beremiz.py Mon Aug 14 19:13:01 2017 +0300
    @@ -60,14 +60,14 @@
    def Usage(self):
    print "Usage:"
    - print "%s [Options] [Projectpath] [Buildpath]"%sys.argv[0]
    + print "%s [Options] [Projectpath] [Buildpath]"%sys.argv[0]
    print ""
    print "Supported options:"
    print "-h --help Print this help"
    print "-u --updatecheck URL Retrieve update information by checking URL"
    print "-e --extend PathToExtension Extend IDE functionality by loading at start additional extensions"
    print ""
    - print ""
    + print ""
    def SetCmdOptions(self):
    self.shortCmdOpts = "hu:e:"
    --- a/BeremizIDE.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/BeremizIDE.py Mon Aug 14 19:13:01 2017 +0300
    @@ -276,9 +276,9 @@
    wx.MessageBox(version.GetCommunityHelpMsg(), _(u'Community support'), wx.OK | wx.ICON_INFORMATION)
    }
    id = wx.NewId()
    - parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support'))
    + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support'))
    self.Bind(wx.EVT_MENU, handler, id=id)
    -
    +
    parent.Append(help='', id=wx.ID_ABOUT,
    kind=wx.ITEM_NORMAL, text=_(u'About'))
    self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
    @@ -371,7 +371,7 @@
    self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP)
    self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
    self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100)
    - self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2)
    + self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2)
    self.ProgressStatusBar.Hide()
    self.SetStatusBar(self.ConnectionStatusBar)
    @@ -383,13 +383,13 @@
    # commands invoked by build process by default are
    # found here.
    os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"]
    -
    -
    +
    +
    def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
    # Add beremiz's icon in top left corner of the frame
    self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)
    self.__init_execute_path()
    -
    +
    IDEFrame.__init__(self, parent, debug)
    self.Log = LogPseudoFile(self.LogConsole,self.SelectTab)
    @@ -932,7 +932,7 @@
    self.Close()
    def OnAboutMenu(self, event):
    - info = version.GetAboutDialogInfo()
    + info = version.GetAboutDialogInfo()
    ShowAboutDialog(self, info)
    def OnProjectTreeItemBeginEdit(self, event):
    --- a/Beremiz_service.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/Beremiz_service.py Mon Aug 14 19:13:01 2017 +0300
    @@ -306,7 +306,7 @@
    def OnTaskBarChangeInterface(self, evt):
    ip_addr = self.pyroserver.ip_addr
    ip_addr = '' if ip_addr is None else ip_addr
    - dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr)
    + dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr)
    dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")),
    ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!"))
    ])
    --- a/CodeFileTreeNode.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/CodeFileTreeNode.py Mon Aug 14 19:13:01 2017 +0300
    @@ -81,10 +81,10 @@
    SECTION_TAG_ELEMENT = "<xsd:element name=\"%s\" type=\"CodeText\"/>"
    class CodeFile:
    -
    +
    CODEFILE_NAME = "CodeFile"
    SECTIONS_NAMES = []
    -
    +
    def __init__(self):
    sections_str = {"codefile_name": self.CODEFILE_NAME}
    if "includes" in self.SECTIONS_NAMES:
    @@ -94,26 +94,26 @@
    sections_str["sections"] = "\n".join(
    [SECTION_TAG_ELEMENT % name
    for name in self.SECTIONS_NAMES if name != "includes"])
    -
    +
    self.CodeFileParser = GenerateParserFromXSDstring(
    CODEFILE_XSD % sections_str)
    self.CodeFileVariables = etree.XPath("variables/variable")
    -
    +
    filepath = self.CodeFileName()
    -
    +
    if os.path.isfile(filepath):
    xmlfile = open(filepath, 'r')
    codefile_xml = xmlfile.read()
    xmlfile.close()
    -
    +
    codefile_xml = codefile_xml.replace(
    - '<%s>' % self.CODEFILE_NAME,
    + '<%s>' % self.CODEFILE_NAME,
    '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME)
    for cre, repl in [
    (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
    (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
    codefile_xml = cre.sub(repl, codefile_xml)
    -
    +
    try:
    self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml)
    if error is not None:
    @@ -138,7 +138,7 @@
    def GenerateNewName(self, format, start_idx):
    return self.GetCTRoot().GenerateNewName(
    None, None, format, start_idx,
    - dict([(var.getname().upper(), True)
    + dict([(var.getname().upper(), True)
    for var in self.CodeFile.variables.getvariable()]))
    def SetVariables(self, variables):
    @@ -152,12 +152,12 @@
    variable.setonchange(var["OnChange"])
    variable.setopts(var["Options"])
    self.CodeFile.variables.appendvariable(variable)
    -
    +
    def GetVariables(self):
    datas = []
    for var in self.CodeFileVariables(self.CodeFile):
    - datas.append({"Name" : var.getname(),
    - "Type" : var.gettype(),
    + datas.append({"Name" : var.getname(),
    + "Type" : var.gettype(),
    "Initial" : var.getinitial(),
    "Description" : var.getdesc(),
    "OnChange" : var.getonchange(),
    @@ -170,25 +170,25 @@
    section_code = parts.get(section)
    if section_code is not None:
    getattr(self.CodeFile, section).setanyText(section_code)
    -
    +
    def GetTextParts(self):
    return dict([(section, getattr(self.CodeFile, section).getanyText())
    for section in self.SECTIONS_NAMES])
    -
    +
    def CTNTestModified(self):
    - return self.ChangesToSave or not self.CodeFileIsSaved()
    + return self.ChangesToSave or not self.CodeFileIsSaved()
    def OnCTNSave(self, from_project_path=None):
    filepath = self.CodeFileName()
    -
    +
    xmlfile = open(filepath,"w")
    xmlfile.write(etree.tostring(
    - self.CodeFile,
    - pretty_print=True,
    - xml_declaration=True,
    + self.CodeFile,
    + pretty_print=True,
    + xml_declaration=True,
    encoding='utf-8'))
    xmlfile.close()
    -
    +
    self.MarkCodeFileAsSaved()
    return True
    @@ -196,7 +196,7 @@
    variables = self.CodeFileVariables(self.CodeFile)
    ret = [(variable.getname(),
    variable.gettype(),
    - variable.getinitial())
    + variable.getinitial())
    for variable in variables]
    ret.extend([("On"+variable.getname()+"Change", "python_poll", "")
    for variable in variables
    @@ -219,31 +219,30 @@
    def BufferCodeFile(self):
    self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile))
    -
    +
    def StartBuffering(self):
    self.Buffering = True
    -
    +
    def EndBuffering(self):
    if self.Buffering:
    self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile))
    self.Buffering = False
    -
    +
    def MarkCodeFileAsSaved(self):
    self.EndBuffering()
    self.CodeFileBuffer.CurrentSaved()
    -
    +
    def CodeFileIsSaved(self):
    return self.CodeFileBuffer.IsCurrentSaved() and not self.Buffering
    -
    +
    def LoadPrevious(self):
    self.EndBuffering()
    self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Previous())
    -
    +
    def LoadNext(self):
    self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Next())
    -
    +
    def GetBufferState(self):
    first = self.CodeFileBuffer.IsFirst() and not self.Buffering
    last = self.CodeFileBuffer.IsLast()
    return not first, not last
    -
    --- a/ConfigTreeNode.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/ConfigTreeNode.py Mon Aug 14 19:13:01 2017 +0300
    @@ -67,7 +67,7 @@
    LibraryControler = None
    EditorType = ConfTreeNodeEditor
    IconPath = None
    -
    +
    def _AddParamsMembers(self):
    self.CTNParams = None
    if self.XSD:
    @@ -78,7 +78,7 @@
    setattr(self, name, obj)
    def __init__(self):
    - # Create BaseParam
    + # Create BaseParam
    self.BaseParams = _BaseParamsParser.CreateRoot()
    self.MandatoryParams = ("BaseParams", self.BaseParams)
    self._AddParamsMembers()
    @@ -86,10 +86,10 @@
    self._View = None
    # copy ConfNodeMethods so that it can be later customized
    self.ConfNodeMethods = [dic.copy() for dic in self.ConfNodeMethods]
    -
    +
    def ConfNodeBaseXmlFilePath(self, CTNName=None):
    return os.path.join(self.CTNPath(CTNName), "baseconfnode.xml")
    -
    +
    def ConfNodeXmlFilePath(self, CTNName=None):
    return os.path.join(self.CTNPath(CTNName), "confnode.xml")
    @@ -103,22 +103,22 @@
    project_path = self.CTNParent.CTNPath()
    return os.path.join(project_path,
    CTNName + NameTypeSeparator + self.CTNType)
    -
    +
    def CTNName(self):
    return self.BaseParams.getName()
    -
    +
    def CTNEnabled(self):
    return self.BaseParams.getEnabled()
    -
    +
    def CTNFullName(self):
    parent = self.CTNParent.CTNFullName()
    if parent != "":
    return parent + "." + self.CTNName()
    return self.BaseParams.getName()
    -
    +
    def GetIconName(self):
    return None
    -
    +
    def CTNTestModified(self):
    return self.ChangesToSave
    @@ -134,10 +134,10 @@
    return True
    return False
    -
    +
    def RemoteExec(self, script, **kwargs):
    return self.CTNParent.RemoteExec(script, **kwargs)
    -
    +
    def OnCTNSave(self, from_project_path=None):
    #Default, do nothing and return success
    return True
    @@ -154,7 +154,7 @@
    if self.CTNParams:
    params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0]))
    return params
    -
    +
    def SetParamsAttribute(self, path, value):
    self.ChangesToSave = True
    # Filter IEC_Channel and Name, that have specific behavior
    @@ -169,7 +169,7 @@
    res = self.FindNewName(value)
    self.CTNRequestSave()
    return res, True
    -
    +
    parts = path.split(".", 1)
    if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
    self.MandatoryParams[1].setElementValue(parts[1], value)
    @@ -189,32 +189,32 @@
    if not os.path.isdir(ctnpath):
    # Create it
    os.mkdir(ctnpath)
    -
    +
    # generate XML for base XML parameters controller of the confnode
    if self.MandatoryParams:
    BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(),'w')
    BaseXMLFile.write(etree.tostring(
    - self.MandatoryParams[1],
    - pretty_print=True,
    - xml_declaration=True,
    + self.MandatoryParams[1],
    + pretty_print=True,
    + xml_declaration=True,
    encoding='utf-8'))
    BaseXMLFile.close()
    -
    +
    # generate XML for XML parameters controller of the confnode
    if self.CTNParams:
    XMLFile = open(self.ConfNodeXmlFilePath(),'w')
    XMLFile.write(etree.tostring(
    - self.CTNParams[1],
    - pretty_print=True,
    - xml_declaration=True,
    + self.CTNParams[1],
    + pretty_print=True,
    + xml_declaration=True,
    encoding='utf-8'))
    XMLFile.close()
    -
    +
    # Call the confnode specific OnCTNSave method
    result = self.OnCTNSave(from_project_path)
    if not result:
    return _("Error while saving \"%s\"\n")%self.CTNPath()
    -
    +
    # mark confnode as saved
    self.ChangesToSave = False
    # go through all children and do the same
    @@ -226,7 +226,7 @@
    if result:
    return result
    return None
    -
    +
    def CTNImport(self, src_CTNPath):
    shutil.copytree(src_CTNPath, self.CTNPath)
    return True
    @@ -236,13 +236,13 @@
    @return: [(instance_name, instance_type),...]
    """
    return []
    -
    +
    def _GlobalInstances(self):
    instances = self.CTNGlobalInstances()
    for CTNChild in self.IECSortedChildren():
    instances.extend(CTNChild._GlobalInstances())
    return instances
    -
    +
    def CTNGenerate_C(self, buildpath, locations):
    """
    Generate C code
    @@ -257,7 +257,7 @@
    """
    self.GetCTRoot().logger.write_warning(".".join(map(lambda x:str(x), self.GetCurrentLocation())) + " -> Nothing to do\n")
    return [],"",False
    -
    +
    def _Generate_C(self, buildpath, locations):
    # Generate confnodes [(Cfiles, CFLAGS)], LDFLAGS, DoCalls, extra_files
    # extra_files = [(fname,fobject), ...]
    @@ -280,7 +280,7 @@
    LDFLAGS=CTNLDFLAGS[:]
    else:
    LDFLAGS=[]
    -
    +
    # recurse through all children, and stack their results
    for CTNChild in self.IECSortedChildren():
    new_location = CTNChild.GetCurrentLocation()
    @@ -296,14 +296,14 @@
    LocationCFilesAndCFLAGS += _LocationCFilesAndCFLAGS
    LDFLAGS += _LDFLAGS
    extra_files += _extra_files
    -
    +
    return LocationCFilesAndCFLAGS, LDFLAGS, extra_files
    def IterChildren(self):
    for CTNType, Children in self.Children.items():
    for CTNInstance in Children:
    yield CTNInstance
    -
    +
    def IECSortedChildren(self):
    # reorder children by IEC_channels
    ordered = [(chld.BaseParams.getIEC_Channel(),chld) for chld in self.IterChildren()]
    @@ -312,7 +312,7 @@
    return zip(*ordered)[1]
    else:
    return []
    -
    +
    def _GetChildBySomething(self, something, toks):
    for CTNInstance in self.IterChildren():
    # if match component of the name
    @@ -338,7 +338,7 @@
    return self._GetChildBySomething("IEC_Channel", Location)
    else:
    return self
    -
    +
    def GetCurrentLocation(self):
    """
    @return: Tupple containing confnode IEC location of current confnode : %I0.0.4.5 => (0,0,4,5)
    @@ -372,7 +372,7 @@
    This function is meant to be overridden by confnodes.
    It should returns an list of dictionaries
    -
    +
    - IEC_type is an IEC type like BOOL/BYTE/SINT/...
    - location is a string of this variable's location, like "%IX0.0.0"
    '''
    @@ -409,7 +409,7 @@
    while res in AllNames:
    res = "%s_%d"%(BaseDesiredName, suffix)
    suffix += 1
    -
    +
    # Get old path
    oldname = self.CTNPath()
    # Check previous confnode existance
    @@ -444,7 +444,7 @@
    #if CurrentChannel == DesiredChannel: return CurrentChannel
    # Build a list of used Channels out of parent's Children
    AllChannels = self.GetAllChannels()
    -
    +
    # Now, try to guess the nearest available channel
    res = DesiredChannel
    while res in AllChannels: # While channel not free
    @@ -466,14 +466,14 @@
    if self.EditorType is not None:
    app_frame = self.GetCTRoot().AppFrame
    if self._View is None and not onlyopened:
    -
    +
    self._View = self.EditorType(app_frame.TabsOpened, self, app_frame)
    -
    +
    if self._View is not None:
    if name is None:
    name = self.CTNFullName()
    app_frame.EditProjectElement(self._View, name)
    -
    +
    return self._View
    return None
    @@ -527,23 +527,23 @@
    CTNClass, CTNHelp = CTNChildrenTypes[CTNType]
    except KeyError:
    raise Exception, _("Cannot create child {a1} of type {a2} ").format(a1 = CTNName, a2 = CTNType)
    -
    +
    # if CTNClass is a class factory, call it. (prevent unneeded imports)
    if type(CTNClass) == types.FunctionType:
    CTNClass = CTNClass()
    -
    +
    # Eventualy Initialize child instance list for this class of confnode
    ChildrenWithSameClass = self.Children.setdefault(CTNType, list())
    # Check count
    if getattr(CTNClass, "CTNMaxCount", None) and len(ChildrenWithSameClass) >= CTNClass.CTNMaxCount:
    msg = _("Max count ({a1}) reached for this confnode of type {a2} ").format(a1 = CTNClass.CTNMaxCount, a2 = CTNType)
    raise Exception, msg
    -
    +
    # create the final class, derived of provided confnode and template
    class FinalCTNClass(CTNClass, ConfigTreeNode):
    """
    ConfNode class is derivated into FinalCTNClass before being instanciated
    - This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called
    + This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called
    before CTNClass.__init__, and to do the file related stuff.
    """
    def __init__(_self):
    @@ -568,7 +568,7 @@
    raise Exception, msg
    # Now, self.CTNPath() should be OK
    -
    +
    # Check that IEC_Channel is not already in use.
    _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel())
    # Call the confnode real __init__
    @@ -589,29 +589,29 @@
    _self.CTNRequestSave()
    #just created, must be saved
    _self.ChangesToSave = True
    -
    +
    def _getBuildPath(_self):
    return self._getBuildPath()
    -
    +
    # Create the object out of the resulting class
    newConfNodeOpj = FinalCTNClass()
    # Store it in CTNgedChils
    ChildrenWithSameClass.append(newConfNodeOpj)
    -
    +
    return newConfNodeOpj
    -
    +
    def ClearChildren(self):
    for child in self.IterChildren():
    child.ClearChildren()
    self.Children = {}
    -
    +
    def LoadXMLParams(self, CTNName = None):
    methode_name = os.path.join(self.CTNPath(CTNName), "methods.py")
    if os.path.isfile(methode_name):
    execfile(methode_name)
    -
    +
    ConfNodeName = CTNName if CTNName is not None else self.CTNName()
    -
    +
    # Get the base xml tree
    if self.MandatoryParams:
    try:
    @@ -626,7 +626,7 @@
    msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc))
    self.GetCTRoot().logger.write_error(msg)
    self.GetCTRoot().logger.write_error(traceback.format_exc())
    -
    +
    # Get the xml tree
    if self.CTNParams:
    try:
    @@ -643,7 +643,7 @@
    msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc))
    self.GetCTRoot().logger.write_error(msg)
    self.GetCTRoot().logger.write_error(traceback.format_exc())
    -
    +
    def LoadChildren(self):
    # Iterate over all CTNName@CTNType in confnode directory, and try to open them
    for CTNDir in os.listdir(self.CTNPath()):
    @@ -656,4 +656,3 @@
    msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1 = pname, a2 = ptype, a3 = unicode(exc))
    self.GetCTRoot().logger.write_error(msg)
    self.GetCTRoot().logger.write_error(traceback.format_exc())
    -
    --- a/IDEFrame.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/IDEFrame.py Mon Aug 14 19:13:01 2017 +0300
    @@ -474,10 +474,10 @@
    def _init_icon(self, parent):
    if self.icon:
    - self.SetIcon(self.icon)
    + self.SetIcon(self.icon)
    elif parent and parent.icon:
    - self.SetIcon(parent.icon)
    -
    + self.SetIcon(parent.icon)
    +
    def _init_ctrls(self, prnt):
    wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame',
    parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600),
    @@ -1626,7 +1626,7 @@
    self.RefreshPageTitles()
    elif item_infos["type"] == ITEM_TRANSITION:
    pou_item = self.ProjectTree.GetItemParent(event.GetItem())
    - pou_name = self.ProjectTree.GetItemText(pou_item)
    + pou_name = self.ProjectTree.GetItemText(pou_item)
    if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]:
    message = _("A POU named \"%s\" already exists!")%new_name
    elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]:
    @@ -2630,4 +2630,3 @@
    self.Viewer.DoDrawing(dc, True)
    return True
    -
    --- a/NativeLib.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/NativeLib.py Mon Aug 14 19:13:01 2017 +0300
    @@ -29,5 +29,4 @@
    class NativeLibrary(POULibrary):
    def GetLibraryPath(self):
    - return paths.AbsNeighbourFile(__file__, "NativeLib.xml")
    -
    + return paths.AbsNeighbourFile(__file__, "NativeLib.xml")
    --- a/PLCGenerator.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/PLCGenerator.py Mon Aug 14 19:13:01 2017 +0300
    @@ -771,7 +771,7 @@
    connections = content["value"].getconnections()
    if not connections:
    raise PLCGenException, _("SFC transition in POU \"%s\" must be connected.") % self.Name
    - for link in connections:
    + for link in connections:
    connected = self.GetLinkedConnector(link, body)
    if connected is not None and not self.ConnectionTypes.has_key(connected):
    for related in self.ExtractRelatedConnections(connected):
    @@ -1662,4 +1662,3 @@
    generator = ProgramGenerator(controler, project, errors, warnings)
    generator.GenerateProgram()
    return generator.GetGeneratedProgram()
    -
    --- a/PLCOpenEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/PLCOpenEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -156,14 +156,14 @@
    # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2')
    #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT,
    # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3')
    -
    +
    handler=lambda event: {
    wx.MessageBox(version.GetCommunityHelpMsg(), _(u'Community support'), wx.OK | wx.ICON_INFORMATION)
    }
    id = wx.NewId()
    - parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support'))
    + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support'))
    self.Bind(wx.EVT_MENU, handler, id=id)
    -
    +
    AppendMenu(parent, help='', id=wx.ID_ABOUT,
    kind=wx.ITEM_NORMAL, text=_(u'About'))
    self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP)
    @@ -505,4 +505,3 @@
    frame.Show()
    app.MainLoop()
    -
    --- a/POULibrary.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/POULibrary.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,14 +38,14 @@
    def GetSTCode(self):
    if not self.program:
    self.program = self.LibraryControler.GenerateProgram()[0]+"\n"
    - return self.program
    + return self.program
    def GetName(self):
    return self.LibName
    def GetCTR(self):
    return self.CTR()
    -
    +
    def GetTypes(self):
    return {"name" : self.GetName(), "types": self.LibraryControler.Project}
    --- a/ProjectController.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/ProjectController.py Mon Aug 14 19:13:01 2017 +0300
    @@ -98,9 +98,9 @@
    for p in paths:
    if test(p):
    path = p
    - break
    + break
    return path
    -
    +
    def findCmd(self):
    cmd="iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")
    paths=[
    @@ -113,7 +113,7 @@
    cmd=os.path.join(path, cmd)
    return cmd
    -
    +
    def findLibPath(self):
    paths=[
    os.path.join(base_folder, "matiec", "lib"),
    @@ -121,7 +121,7 @@
    ]
    path = self.findObject(paths, lambda p:os.path.isfile(os.path.join(p, "ieclib.txt")))
    return path
    -
    +
    def findLibCPath(self):
    path=None
    paths=[
    @@ -1158,14 +1158,14 @@
    def ShowError(self, logger, from_location, to_location):
    chunk_infos = self.GetChunkInfos(from_location, to_location)
    for infos, (start_row, start_col) in chunk_infos:
    - row = 1 if from_location[0] < start_row else (from_location[0] - start_row)
    + row = 1 if from_location[0] < start_row else (from_location[0] - start_row)
    col = 1 if (start_row != from_location[0]) else (from_location[1] - start_col)
    start = (row, col)
    - row = 1 if to_location[0] < start_row else (to_location[0] - start_row)
    + row = 1 if to_location[0] < start_row else (to_location[0] - start_row)
    col = 1 if (start_row != to_location[0]) else (to_location[1] - start_col)
    end = (row, col)
    -
    +
    if self.AppFrame is not None:
    self.AppFrame.ShowError(infos, start, end)
    @@ -1198,7 +1198,7 @@
    except:
    text = '(* No IEC code have been generated at that time ! *)'
    self._IECCodeView.SetText(text = text)
    - self._IECCodeView.Editor.SetReadOnly(True)
    + self._IECCodeView.Editor.SetReadOnly(True)
    self._IECCodeView.SetIcon(GetBitmap("ST"))
    setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor)
    @@ -1305,11 +1305,11 @@
    self.ShowMethod("_showIECcode", os.path.isfile(self._getIECcodepath()))
    if self.AppFrame is not None and not self.UpdateMethodsFromPLCStatus():
    self.AppFrame.RefreshStatusToolBar()
    -
    +
    def UpdateButtons(self):
    wx.CallAfter(self._UpdateButtons)
    -
    +
    def UpdatePLCLog(self, log_count):
    if log_count:
    if self.AppFrame is not None:
    @@ -1364,7 +1364,7 @@
    "Disconnected": _("Disconnected")
    }
    return msgs.get(status, status)
    -
    +
    def ShowPLCProgress(self, status = "", progress = 0):
    self.AppFrame.ProgressStatusBar.Show()
    self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1)
    @@ -1376,7 +1376,7 @@
    self.previous_plcstate = ""
    self.AppFrame.ProgressStatusBar.Hide()
    self.UpdateMethodsFromPLCStatus()
    -
    +
    def PullPLCStatusProc(self, event):
    self.UpdateMethodsFromPLCStatus()
    @@ -1821,7 +1821,7 @@
    self.AppFrame.LogViewer.ResetLogCounters();
    else:
    self.logger.write_error(_("Transfer failed\n"))
    - self.HidePLCProgress()
    + self.HidePLCProgress()
    else:
    self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
    --- a/c_ext/CFileEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/c_ext/CFileEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -29,20 +29,20 @@
    class CppEditor(CodeEditor):
    - KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class",
    - "const", "const_cast", "continue", "default", "delete", "do", "double",
    - "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false",
    - "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
    - "namespace", "new", "operator", "private", "protected", "public", "register",
    - "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
    + KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class",
    + "const", "const_cast", "continue", "default", "delete", "do", "double",
    + "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false",
    + "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
    + "namespace", "new", "operator", "private", "protected", "public", "register",
    + "reinterpret_cast", "return", "short", "signed", "sizeof", "static",
    "static_cast", "struct", "switch", "template", "this", "throw", "true", "try",
    - "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
    + "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual",
    "void", "volatile", "wchar_t", "while"]
    COMMENT_HEADER = "/"
    -
    +
    def SetCodeLexer(self):
    self.SetLexer(stc.STC_LEX_CPP)
    -
    +
    self.StyleSetSpec(stc.STC_C_COMMENT, 'fore:#408060,size:%(size)d' % faces)
    self.StyleSetSpec(stc.STC_C_COMMENTLINE, 'fore:#408060,size:%(size)d' % faces)
    self.StyleSetSpec(stc.STC_C_COMMENTDOC, 'fore:#408060,size:%(size)d' % faces)
    @@ -58,10 +58,7 @@
    #-------------------------------------------------------------------------------
    class CFileEditor(CodeFileEditor):
    -
    +
    CONFNODEEDITOR_TABS = [
    (_("C code"), "_create_CodePanel")]
    CODE_EDITOR = CppEditor
    -
    -
    -
    --- a/canfestival/NetworkEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/canfestival/NetworkEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -28,16 +28,16 @@
    from networkeditortemplate import NetworkEditorTemplate
    from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
    -[ID_NETWORKEDITOR,
    +[ID_NETWORKEDITOR,
    ] = [wx.NewId() for _init_ctrls in range(1)]
    -[ID_NETWORKEDITORCONFNODEMENUADDSLAVE, ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE,
    - ID_NETWORKEDITORCONFNODEMENUMASTER,
    +[ID_NETWORKEDITORCONFNODEMENUADDSLAVE, ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE,
    + ID_NETWORKEDITORCONFNODEMENUMASTER,
    ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(3)]
    [ID_NETWORKEDITORMASTERMENUNODEINFOS, ID_NETWORKEDITORMASTERMENUDS301PROFILE,
    ID_NETWORKEDITORMASTERMENUDS302PROFILE, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE,
    - ID_NETWORKEDITORMASTERMENUADD,
    + ID_NETWORKEDITORMASTERMENUADD,
    ] = [wx.NewId() for _init_coll_MasterMenu_Items in range(5)]
    [ID_NETWORKEDITORADDMENUSDOSERVER, ID_NETWORKEDITORADDMENUSDOCLIENT,
    @@ -46,37 +46,37 @@
    ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)]
    class NetworkEditor(ConfTreeNodeEditor, NetworkEditorTemplate):
    -
    +
    ID = ID_NETWORKEDITOR
    CONFNODEEDITOR_TABS = [
    (_("CANOpen network"), "_create_NetworkEditor")]
    -
    +
    def _create_NetworkEditor(self, prnt):
    - self.NetworkEditor = wx.Panel(id=-1, parent=prnt, pos=wx.Point(0, 0),
    + self.NetworkEditor = wx.Panel(id=-1, parent=prnt, pos=wx.Point(0, 0),
    size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
    -
    +
    NetworkEditorTemplate._init_ctrls(self, self.NetworkEditor)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=0)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW|wx.ALL)
    -
    +
    self.NetworkEditor.SetSizer(main_sizer)
    -
    +
    return self.NetworkEditor
    -
    +
    def __init__(self, parent, controler, window):
    ConfTreeNodeEditor.__init__(self, parent, controler, window)
    NetworkEditorTemplate.__init__(self, controler, window, False)
    -
    +
    self.RefreshNetworkNodes()
    self.RefreshBufferState()
    -
    +
    def __del__(self):
    self.Controler.OnCloseEditor(self)
    -
    +
    def GetConfNodeMenuItems(self):
    add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_NETWORKEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)),
    (wx.ITEM_NORMAL, (_('SDO Client'), ID_NETWORKEDITORADDMENUSDOCLIENT, '', self.OnAddSDOClientMenu)),
    @@ -84,7 +84,7 @@
    (wx.ITEM_NORMAL, (_('PDO Receive'), ID_NETWORKEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)),
    (wx.ITEM_NORMAL, (_('Map Variable'), ID_NETWORKEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)),
    (wx.ITEM_NORMAL, (_('User Type'), ID_NETWORKEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))]
    -
    +
    profile = self.Manager.GetCurrentProfileName()
    if profile not in ("None", "DS-301"):
    other_profile_text = _("%s Profile") % profile
    @@ -93,35 +93,35 @@
    add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text))))
    else:
    other_profile_text = _('Other Profile')
    -
    +
    master_menu = [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_NETWORKEDITORMASTERMENUDS301PROFILE, '', self.OnCommunicationMenu)),
    (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_NETWORKEDITORMASTERMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)),
    (wx.ITEM_NORMAL, (other_profile_text, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)),
    (wx.ITEM_SEPARATOR, None),
    (add_menu, (_('Add'), ID_NETWORKEDITORMASTERMENUADD))]
    -
    +
    return [(wx.ITEM_NORMAL, (_('Add slave'), ID_NETWORKEDITORCONFNODEMENUADDSLAVE, '', self.OnAddSlaveMenu)),
    (wx.ITEM_NORMAL, (_('Remove slave'), ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, '', self.OnRemoveSlaveMenu)),
    (wx.ITEM_SEPARATOR, None),
    (master_menu, (_('Master'), ID_NETWORKEDITORCONFNODEMENUMASTER))]
    -
    +
    def RefreshMainMenu(self):
    pass
    -
    +
    def RefreshConfNodeMenu(self, confnode_menu):
    confnode_menu.Enable(ID_NETWORKEDITORCONFNODEMENUMASTER, self.NetworkNodes.GetSelection() == 0)
    -
    +
    def RefreshView(self):
    ConfTreeNodeEditor.RefreshView(self)
    self.RefreshCurrentIndexList()
    -
    +
    def RefreshBufferState(self):
    NetworkEditorTemplate.RefreshBufferState(self)
    self.ParentWindow.RefreshTitle()
    self.ParentWindow.RefreshFileMenu()
    self.ParentWindow.RefreshEditMenu()
    self.ParentWindow.RefreshPageTitles()
    -
    +
    def OnNodeSelectedChanged(self, event):
    NetworkEditorTemplate.OnNodeSelectedChanged(self, event)
    wx.CallAfter(self.ParentWindow.RefreshEditMenu)
    --- a/canfestival/SlaveEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/canfestival/SlaveEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,7 +30,7 @@
    [ID_SLAVEEDITORCONFNODEMENUNODEINFOS, ID_SLAVEEDITORCONFNODEMENUDS301PROFILE,
    ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE,
    - ID_SLAVEEDITORCONFNODEMENUADD,
    + ID_SLAVEEDITORCONFNODEMENUADD,
    ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(5)]
    [ID_SLAVEEDITORADDMENUSDOSERVER, ID_SLAVEEDITORADDMENUSDOCLIENT,
    @@ -39,22 +39,22 @@
    ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)]
    class SlaveEditor(ConfTreeNodeEditor, NodeEditorTemplate):
    -
    +
    CONFNODEEDITOR_TABS = [
    (_("CANOpen slave"), "_create_SlaveNodeEditor")]
    -
    +
    def _create_SlaveNodeEditor(self, prnt):
    self.SlaveNodeEditor = EditingPanel(prnt, self, self.Controler, self.Editable)
    return self.SlaveNodeEditor
    -
    +
    def __init__(self, parent, controler, window, editable=True):
    self.Editable = editable
    ConfTreeNodeEditor.__init__(self, parent, controler, window)
    NodeEditorTemplate.__init__(self, controler, window, False)
    -
    +
    def __del__(self):
    self.Controler.OnCloseEditor(self)
    -
    +
    def GetConfNodeMenuItems(self):
    if self.Editable:
    add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_SLAVEEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)),
    @@ -63,7 +63,7 @@
    (wx.ITEM_NORMAL, (_('PDO Receive'), ID_SLAVEEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)),
    (wx.ITEM_NORMAL, (_('Map Variable'), ID_SLAVEEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)),
    (wx.ITEM_NORMAL, (_('User Type'), ID_SLAVEEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))]
    -
    +
    profile = self.Controler.GetCurrentProfileName()
    if profile not in ("None", "DS-301"):
    other_profile_text = _("%s Profile") % profile
    @@ -72,14 +72,14 @@
    add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text))))
    else:
    other_profile_text = _('Other Profile')
    -
    +
    return [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, '', self.OnCommunicationMenu)),
    (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)),
    (wx.ITEM_NORMAL, (other_profile_text, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)),
    (wx.ITEM_SEPARATOR, None),
    (add_menu, (_('Add'), ID_SLAVEEDITORCONFNODEMENUADD))]
    return []
    -
    +
    def RefreshConfNodeMenu(self, confnode_menu):
    if self.Editable:
    confnode_menu.Enable(ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, False)
    @@ -90,7 +90,7 @@
    def RefreshCurrentIndexList(self):
    self.RefreshView()
    -
    +
    def RefreshBufferState(self):
    self.ParentWindow.RefreshTitle()
    self.ParentWindow.RefreshFileMenu()
    @@ -100,27 +100,26 @@
    class MasterViewer(SlaveEditor):
    SHOW_BASE_PARAMS = False
    SHOW_PARAMS = False
    -
    +
    def __init__(self, parent, controler, window, tagname):
    SlaveEditor.__init__(self, parent, controler, window, False)
    -
    +
    self.TagName = tagname
    -
    +
    def GetTagName(self):
    return self.TagName
    -
    +
    def GetCurrentNodeId(self):
    return None
    -
    +
    def GetInstancePath(self):
    return self.Controler.CTNFullName() + ".generated_master"
    -
    +
    def GetTitle(self):
    return self.GetInstancePath()
    -
    +
    def IsViewing(self, tagname):
    return self.GetInstancePath() == tagname
    def RefreshView(self):
    self.SlaveNodeEditor.RefreshIndexList()
    -
    --- a/canfestival/canfestival.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/canfestival/canfestival.py Mon Aug 14 19:13:01 2017 +0300
    @@ -67,7 +67,7 @@
    "size": size,
    "IEC_type": IECTypeConversion.get(typeinfos["name"]),
    "var_name": "%s_%4.4x_%2.2x" % ("_".join(name.split()), index, subindex),
    - "location": "%s%s"%(SizeConversion[size], ".".join(map(str, current_location +
    + "location": "%s%s"%(SizeConversion[size], ".".join(map(str, current_location +
    (index, subindex)))),
    "description": "",
    "children": []})
    @@ -102,7 +102,7 @@
    </xsd:element>
    </xsd:schema>
    """
    -
    +
    EditorType = SlaveEditor
    IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png")
    @@ -125,7 +125,7 @@
    self.CreateNewNode(name, # Name - will be changed at build time
    id, # NodeID - will be changed at build time
    "slave", # Type
    - description,# description
    + description,# description
    profile, # profile
    filepath, # prfile filepath
    NMT, # NMT
    @@ -134,7 +134,7 @@
    self.CreateNewNode("SlaveNode", # Name - will be changed at build time
    0x00, # NodeID - will be changed at build time
    "slave", # Type
    - "", # description
    + "", # description
    "None", # profile
    "", # prfile filepath
    "heartbeat", # NMT
    @@ -156,48 +156,48 @@
    if self._View is not None:
    self._View.SetBusId(self.GetCurrentLocation())
    return self._View
    -
    +
    def _ExportSlave(self):
    - dialog = wx.FileDialog(self.GetCTRoot().AppFrame,
    - _("Choose a file"),
    - os.path.expanduser("~"),
    - "%s.eds" % self.CTNName(),
    + dialog = wx.FileDialog(self.GetCTRoot().AppFrame,
    + _("Choose a file"),
    + os.path.expanduser("~"),
    + "%s.eds" % self.CTNName(),
    _("EDS files (*.eds)|*.eds|All files|*.*"),
    wx.SAVE|wx.OVERWRITE_PROMPT)
    if dialog.ShowModal() == wx.ID_OK:
    result = eds_utils.GenerateEDSFile(dialog.GetPath(), self.GetCurrentNodeCopy())
    if result:
    self.GetCTRoot().logger.write_error(_("Error: Export slave failed\n"))
    - dialog.Destroy()
    -
    + dialog.Destroy()
    +
    ConfNodeMethods = [
    {"bitmap" : "ExportSlave",
    - "name" : _("Export slave"),
    + "name" : _("Export slave"),
    "tooltip" : _("Export CanOpen slave to EDS file"),
    "method" : "_ExportSlave"},
    ]
    -
    +
    def CTNTestModified(self):
    return self.ChangesToSave or self.OneFileHasChanged()
    -
    +
    def OnCTNSave(self, from_project_path=None):
    return self.SaveCurrentInFile(self.GetSlaveODPath())
    def SetParamsAttribute(self, path, value):
    result = ConfigTreeNode.SetParamsAttribute(self, path, value)
    -
    +
    # Filter IEC_Channel and Name, that have specific behavior
    if path == "BaseParams.IEC_Channel" and self._View is not None:
    self._View.SetBusId(self.GetCurrentLocation())
    -
    +
    return result
    -
    +
    def GetVariableLocationTree(self):
    current_location = self.GetCurrentLocation()
    - return GetSlaveLocationTree(self.CurrentNode,
    - self.GetCurrentLocation(),
    + return GetSlaveLocationTree(self.CurrentNode,
    + self.GetCurrentLocation(),
    self.BaseParams.getName())
    -
    +
    def CTNGenerate_C(self, buildpath, locations):
    """
    Generate C code
    @@ -230,10 +230,10 @@
    def LoadPrevious(self):
    self.LoadCurrentPrevious()
    -
    +
    def LoadNext(self):
    self.LoadCurrentNext()
    -
    +
    def GetBufferState(self):
    return self.GetCurrentBufferState()
    @@ -242,30 +242,30 @@
    #--------------------------------------------------
    class MiniNodeManager(NodeManager):
    -
    +
    def __init__(self, parent, filepath, fullname):
    NodeManager.__init__(self)
    -
    +
    self.OpenFileInCurrent(filepath)
    -
    +
    self.Parent = parent
    self.Fullname = fullname
    -
    +
    def GetIconName(self):
    return None
    -
    +
    def OnCloseEditor(self, view):
    self.Parent.OnCloseEditor(view)
    -
    +
    def CTNFullName(self):
    return self.Fullname
    -
    +
    def CTNTestModified(self):
    return False
    -
    +
    def GetBufferState(self):
    return self.GetCurrentBufferState()
    -
    +
    ConfNodeMethods = []
    class _NodeManager(NodeManager):
    @@ -273,16 +273,16 @@
    def __init__(self, parent, *args, **kwargs):
    NodeManager.__init__(self, *args, **kwargs)
    self.Parent = parent
    -
    +
    def __del__(self):
    self.Parent = None
    -
    +
    def GetCurrentNodeName(self):
    return self.Parent.CTNName()
    -
    +
    def GetCurrentNodeID(self):
    return self.Parent.CanFestivalNode.getNodeId()
    -
    +
    class _NodeListCTN(NodeList):
    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    @@ -295,20 +295,20 @@
    </xsd:complexType>
    </xsd:element>
    </xsd:schema>
    - """
    -
    + """
    +
    EditorType = NetworkEditor
    IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png")
    -
    +
    def __init__(self):
    manager = _NodeManager(self)
    NodeList.__init__(self, manager)
    self.LoadProject(self.CTNPath())
    self.SetNetworkName(self.BaseParams.getName())
    -
    +
    def GetCanDevice(self):
    return self.CanFestivalNode.getCAN_Device()
    -
    +
    def SetParamsAttribute(self, path, value):
    if path == "CanFestivalNode.NodeId":
    nodeid = self.CanFestivalNode.getNodeId()
    @@ -319,10 +319,10 @@
    value += dir
    if value < 0:
    value = nodeid
    -
    +
    value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value)
    refresh_network = False
    -
    +
    # Filter IEC_Channel and Name, that have specific behavior
    if path == "BaseParams.IEC_Channel" and self._View is not None:
    self._View.SetBusId(self.GetCurrentLocation())
    @@ -331,11 +331,11 @@
    refresh_network = True
    elif path == "CanFestivalNode.NodeId":
    refresh_network = True
    -
    +
    if refresh_network and self._View is not None:
    wx.CallAfter(self._View.RefreshBufferState)
    return value, refresh
    -
    +
    def GetVariableLocationTree(self):
    current_location = self.GetCurrentLocation()
    nodeindexes = self.SlaveNodes.keys()
    @@ -345,17 +345,17 @@
    "location": self.GetFullIEC_Channel(),
    "children": [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(),
    current_location,
    - _("Local entries"))] +
    - [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"],
    - current_location + (nodeid,),
    + _("Local entries"))] +
    + [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"],
    + current_location + (nodeid,),
    self.SlaveNodes[nodeid]["Name"])
    for nodeid in nodeindexes]
    }
    -
    +
    _GeneratedMasterView = None
    def _ShowGeneratedMaster(self):
    self._OpenView("Generated master")
    -
    +
    def _OpenView(self, name=None, onlyopened=False):
    if name == "Generated master":
    app_frame = self.GetCTRoot().AppFrame
    @@ -365,37 +365,37 @@
    if not os.path.exists(buildpath):
    self.GetCTRoot().logger.write_error(_("Error: No PLC built\n"))
    return
    -
    +
    masterpath = os.path.join(buildpath, "MasterGenerated.od")
    if not os.path.exists(masterpath):
    self.GetCTRoot().logger.write_error(_("Error: No Master generated\n"))
    return
    -
    +
    manager = MiniNodeManager(self, masterpath, self.CTNFullName())
    self._GeneratedMasterView = MasterViewer(app_frame.TabsOpened, manager, app_frame, name)
    -
    +
    if self._GeneratedMasterView is not None:
    app_frame.EditProjectElement(self._GeneratedMasterView, self._GeneratedMasterView.GetInstancePath())
    -
    +
    return self._GeneratedMasterView
    else:
    ConfigTreeNode._OpenView(self, name, onlyopened)
    if self._View is not None:
    self._View.SetBusId(self.GetCurrentLocation())
    return self._View
    -
    +
    ConfNodeMethods = [
    {"bitmap" : "ShowMaster",
    - "name" : _("Show Master"),
    + "name" : _("Show Master"),
    "tooltip" : _("Show Master generated by config_utils"),
    "method" : "_ShowGeneratedMaster"}
    ]
    -
    +
    def OnCloseEditor(self, view):
    ConfigTreeNode.OnCloseEditor(self, view)
    if self._GeneratedMasterView == view:
    self._GeneratedMasterView = None
    -
    +
    def OnCTNClose(self):
    ConfigTreeNode.OnCTNClose(self)
    self._CloseView(self._GeneratedMasterView)
    @@ -403,11 +403,11 @@
    def CTNTestModified(self):
    return self.ChangesToSave or self.HasChanged()
    -
    +
    def OnCTNSave(self, from_project_path=None):
    self.SetRoot(self.CTNPath())
    if from_project_path is not None:
    - shutil.copytree(self.GetEDSFolder(from_project_path),
    + shutil.copytree(self.GetEDSFolder(from_project_path),
    self.GetEDSFolder())
    return self.SaveProject() is None
    @@ -438,22 +438,22 @@
    res = gen_cfile.GenerateFile(Gen_OD_path, master, pointers)
    if res :
    raise Exception, res
    -
    +
    file = open(os.path.join(buildpath, "MasterGenerated.od"), "w")
    dump(master, file)
    file.close()
    -
    +
    return [(Gen_OD_path,local_canfestival_config.getCFLAGS(CanFestivalPath))],"",False
    -
    +
    def LoadPrevious(self):
    self.Manager.LoadCurrentPrevious()
    -
    +
    def LoadNext(self):
    self.Manager.LoadCurrentNext()
    -
    +
    def GetBufferState(self):
    return self.Manager.GetCurrentBufferState()
    -
    +
    class RootClass:
    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    @@ -464,7 +464,7 @@
    </xsd:element>
    </xsd:schema>
    """
    -
    +
    CTNChildrenTypes = [("CanOpenNode",_NodeListCTN, "CanOpen Master"),
    ("CanOpenSlave",_SlaveCTN, "CanOpen Slave")]
    def GetParamsAttributes(self, path = None):
    @@ -475,13 +475,13 @@
    if child["name"] == "CAN_Driver":
    child["type"] = local_canfestival_config.DLL_LIST
    return infos
    -
    +
    def GetCanDriver(self):
    res = self.CanFestivalInstance.getCAN_Driver()
    if not res :
    return ""
    return res
    -
    +
    def CTNGenerate_C(self, buildpath, locations):
    can_driver = self.GetCanDriver()
    if can_driver is not None:
    @@ -494,7 +494,7 @@
    else:
    can_driver_name = ""
    -
    +
    format_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())),
    "candriver" : can_driver_name,
    "nodes_includes" : "",
    @@ -515,7 +515,7 @@
    for child in self.IECSortedChildren():
    childlocstr = "_".join(map(str,child.GetCurrentLocation()))
    nodename = "OD_%s" % childlocstr
    -
    +
    # Try to get Slave Node
    child_data = getattr(child, "CanFestivalSlaveNode", None)
    if child_data is None:
    @@ -569,7 +569,7 @@
    format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n '%(
    nodename,
    child_data.getNodeId())
    -
    +
    # Include generated OD headers
    format_dict["nodes_includes"] += '#include "%s.h"\n'%(nodename)
    # Declare CAN channels according user filled config
    @@ -580,7 +580,7 @@
    format_dict["nodes_open"] += 'NODE_OPEN(%s)\n '%(nodename)
    format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n '%(nodename)
    format_dict["nodes_stop"] += 'NODE_STOP(%s)\n '%(nodename)
    -
    +
    filename = paths.AbsNeighbourFile(__file__,"cf_runtime.c")
    cf_main = open(filename).read() % format_dict
    cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c"%format_dict)
    @@ -589,11 +589,10 @@
    f.close()
    res = [(cf_main_path, local_canfestival_config.getCFLAGS(CanFestivalPath))],local_canfestival_config.getLDFLAGS(CanFestivalPath), True
    -
    +
    if can_driver is not None:
    can_driver_path = os.path.join(CanFestivalPath,"drivers",can_driver,can_driver_name)
    if os.path.exists(can_driver_path):
    res += ((can_driver_name, file(can_driver_path,"rb")),)
    return res
    -
    --- a/canfestival/config_utils.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/canfestival/config_utils.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,7 +30,7 @@
    "LREAL":0x11,"STRING":0x09,"BYTE":0x05,"WORD":0x06,"DWORD":0x07,
    "LWORD":0x1B,"WSTRING":0x0B}
    -# Constants for PDO types
    +# Constants for PDO types
    RPDO = 1
    TPDO = 2
    @@ -61,7 +61,7 @@
    @param size: number of bytes generated
    @return: a string containing the value converted
    """
    -
    +
    data = ("%" + str(size * 2) + "." + str(size * 2) + "X") % value
    list_car = [data[i:i+2] for i in xrange(0, len(data), 2)]
    list_car.reverse()
    @@ -71,12 +71,12 @@
    def GetNodePDOIndexes(node, type, parameters = False):
    """
    Find the PDO indexes of a node
    - @param node: node
    + @param node: node
    @param type: type of PDO searched (RPDO or TPDO or both)
    @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False)
    @return: a list of indexes found
    """
    -
    +
    indexes = []
    if type & RPDO:
    indexes.extend([idx for idx in node.GetIndexes() if 0x1400 <= idx <= 0x15FF])
    @@ -91,14 +91,14 @@
    def SearchNodePDOMapping(loc_infos, node):
    """
    Find the PDO indexes of a node
    - @param node: node
    + @param node: node
    @param type: type of PDO searched (RPDO or TPDO or both)
    @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False)
    @return: a list of indexes found
    """
    -
    +
    model = (loc_infos["index"] << 16) + (loc_infos["subindex"] << 8)
    -
    +
    for PDOidx in GetNodePDOIndexes(node, loc_infos["pdotype"]):
    values = node.GetEntry(PDOidx)
    if values != None:
    @@ -115,9 +115,9 @@
    @param cobid: PDO generated COB ID
    @param transmittype : PDO transmit type
    @param pdomapping: list of PDO mappings
    - @return: a tuple of value and number of parameters to add to DCF
    + @return: a tuple of value and number of parameters to add to DCF
    """
    -
    +
    dcfdata=[]
    # Create entry for RPDO or TPDO parameters and Disable PDO
    # ---- INDEX ----- --- SUBINDEX ---- ----- SIZE ------ ------ DATA ------
    @@ -152,7 +152,7 @@
    self.TrashVariables = {}
    # Dictionary of pointed variables
    self.PointedVariables = {}
    -
    +
    self.NodeList = nodelist
    self.Manager = self.NodeList.Manager
    self.MasterNode = self.Manager.GetCurrentNodeCopy()
    @@ -161,18 +161,18 @@
    def GetPointedVariables(self):
    return self.PointedVariables
    -
    +
    def RemoveUsedNodeCobId(self, node):
    """
    Remove all PDO COB ID used by the given node from the list of available COB ID
    @param node: node
    @return: a tuple of number of RPDO and TPDO for the node
    """
    -
    +
    # Get list of all node TPDO and RPDO indexes
    nodeRpdoIndexes = GetNodePDOIndexes(node, RPDO, True)
    nodeTpdoIndexes = GetNodePDOIndexes(node, TPDO, True)
    -
    +
    # Mark all the COB ID of the node already mapped PDO as not available
    for PdoIdx in nodeRpdoIndexes + nodeTpdoIndexes:
    pdo_cobid = node.GetEntry(PdoIdx, 0x01)
    @@ -182,20 +182,20 @@
    # Remove COB ID from the list of available COB ID
    if pdo_cobid in self.ListCobIDAvailable:
    self.ListCobIDAvailable.remove(pdo_cobid)
    -
    +
    return len(nodeRpdoIndexes), len(nodeTpdoIndexes)
    -
    +
    def PrepareMasterNode(self):
    """
    Add mandatory entries for DCF generation into MasterNode.
    """
    -
    +
    # Adding DCF entry into Master node
    if not self.MasterNode.IsEntry(0x1F22):
    self.MasterNode.AddEntry(0x1F22, 1, "")
    self.Manager.AddSubentriesToCurrent(0x1F22, 127, self.MasterNode)
    -
    +
    # Adding trash mappable variables for unused mapped datas
    idxTrashVariables = 0x2000 + self.MasterNode.GetNodeID()
    # Add an entry for storing unexpected all variable
    @@ -206,9 +206,9 @@
    self.Manager.SetCurrentEntry(idxTrashVariables, subidx + 1, typeidx, "type", None, self.MasterNode)
    # Store the mapping value for this entry
    self.TrashVariables[size] = (idxTrashVariables << 16) + ((subidx + 1) << 8) + size
    -
    +
    RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(self.MasterNode)
    -
    +
    # Store the indexes of the first RPDO and TPDO available for MasterNode
    self.CurrentPDOParamsIdx = {RPDO : 0x1400 + RPDOnumber, TPDO : 0x1800 + TPDOnumber}
    @@ -216,9 +216,9 @@
    for idx, (nodeid, nodeinfos) in enumerate(self.NodeList.SlaveNodes.items()):
    node = nodeinfos["Node"]
    node.SetNodeID(nodeid)
    -
    +
    RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(node)
    -
    +
    # Get Slave's default SDO server parameters
    RSDO_cobid = node.GetEntry(0x1200,0x01)
    if not RSDO_cobid:
    @@ -226,20 +226,20 @@
    TSDO_cobid = node.GetEntry(0x1200,0x02)
    if not TSDO_cobid:
    TSDO_cobid = 0x580 + nodeid
    -
    +
    # Configure Master's SDO parameters entries
    self.Manager.ManageEntriesOfCurrent([0x1280 + idx], [], self.MasterNode)
    self.MasterNode.SetEntry(0x1280 + idx, 0x01, RSDO_cobid)
    self.MasterNode.SetEntry(0x1280 + idx, 0x02, TSDO_cobid)
    - self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid)
    -
    -
    + self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid)
    +
    +
    def GetMasterNode(self):
    """
    Return MasterNode.
    """
    return self.MasterNode
    -
    +
    def AddParamsToDCF(self, nodeid, data, nbparams):
    """
    Add entry to DCF, for the requested nodeID
    @@ -249,19 +249,19 @@
    """
    # Get current DCF for slave
    nodeDCF = self.MasterNode.GetEntry(0x1F22, nodeid)
    -
    +
    # Extract data and number of params in current DCF
    if nodeDCF != None and nodeDCF != '':
    tmpnbparams = [i for i in nodeDCF[:4]]
    tmpnbparams.reverse()
    nbparams += int(''.join(["%2.2x"%ord(i) for i in tmpnbparams]), 16)
    data = nodeDCF[4:] + data
    -
    +
    # Build new DCF
    dcf = LE_to_BE(nbparams, 0x04) + data
    # Set new DCF for slave
    self.MasterNode.SetEntry(0x1F22, nodeid, dcf)
    -
    +
    def GetEmptyPDO(self, nodeid, pdotype, start_index=None):
    """
    Search a not configured PDO for a slave
    @@ -275,7 +275,7 @@
    index = PDOTypeBaseIndex[pdotype]
    else:
    index = start_index
    -
    +
    # Search for all PDO possible index until find a configurable PDO
    # starting from start_index
    while index < PDOTypeBaseIndex[pdotype] + 0x200:
    @@ -296,7 +296,7 @@
    return index, cobid, values[0]
    index += 1
    return None
    -
    +
    def AddPDOMapping(self, nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs):
    """
    Record a new mapping request for a slave, and add related slave config to the DCF
    @@ -305,16 +305,16 @@
    @param pdomapping: list od variables to map with PDO
    """
    # Add an entry to MasterMapping
    - self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype],
    + self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype],
    "mapping" : [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping]}
    -
    +
    # Return the data to add to DCF
    if sync_TPDOs:
    return GeneratePDOMappingDCF(pdoindex, pdocobid, 0x01, pdomapping)
    else:
    return GeneratePDOMappingDCF(pdoindex, pdocobid, 0xFF, pdomapping)
    return 0, ""
    -
    +
    def GenerateDCF(self, locations, current_location, sync_TPDOs):
    """
    Generate Concise DCF of MasterNode for the locations list given
    @@ -322,18 +322,18 @@
    @param current_location: tuple of the located prefixes not to be considered
    @param sync_TPDOs: indicate if TPDO must be synchronous
    """
    -
    +
    #-------------------------------------------------------------------------------
    # Verify that locations correspond to real slave variables
    #-------------------------------------------------------------------------------
    -
    +
    # Get list of locations check if exists and mappables -> put them in IECLocations
    for location in locations:
    COlocationtype = IECToCOType[location["IEC_TYPE"]]
    name = location["NAME"]
    if name in self.IECLocations:
    if self.IECLocations[name]["type"] != COlocationtype:
    - raise PDOmappingException, _("Type conflict for location \"%s\"") % name
    + raise PDOmappingException, _("Type conflict for location \"%s\"") % name
    else:
    # Get only the part of the location that concern this node
    loc = location["LOC"][len(current_location):]
    @@ -342,30 +342,30 @@
    raise PDOmappingException, _("Bad location size : %s") % str(loc)
    elif len(loc) == 2:
    continue
    -
    +
    direction = location["DIR"]
    -
    +
    sizelocation = location["SIZE"]
    -
    +
    # Extract and check nodeid
    nodeid, index, subindex = loc[:3]
    -
    +
    # Check Id is in slave node list
    if nodeid not in self.NodeList.SlaveNodes.keys():
    raise PDOmappingException, _("Non existing node ID : {a1} (variable {a2})").format(a1 = nodeid, a2 = name)
    -
    +
    # Get the model for this node (made from EDS)
    node = self.NodeList.SlaveNodes[nodeid]["Node"]
    -
    +
    # Extract and check index and subindex
    if not node.IsEntry(index, subindex):
    msg = _("No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})").\
    format(a1 = "%x" % index, a2 ="%x" % subindex, a3 = nodeid, a4 = name)
    raise PDOmappingException, msg
    -
    +
    # Get the entry info
    subentry_infos = node.GetSubentryInfos(index, subindex)
    -
    +
    # If a PDO mappable
    if subentry_infos and subentry_infos["pdo"]:
    if sizelocation == "X" and len(loc) > 3:
    @@ -376,11 +376,11 @@
    raise PDOmappingException, msg
    else:
    numbit = None
    -
    +
    if location["IEC_TYPE"] != "BOOL" and subentry_infos["type"] != COlocationtype:
    raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\
    format(a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name)
    -
    +
    typeinfos = node.GetEntryInfos(COlocationtype)
    self.IECLocations[name] = {"type":COlocationtype, "pdotype":SlavePDOType[direction],
    "nodeid": nodeid, "index": index,"subindex": subindex,
    @@ -388,21 +388,21 @@
    else:
    raise PDOmappingException, _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))").\
    format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex)
    -
    +
    #-------------------------------------------------------------------------------
    # Search for locations already mapped
    #-------------------------------------------------------------------------------
    -
    +
    for name, locationinfos in self.IECLocations.items():
    node = self.NodeList.SlaveNodes[locationinfos["nodeid"]]["Node"]
    -
    +
    # Search if slave has a PDO mapping this locations
    result = SearchNodePDOMapping(locationinfos, node)
    if result != None:
    index, subindex = result
    # Get COB ID of the PDO
    cobid = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 1)
    -
    +
    # Add PDO to MasterMapping
    if cobid not in self.MasterMapping.keys():
    # Verify that PDO transmit type is conform to sync_TPDOs
    @@ -414,10 +414,10 @@
    else:
    # Change TransmitType to ASYCHRONE
    data, nbparams = GeneratePDOMappingDCF(index - 0x200, cobid, 0xFF, [])
    -
    - # Add entry to slave dcf to change transmit type of
    +
    + # Add entry to slave dcf to change transmit type of
    self.AddParamsToDCF(locationinfos["nodeid"], data, nbparams)
    -
    +
    mapping = [None]
    values = node.GetEntry(index)
    # Store the size of each entry mapped in PDO
    @@ -425,7 +425,7 @@
    if value != 0:
    mapping.append(value % 0x100)
    self.MasterMapping[cobid] = {"type" : InvertPDOType[locationinfos["pdotype"]], "mapping" : mapping}
    -
    +
    # Indicate that this PDO entry must be saved
    if locationinfos["bit"] is not None:
    if not isinstance(self.MasterMapping[cobid]["mapping"][subindex], ListType):
    @@ -434,24 +434,24 @@
    self.MasterMapping[cobid]["mapping"][subindex][locationinfos["bit"]] = (locationinfos["type"], name)
    else:
    self.MasterMapping[cobid]["mapping"][subindex] = (locationinfos["type"], name)
    -
    +
    else:
    # Add location to those that haven't been mapped yet
    if locationinfos["nodeid"] not in self.LocationsNotMapped.keys():
    self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO : [], RPDO : []}
    self.LocationsNotMapped[locationinfos["nodeid"]][locationinfos["pdotype"]].append((name, locationinfos))
    -
    +
    #-------------------------------------------------------------------------------
    # Build concise DCF for the others locations
    #-------------------------------------------------------------------------------
    -
    +
    for nodeid, locations in self.LocationsNotMapped.items():
    node = self.NodeList.SlaveNodes[nodeid]["Node"]
    -
    +
    # Initialize number of params and data to add to node DCF
    nbparams = 0
    dataparams = ""
    -
    +
    # Generate the best PDO mapping for each type of PDO
    for pdotype in (TPDO, RPDO):
    if len(locations[pdotype]) > 0:
    @@ -483,78 +483,78 @@
    data, nbaddedparams = self.AddPDOMapping(nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs)
    dataparams += data
    nbparams += nbaddedparams
    -
    +
    # Add number of params and data to node DCF
    self.AddParamsToDCF(nodeid, dataparams, nbparams)
    -
    +
    #-------------------------------------------------------------------------------
    # Master Node Configuration
    #-------------------------------------------------------------------------------
    -
    +
    # Generate Master's Configuration from informations stored in MasterMapping
    for cobid, pdo_infos in self.MasterMapping.items():
    # Get next PDO index in MasterNode for this PDO type
    current_idx = self.CurrentPDOParamsIdx[pdo_infos["type"]]
    -
    +
    # Search if there is already a PDO in MasterNode with this cob id
    for idx in GetNodePDOIndexes(self.MasterNode, pdo_infos["type"], True):
    if self.MasterNode.GetEntry(idx, 1) == cobid:
    current_idx = idx
    -
    +
    # Add a PDO to MasterNode if not PDO have been found
    if current_idx == self.CurrentPDOParamsIdx[pdo_infos["type"]]:
    addinglist = [current_idx, current_idx + 0x200]
    self.Manager.ManageEntriesOfCurrent(addinglist, [], self.MasterNode)
    self.MasterNode.SetEntry(current_idx, 0x01, cobid)
    -
    +
    # Increment the number of PDO for this PDO type
    self.CurrentPDOParamsIdx[pdo_infos["type"]] += 1
    -
    +
    # Change the transmit type of the PDO
    if sync_TPDOs:
    self.MasterNode.SetEntry(current_idx, 0x02, 0x01)
    else:
    self.MasterNode.SetEntry(current_idx, 0x02, 0xFF)
    -
    +
    mapping = []
    for item in pdo_infos["mapping"]:
    if isinstance(item, ListType):
    mapping.extend(item)
    else:
    mapping.append(item)
    -
    +
    # Add some subentries to PDO mapping if there is not enough
    if len(mapping) > 1:
    self.Manager.AddSubentriesToCurrent(current_idx + 0x200, len(mapping) - 1, self.MasterNode)
    -
    +
    # Generate MasterNode's PDO mapping
    for subindex, variable in enumerate(mapping):
    if subindex == 0:
    continue
    new_index = False
    -
    +
    if isinstance(variable, (IntType, LongType)):
    # If variable is an integer then variable is unexpected
    self.MasterNode.SetEntry(current_idx + 0x200, subindex, self.TrashVariables[variable])
    else:
    typeidx, varname = variable
    variable_infos = self.IECLocations[varname]
    -
    +
    # Calculate base index for storing variable
    mapvariableidx = VariableStartIndex[variable_infos["pdotype"]] + \
    VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \
    variable_infos["nodeid"]
    -
    +
    # Generate entry name
    indexname = "%s%s%s_%d"%(VariableDirText[variable_infos["pdotype"]],
    variable_infos["sizelocation"],
    '_'.join(map(str,current_location)),
    - variable_infos["nodeid"])
    -
    - # Search for an entry that has an empty subindex
    + variable_infos["nodeid"])
    +
    + # Search for an entry that has an empty subindex
    while mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000:
    # Entry doesn't exist
    - if not self.MasterNode.IsEntry(mapvariableidx):
    + if not self.MasterNode.IsEntry(mapvariableidx):
    # Add entry to MasterNode
    self.Manager.AddMapVariableToCurrent(mapvariableidx, "beremiz"+indexname, 3, 1, self.MasterNode)
    new_index = True
    @@ -567,7 +567,7 @@
    mapvariableidx += 8 * VariableIncrement
    else:
    break
    -
    +
    # Verify that a not full entry has been found
    if mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000:
    # Generate subentry name
    @@ -582,13 +582,13 @@
    # Add informations to the new subentry created
    self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"name" : subindexname})
    self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"type" : typeidx})
    -
    +
    # Set value of the PDO mapping
    typeinfos = self.Manager.GetEntryInfos(typeidx)
    if typeinfos != None:
    value = (mapvariableidx << 16) + ((nbsubentries) << 8) + typeinfos["size"]
    self.MasterNode.SetEntry(current_idx + 0x200, subindex, value)
    -
    +
    # Add variable to pointed variables
    self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s"%(indexname, subindexname)
    @@ -605,7 +605,7 @@
    @param nodelist: CanFestival network editor model
    @return: a modified copy of the given CanFestival network editor model
    """
    -
    +
    dcfgenerator = ConciseDCFGenerator(nodelist, nodename)
    dcfgenerator.GenerateDCF(locations, current_location, sync_TPDOs)
    masternode,pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables()
    @@ -621,7 +621,7 @@
    name = location["NAME"]
    if name in IECLocations:
    if IECLocations[name] != COlocationtype:
    - raise PDOmappingException, _("Type conflict for location \"%s\"") % name
    + raise PDOmappingException, _("Type conflict for location \"%s\"") % name
    else:
    # Get only the part of the location that concern this node
    loc = location["LOC"][len(current_location):]
    @@ -630,25 +630,25 @@
    raise PDOmappingException, _("Bad location size : %s") % str(loc)
    elif len(loc) != 2:
    continue
    -
    +
    # Extract and check nodeid
    index, subindex = loc[:2]
    -
    +
    # Extract and check index and subindex
    if not slave.IsEntry(index, subindex):
    raise PDOmappingException, _("No such index/subindex ({a1},{a2}) (variable {a3})").\
    format(a1 = "%x" % index, a2 = "%x" % subindex, a3 = name)
    -
    +
    # Get the entry info
    - subentry_infos = slave.GetSubentryInfos(index, subindex)
    + subentry_infos = slave.GetSubentryInfos(index, subindex)
    if subentry_infos["type"] != COlocationtype:
    raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\
    format( a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name)
    -
    +
    IECLocations[name] = COlocationtype
    pointers[(index, subindex)] = name
    return pointers
    -
    +
    if __name__ == "__main__":
    import os, sys, getopt
    @@ -667,7 +667,7 @@
    Use with caution. Be sure that config_utils
    is currently working properly.
    """%sys.argv[0]
    -
    +
    # Boolean that indicate if reference result must be redefined
    reset = False
    @@ -693,15 +693,15 @@
    base_folder = os.path.split(base_folder)[0]
    # Add CanFestival folder to search pathes
    sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
    -
    +
    from nodemanager import *
    from nodelist import *
    -
    +
    # Open the test nodelist contained into test_config folder
    manager = NodeManager()
    nodelist = NodeList(manager)
    result = nodelist.LoadProject("test_config")
    -
    +
    # List of locations, we try to map for test
    locations = [{"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24576_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24576,1)},
    {"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_2","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,2)},
    @@ -713,34 +713,34 @@
    {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_3","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,3)},
    {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_4","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,4)},
    {"IEC_TYPE":"UDINT","NAME":"__ID0_1_4096_0","DIR":"I","SIZE":"D","LOC":(0,1,4096,0)}]
    -
    +
    # Generate MasterNode configuration
    try:
    masternode, pointedvariables = GenerateConciseDCF(locations, (0, 1), nodelist, True, "TestNode")
    except ValueError, message:
    print "%s\nTest Failed!"%message
    sys.exit()
    -
    +
    import pprint
    - # Get Text corresponding to MasterNode
    + # Get Text corresponding to MasterNode
    result_node = masternode.PrintString()
    result_vars = pprint.pformat(pointedvariables)
    result = result_node + "\n********POINTERS*********\n" + result_vars + "\n"
    -
    +
    # If reset has been choosen
    if reset:
    # Write Text into reference result file
    testfile = open("test_config/result.txt", "w")
    testfile.write(result)
    testfile.close()
    -
    +
    print "Reset Successful!"
    else:
    import os
    -
    +
    testfile = open("test_config/result_tmp.txt", "w")
    testfile.write(result)
    testfile.close()
    -
    +
    os.system("diff test_config/result.txt test_config/result_tmp.txt")
    os.remove("test_config/result_tmp.txt")
    --- a/controls/CustomToolTip.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/CustomToolTip.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,32 +39,32 @@
    """
    class CustomToolTip(wx.PopupWindow):
    -
    +
    def __init__(self, parent, tip, restricted=True):
    """
    Constructor
    @param parent: Parent window
    @param tip: Tip text (may be multiline)
    - @param restricted: Tool tip must follow size restriction in line and
    + @param restricted: Tool tip must follow size restriction in line and
    characters number defined (default True)
    """
    wx.PopupWindow.__init__(self, parent)
    -
    +
    self.Restricted = restricted
    -
    +
    self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
    self.SetTip(tip)
    -
    +
    # Initialize text font style
    self.Font = wx.Font(
    - faces["size"],
    - wx.SWISS,
    - wx.NORMAL,
    - wx.NORMAL,
    + faces["size"],
    + wx.SWISS,
    + wx.NORMAL,
    + wx.NORMAL,
    faceName = faces["mono"])
    -
    +
    self.Bind(wx.EVT_PAINT, self.OnPaint)
    -
    +
    def SetFont(self, font):
    """
    Set tool tip text font style
    @@ -72,7 +72,7 @@
    """
    self.Font = font
    self.RefreshTip()
    -
    +
    def SetTip(self, tip):
    """
    Set tool tip text
    @@ -97,11 +97,11 @@
    self.Tip.append(new_line)
    else:
    self.Tip.append(line)
    -
    +
    # Restrict number of lines
    if len(self.Tip) > TOOLTIP_MAX_LINE:
    self.Tip = self.Tip[:TOOLTIP_MAX_LINE]
    -
    +
    # Add ... to the end of last line to indicate that tool tip
    # text is too long
    if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
    @@ -111,10 +111,10 @@
    [:TOOLTIP_MAX_CHARACTERS - 3] + "..."
    else:
    self.Tip = tip.splitlines()
    -
    +
    # Prevent to call wx method in non-wx threads
    wx.CallAfter(self.RefreshTip)
    -
    +
    def SetToolTipPosition(self, pos):
    """
    Set tool tip position
    @@ -122,32 +122,32 @@
    """
    # Get screen size to prevent tool tip to go out of the screen
    screen_width, screen_height = wx.GetDisplaySize()
    -
    +
    # Calculate position of tool tip to stay in screen limits
    tip_width, tip_height = self.GetToolTipSize()
    self.SetPosition(wx.Point(
    max(0, min(pos.x, screen_width - tip_width)),
    max(0, min(pos.y, screen_height - tip_height))))
    -
    +
    def GetToolTipSize(self):
    """
    Get tool tip size according to tip text and restriction
    @return: wx.Size(tool_tip_width, tool_tip_height)
    """
    max_width = max_height = 0
    -
    +
    # Create a memory DC for calculating text extent
    dc = wx.MemoryDC()
    dc.SetFont(self.Font)
    -
    +
    # Compute max tip text size
    for line in self.Tip:
    w, h = dc.GetTextExtent(line)
    max_width = max(max_width, w)
    max_height += h
    -
    +
    return wx.Size(max_width + 4, max_height + 4)
    -
    +
    def RefreshTip(self):
    """
    Refresh tip on screen
    @@ -156,10 +156,10 @@
    if self:
    # Refresh tool tip size and position
    self.SetClientSize(self.GetToolTipSize())
    -
    +
    # Redraw tool tip
    self.Refresh()
    -
    +
    def OnPaint(self, event):
    """
    Callback for Paint Event
    @@ -168,26 +168,26 @@
    # Get buffered paint DC for tool tip
    dc = wx.AutoBufferedPaintDC(self)
    dc.Clear()
    -
    +
    # Set DC drawing style
    dc.SetPen(wx.BLACK_PEN)
    dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
    dc.SetFont(self.Font)
    -
    +
    # Draw Tool tip
    dc.BeginDrawing()
    tip_width, tip_height = self.GetToolTipSize()
    -
    +
    # Draw background rectangle
    dc.DrawRectangle(0, 0, tip_width, tip_height)
    -
    +
    # Draw tool tip text
    line_offset = 0
    for line in self.Tip:
    dc.DrawText(line, 2, line_offset + 2)
    line_width, line_height = dc.GetTextExtent(line)
    line_offset += line_height
    -
    +
    dc.EndDrawing()
    -
    +
    event.Skip()
    --- a/controls/CustomTree.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/CustomTree.py Mon Aug 14 19:13:01 2017 +0300
    @@ -44,30 +44,30 @@
    CT.GenericTreeItem.GetCurrentCheckedImage = GetCurrentCheckedImage
    class CustomTree(CT.CustomTreeCtrl):
    -
    +
    def __init__(self, *args, **kwargs):
    CT.CustomTreeCtrl.__init__(self, *args, **kwargs)
    -
    +
    self.BackgroundBitmap = None
    self.BackgroundAlign = wx.ALIGN_LEFT|wx.ALIGN_TOP
    -
    +
    self.AddMenu = None
    self.Enabled = False
    -
    +
    self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
    self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
    -
    +
    def SetBackgroundBitmap(self, bitmap, align):
    self.BackgroundBitmap = bitmap
    self.BackgroundAlign = align
    -
    +
    def SetImageListCheck(self, sizex, sizey, imglist=None):
    CT.CustomTreeCtrl.SetImageListCheck(self, sizex, sizey, imglist=None)
    -
    +
    self.ExtraImages = {}
    for image in ["function", "functionBlock", "program"]:
    self.ExtraImages[image] = self._imageListCheck.Add(GetBitmap(image.upper()))
    -
    +
    def SetItemExtraImage(self, item, bitmap):
    dc = wx.ClientDC(self)
    image = self.ExtraImages.get(bitmap)
    @@ -76,45 +76,45 @@
    else:
    item.SetExtraImage(None)
    self.CalculateSize(item, dc)
    - self.RefreshLine(item)
    -
    + self.RefreshLine(item)
    +
    def SetAddMenu(self, add_menu):
    self.AddMenu = add_menu
    -
    +
    def Enable(self, enabled):
    self.Enabled = enabled
    -
    +
    def GetBitmapRect(self):
    client_size = self.GetClientSize()
    bitmap_size = self.BackgroundBitmap.GetSize()
    -
    +
    if self.BackgroundAlign & wx.ALIGN_RIGHT:
    x = client_size[0] - bitmap_size[0]
    elif self.BackgroundAlign & wx.ALIGN_CENTER_HORIZONTAL:
    x = (client_size[0] - bitmap_size[0]) / 2
    else:
    x = 0
    -
    +
    if self.BackgroundAlign & wx.ALIGN_BOTTOM:
    y = client_size[1] - bitmap_size[1]
    elif self.BackgroundAlign & wx.ALIGN_CENTER_VERTICAL:
    y = (client_size[1] - bitmap_size[1]) / 2
    else:
    y = 0
    -
    +
    return wx.Rect(x, y, bitmap_size[0], bitmap_size[1])
    -
    +
    def OnLeftUp(self, event):
    if self.Enabled:
    pos = event.GetPosition()
    item, flags = self.HitTest(pos)
    -
    +
    bitmap_rect = self.GetBitmapRect()
    - if (bitmap_rect.InsideXY(pos.x, pos.y) or
    + if (bitmap_rect.InsideXY(pos.x, pos.y) or
    flags & wx.TREE_HITTEST_NOWHERE) and self.AddMenu is not None:
    wx.CallAfter(self.PopupMenuXY, self.AddMenu, pos.x, pos.y)
    event.Skip()
    -
    +
    def OnEraseBackground(self, event):
    dc = event.GetDC()
    @@ -122,12 +122,12 @@
    dc = wx.ClientDC(self)
    rect = self.GetUpdateRegion().GetBox()
    dc.SetClippingRect(rect)
    -
    +
    dc.Clear()
    -
    +
    bitmap_rect = self.GetBitmapRect()
    dc.DrawBitmap(self.BackgroundBitmap, bitmap_rect.x, bitmap_rect.y)
    -
    +
    def OnScroll(self, event):
    wx.CallAfter(self.Refresh)
    event.Skip()
    --- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -83,23 +83,23 @@
    min_value = range_min
    elif range_min is not None:
    min_value = min(min_value, range_min)
    -
    +
    # Update maximal range value
    if max_value is None:
    max_value = range_max
    elif range_min is not None:
    max_value = max(max_value, range_max)
    -
    +
    # Calculate range center and width if at least one valid range is defined
    if min_value is not None and max_value is not None:
    center = (min_value + max_value) / 2.
    range_size = max(1.0, max_value - min_value)
    -
    +
    # Set default center and with if no valid range is defined
    else:
    center = 0.5
    range_size = 1.0
    -
    +
    # Return range expended from 10 %
    return center - range_size * 0.55, center + range_size * 0.55
    @@ -113,7 +113,7 @@
    """
    class DebugVariableGraphicDropTarget(wx.TextDropTarget):
    -
    +
    def __init__(self, parent, window):
    """
    Constructor
    @@ -123,7 +123,7 @@
    wx.TextDropTarget.__init__(self)
    self.ParentControl = parent
    self.ParentWindow = window
    -
    +
    def __del__(self):
    """
    Destructor
    @@ -132,7 +132,7 @@
    # Panel
    self.ParentControl = None
    self.ParentWindow = None
    -
    +
    def OnDragOver(self, x, y, d):
    """
    Function called when mouse is dragged over Drop Target
    @@ -142,9 +142,9 @@
    """
    # Signal parent that mouse is dragged over
    self.ParentControl.OnMouseDragging(x, y)
    -
    +
    return wx.TextDropTarget.OnDragOver(self, x, y, d)
    -
    +
    def OnDropText(self, x, y, data):
    """
    Function called when mouse is released in Drop Target
    @@ -154,9 +154,9 @@
    """
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    -
    +
    message = None
    -
    +
    # Check that data is valid regarding DebugVariablePanel
    try:
    values = eval(data)
    @@ -165,54 +165,54 @@
    except:
    message = _("Invalid value \"%s\" for debug variable")%data
    values = None
    -
    +
    # Display message if data is invalid
    if message is not None:
    wx.CallAfter(self.ShowMessage, message)
    -
    +
    # Data contain a reference to a variable to debug
    elif values[1] == "debug":
    target_idx = self.ParentControl.GetIndex()
    -
    +
    # If mouse is dropped in graph canvas bounding box and graph is
    # not 3D canvas, graphs will be merged
    rect = self.ParentControl.GetAxesBoundingBox()
    if not self.ParentControl.Is3DCanvas() and rect.InsideXY(x, y):
    # Default merge type is parallel
    merge_type = GRAPH_PARALLEL
    -
    +
    # If mouse is dropped in left part of graph canvas, graph
    # wall be merged orthogonally
    - merge_rect = wx.Rect(rect.x, rect.y,
    + merge_rect = wx.Rect(rect.x, rect.y,
    rect.width / 2., rect.height)
    if merge_rect.InsideXY(x, y):
    merge_type = GRAPH_ORTHOGONAL
    -
    +
    # Merge graphs
    - wx.CallAfter(self.ParentWindow.MergeGraphs,
    - values[0], target_idx,
    + wx.CallAfter(self.ParentWindow.MergeGraphs,
    + values[0], target_idx,
    merge_type, force=True)
    -
    +
    else:
    width, height = self.ParentControl.GetSize()
    -
    +
    # Get Before which Viewer the variable has to be moved or added
    # according to the position of mouse in Viewer.
    if y > height / 2:
    target_idx += 1
    -
    +
    # Drag'n Drop is an internal is an internal move inside Debug
    - # Variable Panel
    + # Variable Panel
    if len(values) > 2 and values[2] == "move":
    - self.ParentWindow.MoveValue(values[0],
    + self.ParentWindow.MoveValue(values[0],
    target_idx)
    -
    +
    # Drag'n Drop was initiated by another control of Beremiz
    else:
    - self.ParentWindow.InsertValue(values[0],
    - target_idx,
    + self.ParentWindow.InsertValue(values[0],
    + target_idx,
    force=True)
    -
    +
    def OnLeave(self):
    """
    Function called when mouse is leave Drop Target
    @@ -220,15 +220,15 @@
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    return wx.TextDropTarget.OnLeave(self)
    -
    +
    def ShowMessage(self, message):
    """
    Show error message in Error Dialog
    @param message: Error message to display
    """
    - dialog = wx.MessageDialog(self.ParentWindow,
    - message,
    - _("Error"),
    + dialog = wx.MessageDialog(self.ParentWindow,
    + message,
    + _("Error"),
    wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    @@ -243,7 +243,7 @@
    """
    class DebugVariableGraphicViewer(DebugVariableViewer, FigureCanvas):
    -
    +
    def __init__(self, parent, window, items, graph_type):
    """
    Constructor
    @@ -253,36 +253,36 @@
    @param graph_type: Graph display type (Parallel or orthogonal)
    """
    DebugVariableViewer.__init__(self, window, items)
    -
    +
    self.GraphType = graph_type # Graph type display
    self.CursorTick = None # Tick of the graph cursor
    -
    +
    # Mouse position when start dragging
    self.MouseStartPos = None
    # Tick when moving tick start
    self.StartCursorTick = None
    # Canvas size when starting to resize canvas
    - self.CanvasStartSize = None
    -
    + self.CanvasStartSize = None
    +
    # List of current displayed contextual buttons
    self.ContextualButtons = []
    # Reference to item for which contextual buttons was displayed
    self.ContextualButtonsItem = None
    -
    +
    # Flag indicating that zoom fit current displayed data range or whole
    # data range if False
    self.ZoomFit = False
    -
    +
    # Create figure for drawing graphs
    self.Figure = matplotlib.figure.Figure(facecolor='w')
    # Defined border around figure in canvas
    - self.Figure.subplotpars.update(top=0.95, left=0.1,
    + self.Figure.subplotpars.update(top=0.95, left=0.1,
    bottom=0.1, right=0.95)
    -
    +
    FigureCanvas.__init__(self, parent, -1, self.Figure)
    self.SetWindowStyle(wx.WANTS_CHARS)
    self.SetBackgroundColour(wx.WHITE)
    -
    +
    # Bind wx events
    self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
    self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    @@ -290,42 +290,42 @@
    self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
    self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    self.Bind(wx.EVT_SIZE, self.OnResize)
    -
    +
    # Set canvas min size
    canvas_size = self.GetCanvasMinSize()
    self.SetMinSize(canvas_size)
    -
    +
    # Define Viewer drop target
    self.SetDropTarget(DebugVariableGraphicDropTarget(self, window))
    -
    +
    # Connect matplotlib events
    self.mpl_connect('button_press_event', self.OnCanvasButtonPressed)
    self.mpl_connect('motion_notify_event', self.OnCanvasMotion)
    self.mpl_connect('button_release_event', self.OnCanvasButtonReleased)
    self.mpl_connect('scroll_event', self.OnCanvasScroll)
    -
    +
    # Add buttons for zooming on current displayed data range
    self.Buttons.append(
    GraphButton(0, 0, "fit_graph", self.OnZoomFitButton))
    -
    +
    # Add buttons for changing canvas size with predefined height
    for size, bitmap in zip(
    [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI],
    ["minimize_graph", "middle_graph", "maximize_graph"]):
    self.Buttons.append(
    - GraphButton(0, 0, bitmap,
    + GraphButton(0, 0, bitmap,
    self.GetOnChangeSizeButton(size)))
    -
    +
    # Add buttons for exporting graph values to clipboard and close graph
    for bitmap, callback in [
    ("export_graph_mini", self.OnExportGraphButton),
    ("delete_graph", self.OnCloseButton)]:
    self.Buttons.append(GraphButton(0, 0, bitmap, callback))
    -
    +
    # Update graphs elements
    self.ResetGraphics()
    self.RefreshLabelsPosition(canvas_size.height)
    -
    +
    def AddItem(self, item):
    """
    Add an item to the list of items displayed by Viewer
    @@ -333,14 +333,14 @@
    """
    DebugVariableViewer.AddItem(self, item)
    self.ResetGraphics()
    -
    +
    def RemoveItem(self, item):
    """
    Remove an item from the list of items displayed by Viewer
    @param item: Item to remove from the list
    """
    DebugVariableViewer.RemoveItem(self, item)
    -
    +
    # If list of items is not empty
    if not self.ItemsIsEmpty():
    # Return to parallel graph if there is only one item
    @@ -348,51 +348,51 @@
    if len(self.Items) == 1:
    self.GraphType = GRAPH_PARALLEL
    self.ResetGraphics()
    -
    +
    def SetCursorTick(self, cursor_tick):
    """
    Set cursor tick
    @param cursor_tick: Cursor tick
    """
    self.CursorTick = cursor_tick
    -
    +
    def SetZoomFit(self, zoom_fit):
    """
    Set flag indicating that zoom fit current displayed data range
    @param zoom_fit: Flag for zoom fit (False: zoom fit whole data range)
    """
    - # Flag is different from the actual one
    + # Flag is different from the actual one
    if zoom_fit != self.ZoomFit:
    # Save new flag value
    self.ZoomFit = zoom_fit
    -
    +
    # Update button for zoom fit bitmap
    self.Buttons[0].SetBitmap("full_graph" if zoom_fit else "fit_graph")
    -
    +
    # Refresh canvas
    self.RefreshViewer()
    -
    +
    def SubscribeAllDataConsumers(self):
    """
    Function that unsubscribe and remove every item that store values of
    a variable that doesn't exist in PLC anymore
    """
    DebugVariableViewer.SubscribeAllDataConsumers(self)
    -
    +
    # Graph still have data to display
    if not self.ItemsIsEmpty():
    # Reset flag indicating that zoom fit current displayed data range
    self.SetZoomFit(False)
    -
    +
    self.ResetGraphics()
    -
    +
    def Is3DCanvas(self):
    """
    Return if Viewer is a 3D canvas
    @return: True if Viewer is a 3D canvas
    """
    return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
    -
    +
    def GetButtons(self):
    """
    Return list of buttons defined in Viewer
    @@ -400,7 +400,7 @@
    """
    # Add contextual buttons to default buttons
    return self.Buttons + self.ContextualButtons
    -
    +
    def PopupContextualButtons(self, item, rect, direction=wx.RIGHT):
    """
    Show contextual menu for item aside a label of this item defined
    @@ -412,19 +412,19 @@
    # Return immediately if contextual menu for item is already shown
    if self.ContextualButtonsItem == item:
    return
    -
    +
    # Close already shown contextual menu
    self.DismissContextualButtons()
    -
    +
    # Save item for which contextual menu is shown
    self.ContextualButtonsItem = item
    -
    +
    # If item variable is forced, add button for release variable to
    # contextual menu
    if self.ContextualButtonsItem.IsForced():
    self.ContextualButtons.append(
    GraphButton(0, 0, "release", self.OnReleaseItemButton))
    -
    +
    # Add other buttons to contextual menu
    for bitmap, callback in [
    ("force", self.OnForceItemButton),
    @@ -432,13 +432,13 @@
    ("delete_graph", self.OnRemoveItemButton)]:
    self.ContextualButtons.append(
    GraphButton(0, 0, bitmap, callback))
    -
    +
    # If buttons are shown at left side or upper side of rect, positions
    # will be set in reverse order
    buttons = self.ContextualButtons[:]
    if direction in [wx.TOP, wx.LEFT]:
    buttons.reverse()
    -
    +
    # Set contextual menu buttons position aside rect depending on
    # direction given
    offset = 0
    @@ -458,10 +458,10 @@
    offset += h
    button.SetPosition(x, y)
    button.Show()
    -
    +
    # Refresh canvas
    self.ParentWindow.ForceRefresh()
    -
    +
    def DismissContextualButtons(self):
    """
    Close current shown contextual menu
    @@ -469,14 +469,14 @@
    # Return immediately if no contextual menu is shown
    if self.ContextualButtonsItem is None:
    return
    -
    +
    # Reset variables corresponding to contextual menu
    self.ContextualButtonsItem = None
    self.ContextualButtons = []
    -
    +
    # Refresh canvas
    self.ParentWindow.ForceRefresh()
    -
    +
    def IsOverContextualButton(self, x, y):
    """
    Return if point is over one contextual button of Viewer
    @@ -488,7 +488,7 @@
    if button.HitTest(x, y):
    return button
    return None
    -
    +
    def ExportGraph(self, item=None):
    """
    Export item(s) data to clipboard in CSV format
    @@ -497,17 +497,17 @@
    """
    self.ParentWindow.CopyDataToClipboard(
    [(item, [entry for entry in item.GetData()])
    - for item in (self.Items
    - if item is None
    + for item in (self.Items
    + if item is None
    else [item])])
    -
    +
    def OnZoomFitButton(self):
    """
    Function called when Viewer Zoom Fit button is pressed
    """
    # Toggle zoom fit flag value
    self.SetZoomFit(not self.ZoomFit)
    -
    +
    def GetOnChangeSizeButton(self, height):
    """
    Function that generate callback function for change Viewer height to
    @@ -518,32 +518,32 @@
    def OnChangeSizeButton():
    self.SetCanvasHeight(height)
    return OnChangeSizeButton
    -
    +
    def OnExportGraphButton(self):
    """
    Function called when Viewer Export button is pressed
    """
    # Export data of every item in Viewer
    self.ExportGraph()
    -
    +
    def OnForceItemButton(self):
    """
    Function called when contextual menu Force button is pressed
    """
    - # Open dialog for forcing item variable value
    + # Open dialog for forcing item variable value
    self.ForceValue(self.ContextualButtonsItem)
    # Close contextual menu
    self.DismissContextualButtons()
    -
    +
    def OnReleaseItemButton(self):
    """
    Function called when contextual menu Release button is pressed
    """
    - # Release item variable value
    + # Release item variable value
    self.ReleaseValue(self.ContextualButtonsItem)
    # Close contextual menu
    self.DismissContextualButtons()
    -
    +
    def OnExportItemGraphButton(self):
    """
    Function called when contextual menu Export button is pressed
    @@ -552,17 +552,17 @@
    self.ExportGraph(self.ContextualButtonsItem)
    # Close contextual menu
    self.DismissContextualButtons()
    -
    - def OnRemoveItemButton(self):
    +
    + def OnRemoveItemButton(self):
    """
    Function called when contextual menu Remove button is pressed
    """
    # Remove item from Viewer
    - wx.CallAfter(self.ParentWindow.DeleteValue, self,
    + wx.CallAfter(self.ParentWindow.DeleteValue, self,
    self.ContextualButtonsItem)
    # Close contextual menu
    self.DismissContextualButtons()
    -
    +
    def HandleCursorMove(self, event):
    """
    Update Cursor position according to mouse position and graph type
    @@ -571,7 +571,7 @@
    start_tick, end_tick = self.ParentWindow.GetRange()
    cursor_tick = None
    items = self.ItemsDict.values()
    -
    +
    # Graph is orthogonal
    if self.GraphType == GRAPH_ORTHOGONAL:
    # Extract items data displayed in canvas figure
    @@ -579,31 +579,31 @@
    end_tick = max(end_tick, start_tick)
    x_data = items[0].GetData(start_tick, end_tick)
    y_data = items[1].GetData(start_tick, end_tick)
    -
    +
    # Search for the nearest point from mouse position
    if len(x_data) > 0 and len(y_data) > 0:
    - length = min(len(x_data), len(y_data))
    + length = min(len(x_data), len(y_data))
    d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + \
    (y_data[:length,1]-event.ydata) ** 2)
    -
    +
    # Set cursor tick to the tick of this point
    cursor_tick = x_data[numpy.argmin(d), 0]
    -
    +
    # Graph is parallel
    else:
    # Extract items tick
    data = items[0].GetData(start_tick, end_tick)
    -
    +
    # Search for point that tick is the nearest from mouse X position
    # and set cursor tick to the tick of this point
    if len(data) > 0:
    cursor_tick = data[numpy.argmin(
    numpy.abs(data[:,0] - event.xdata)), 0]
    -
    +
    # Update cursor tick
    if cursor_tick is not None:
    self.ParentWindow.SetCursorTick(cursor_tick)
    -
    +
    def OnCanvasButtonPressed(self, event):
    """
    Function called when a button of mouse is pressed
    @@ -613,18 +613,18 @@
    # comparing to wx
    width, height = self.GetSize()
    x, y = event.x, height - event.y
    -
    +
    # Return immediately if mouse is over a button
    if self.IsOverButton(x, y):
    - return
    -
    + return
    +
    # Mouse was clicked inside graph figure
    if event.inaxes == self.Axes:
    -
    +
    # Find if it was on an item label
    item_idx = None
    # Check every label paired with corresponding item
    - for i, t in ([pair for pair in enumerate(self.AxesLabels)] +
    + for i, t in ([pair for pair in enumerate(self.AxesLabels)] +
    [pair for pair in enumerate(self.Labels)]):
    # Get label bounding box
    (x0, y0), (x1, y1) = t.get_window_extent().get_points()
    @@ -633,46 +633,46 @@
    if rect.InsideXY(x, y):
    item_idx = i
    break
    -
    +
    # If an item label have been clicked
    if item_idx is not None:
    # Hide buttons and contextual buttons
    self.ShowButtons(False)
    self.DismissContextualButtons()
    -
    +
    # Start a drag'n drop from mouse position in wx coordinate of
    # parent
    xw, yw = self.GetPosition()
    - self.ParentWindow.StartDragNDrop(self,
    - self.ItemsDict.values()[item_idx],
    + self.ParentWindow.StartDragNDrop(self,
    + self.ItemsDict.values()[item_idx],
    x + xw, y + yw, # Current mouse position
    x + xw, y + yw) # Mouse position when button was clicked
    -
    +
    # Don't handle mouse button if canvas is 3D and let matplotlib do
    # the default behavior (rotate 3D axes)
    elif not self.Is3DCanvas():
    # Save mouse position when clicked
    self.MouseStartPos = wx.Point(x, y)
    -
    +
    # Mouse button was left button, start moving cursor
    if event.button == 1:
    # Save current tick in case a drag'n drop is initiate to
    # restore it
    self.StartCursorTick = self.CursorTick
    -
    +
    self.HandleCursorMove(event)
    -
    +
    # Mouse button is middle button and graph is parallel, start
    # moving graph along X coordinate (tick)
    elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
    self.StartCursorTick = self.ParentWindow.GetRange()[0]
    -
    +
    # Mouse was clicked outside graph figure and over resize highlight with
    # left button, start resizing Viewer
    elif event.button == 1 and event.y <= 5:
    self.MouseStartPos = wx.Point(x, y)
    self.CanvasStartSize = height
    -
    +
    def OnCanvasButtonReleased(self, event):
    """
    Function called when a button of mouse is released
    @@ -686,37 +686,37 @@
    # Give mouse position in wx coordinate of parent
    self.ParentWindow.StopDragNDrop(item.GetVariable(),
    xw + event.x, yw + height - event.y)
    -
    +
    else:
    # Reset any move in progress
    self.MouseStartPos = None
    self.CanvasStartSize = None
    -
    +
    # Handle button under mouse if it exist
    width, height = self.GetSize()
    self.HandleButton(event.x, height - event.y)
    -
    +
    def OnCanvasMotion(self, event):
    """
    Function called when a button of mouse is moved over Viewer
    @param event: Mouse event
    """
    width, height = self.GetSize()
    -
    +
    # If a drag'n drop is in progress, move canvas dragged
    if self.ParentWindow.IsDragging():
    xw, yw = self.GetPosition()
    # Give mouse position in wx coordinate of parent
    self.ParentWindow.MoveDragNDrop(
    - xw + event.x,
    + xw + event.x,
    yw + height - event.y)
    -
    - # If a Viewer resize is in progress, change Viewer size
    +
    + # If a Viewer resize is in progress, change Viewer size
    elif event.button == 1 and self.CanvasStartSize is not None:
    width, height = self.GetSize()
    self.SetCanvasHeight(
    self.CanvasStartSize + height - event.y - self.MouseStartPos.y)
    -
    +
    # If no button is pressed, show or hide contextual buttons or resize
    # highlight
    elif event.button is None:
    @@ -729,13 +729,13 @@
    wx.LEFT, wx.BOTTOM] # Directions for Labels
    else: # Graph is orthogonal in 3D
    directions = [wx.LEFT] * len(self.Labels)
    -
    +
    # Find if mouse is over an item label
    item_idx = None
    menu_direction = None
    for (i, t), dir in zip(
    - [pair for pair in enumerate(self.AxesLabels)] +
    - [pair for pair in enumerate(self.Labels)],
    + [pair for pair in enumerate(self.AxesLabels)] +
    + [pair for pair in enumerate(self.Labels)],
    directions):
    # Check every label paired with corresponding item
    (x0, y0), (x1, y1) = t.get_window_extent().get_points()
    @@ -745,19 +745,19 @@
    item_idx = i
    menu_direction = dir
    break
    -
    - # If mouse is over an item label,
    +
    + # If mouse is over an item label,
    if item_idx is not None:
    self.PopupContextualButtons(
    - self.ItemsDict.values()[item_idx],
    + self.ItemsDict.values()[item_idx],
    rect, menu_direction)
    return
    -
    +
    # If mouse isn't over a contextual menu, hide the current shown one
    - # if it exists
    + # if it exists
    if self.IsOverContextualButton(event.x, height - event.y) is None:
    self.DismissContextualButtons()
    -
    +
    # Update resize highlight
    if event.y <= 5:
    if self.SetHighlight(HIGHLIGHT_RESIZE):
    @@ -767,46 +767,46 @@
    if self.SetHighlight(HIGHLIGHT_NONE):
    self.SetCursor(wx.NullCursor)
    self.ParentWindow.ForceRefresh()
    -
    - # Handle buttons if canvas is not 3D
    +
    + # Handle buttons if canvas is not 3D
    elif not self.Is3DCanvas():
    -
    +
    # If left button is pressed
    if event.button == 1:
    -
    +
    # Mouse is inside graph figure
    if event.inaxes == self.Axes:
    -
    +
    # If a cursor move is in progress, update cursor position
    if self.MouseStartPos is not None:
    self.HandleCursorMove(event)
    -
    +
    # Mouse is outside graph figure, cursor move is in progress and
    # there is only one item in Viewer, start a drag'n drop
    elif self.MouseStartPos is not None and len(self.Items) == 1:
    xw, yw = self.GetPosition()
    self.ParentWindow.SetCursorTick(self.StartCursorTick)
    - self.ParentWindow.StartDragNDrop(self,
    + self.ParentWindow.StartDragNDrop(self,
    self.ItemsDict.values()[0],
    # Current mouse position
    event.x + xw, height - event.y + yw,
    # Mouse position when button was clicked
    self.MouseStartPos.x + xw,
    self.MouseStartPos.y + yw)
    -
    +
    # If middle button is pressed and moving graph along X coordinate
    # is in progress
    elif event.button == 2 and self.GraphType == GRAPH_PARALLEL and \
    self.MouseStartPos is not None:
    start_tick, end_tick = self.ParentWindow.GetRange()
    rect = self.GetAxesBoundingBox()
    -
    +
    # Move graph along X coordinate
    self.ParentWindow.SetCanvasPosition(
    - self.StartCursorTick +
    + self.StartCursorTick +
    (self.MouseStartPos.x - event.x) *
    (end_tick - start_tick) / rect.width)
    -
    +
    def OnCanvasScroll(self, event):
    """
    Function called when a wheel mouse is use in Viewer
    @@ -815,7 +815,7 @@
    # Change X range of graphs if mouse is in canvas figure and ctrl is
    # pressed
    if event.inaxes is not None and event.guiEvent.ControlDown():
    -
    +
    # Calculate position of fixed tick point according to graph type
    # and mouse position
    if self.GraphType == GRAPH_ORTHOGONAL:
    @@ -824,10 +824,10 @@
    else:
    tick = event.xdata
    self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
    -
    +
    # Vetoing event to prevent parent panel to be scrolled
    self.ParentWindow.VetoScrollEvent = True
    -
    +
    def OnLeftDClick(self, event):
    """
    Function called when a left mouse button is double clicked
    @@ -841,17 +841,17 @@
    self.ParentWindow.SetCursorTick(self.StartCursorTick)
    # Toggle to text Viewer(s)
    self.ParentWindow.ToggleViewerType(self)
    -
    +
    else:
    event.Skip()
    -
    +
    # Cursor tick move for each arrow key
    KEY_CURSOR_INCREMENT = {
    wx.WXK_LEFT: -1,
    wx.WXK_RIGHT: 1,
    wx.WXK_UP: 10,
    wx.WXK_DOWN: -10}
    -
    +
    def OnKeyDown(self, event):
    """
    Function called when key is pressed
    @@ -863,7 +863,7 @@
    if move is not None:
    self.ParentWindow.MoveCursorTick(move)
    event.Skip()
    -
    +
    def OnLeave(self, event):
    """
    Function called when mouse leave Viewer
    @@ -876,7 +876,7 @@
    DebugVariableViewer.OnLeave(self, event)
    else:
    event.Skip()
    -
    +
    def GetCanvasMinSize(self):
    """
    Return the minimum size of Viewer so that all items label can be
    @@ -885,10 +885,10 @@
    """
    # The minimum height take in account the height of all items, padding
    # inside figure and border around figure
    - return wx.Size(200,
    - CANVAS_BORDER[0] + CANVAS_BORDER[1] +
    + return wx.Size(200,
    + CANVAS_BORDER[0] + CANVAS_BORDER[1] +
    2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
    -
    +
    def SetCanvasHeight(self, height):
    """
    Set Viewer size checking that it respects Viewer minimum size
    @@ -899,7 +899,7 @@
    self.SetMinSize(wx.Size(min_width, height))
    self.RefreshLabelsPosition(height)
    self.ParentWindow.RefreshGraphicsSizer()
    -
    +
    def GetAxesBoundingBox(self, parent_coordinate=False):
    """
    Return figure bounding box in wx coordinate
    @@ -911,15 +911,15 @@
    ax, ay, aw, ah = self.figure.gca().get_position().bounds
    bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
    aw * width + 2, ah * height + 1)
    -
    +
    # If parent_coordinate, add Viewer position in parent
    if parent_coordinate:
    xw, yw = self.GetPosition()
    bbox.x += xw
    bbox.y += yw
    -
    +
    return bbox
    -
    +
    def RefreshHighlight(self, x, y):
    """
    Refresh Viewer highlight according to mouse position
    @@ -927,7 +927,7 @@
    @param y: Y coordinate of mouse pointer
    """
    width, height = self.GetSize()
    -
    +
    # Mouse is over Viewer figure and graph is not 3D
    bbox = self.GetAxesBoundingBox()
    if bbox.InsideXY(x, y) and not self.Is3DCanvas():
    @@ -935,28 +935,28 @@
    # Mouse is over Viewer left part of figure
    if rect.InsideXY(x, y):
    self.SetHighlight(HIGHLIGHT_LEFT)
    -
    +
    # Mouse is over Viewer right part of figure
    else:
    self.SetHighlight(HIGHLIGHT_RIGHT)
    -
    +
    # Mouse is over upper part of Viewer
    elif y < height / 2:
    # Viewer is upper one in Debug Variable Panel, show highlight
    if self.ParentWindow.IsViewerFirst(self):
    self.SetHighlight(HIGHLIGHT_BEFORE)
    -
    +
    # Viewer is not the upper one, show highlight in previous one
    # It prevents highlight to move when mouse leave one Viewer to
    # another
    else:
    self.SetHighlight(HIGHLIGHT_NONE)
    self.ParentWindow.HighlightPreviousViewer(self)
    -
    +
    # Mouse is over lower part of Viewer
    else:
    self.SetHighlight(HIGHLIGHT_AFTER)
    -
    +
    def OnAxesMotion(self, event):
    """
    Function overriding default function called when mouse is dragged for
    @@ -969,7 +969,7 @@
    if current_time - self.LastMotionTime > REFRESH_PERIOD:
    self.LastMotionTime = current_time
    Axes3D._on_move(self.Axes, event)
    -
    +
    def GetAddTextFunction(self):
    """
    Return function for adding text in figure according to graph type
    @@ -987,58 +987,58 @@
    self.Axes.set_prop_cycle(cycler('color',color))
    else:
    self.Axes.set_color_cycle(color)
    -
    +
    def ResetGraphics(self):
    """
    Reset figure and graphical elements displayed in it
    - Called any time list of items or graph type change
    + Called any time list of items or graph type change
    """
    # Clear figure from any axes defined
    self.Figure.clear()
    -
    +
    # Add 3D projection if graph is in 3D
    if self.Is3DCanvas():
    self.Axes = self.Figure.gca(projection='3d')
    self.SetAxesColor(['b'])
    -
    - # Override function to prevent too much refresh when graph is
    +
    + # Override function to prevent too much refresh when graph is
    # rotated
    self.LastMotionTime = gettime()
    setattr(self.Axes, "_on_move", self.OnAxesMotion)
    -
    +
    # Init graph mouse event so that graph can be rotated
    self.Axes.mouse_init()
    -
    +
    # Set size of Z axis labels
    self.Axes.tick_params(axis='z', labelsize='small')
    -
    +
    else:
    self.Axes = self.Figure.gca()
    self.SetAxesColor(COLOR_CYCLE)
    -
    +
    # Set size of X and Y axis labels
    self.Axes.tick_params(axis='x', labelsize='small')
    self.Axes.tick_params(axis='y', labelsize='small')
    -
    +
    # Init variables storing graphical elements added to figure
    self.Plots = [] # List of curves
    self.VLine = None # Vertical line for cursor
    self.HLine = None # Horizontal line for cursor (only orthogonal 2D)
    self.AxesLabels = [] # List of items variable path text label
    self.Labels = [] # List of items text label
    -
    - # Get function to add a text in figure according to graph type
    +
    + # Get function to add a text in figure according to graph type
    add_text_func = self.GetAddTextFunction()
    -
    +
    # Graph type is parallel or orthogonal in 3D
    if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
    num_item = len(self.Items)
    for idx in xrange(num_item):
    -
    +
    # Get color from color cycle (black if only one item)
    color = ('k' if num_item == 1
    else COLOR_CYCLE[idx % len(COLOR_CYCLE)])
    -
    +
    # In 3D graph items variable label are not displayed as text
    # in figure, but as axis title
    if not self.Is3DCanvas():
    @@ -1046,12 +1046,12 @@
    self.AxesLabels.append(
    add_text_func(size='small', color=color,
    verticalalignment='top'))
    -
    +
    # Items variable labels are in figure lower right corner
    self.Labels.append(
    - add_text_func(size='large', color=color,
    + add_text_func(size='large', color=color,
    horizontalalignment='right'))
    -
    +
    # Graph type is orthogonal in 2D
    else:
    # X coordinate labels are in figure lower side
    @@ -1059,7 +1059,7 @@
    self.Labels.append(
    add_text_func(size='large',
    horizontalalignment='right'))
    -
    +
    # Y coordinate labels are vertical and in figure left side
    self.AxesLabels.append(
    add_text_func(size='small', rotation='vertical',
    @@ -1067,11 +1067,11 @@
    self.Labels.append(
    add_text_func(size='large', rotation='vertical',
    verticalalignment='top'))
    -
    +
    # Refresh position of labels according to Viewer size
    width, height = self.GetSize()
    self.RefreshLabelsPosition(height)
    -
    +
    def RefreshLabelsPosition(self, height):
    """
    Function called when mouse leave Viewer
    @@ -1087,30 +1087,30 @@
    graph_ratio = 1. / (
    (1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio)
    * height) # Divide by figure height in pixel
    -
    +
    # Update position of figure (keeping up and bottom border the same
    # size)
    self.Figure.subplotpars.update(
    - top= 1.0 - CANVAS_BORDER[1] * canvas_ratio,
    + top= 1.0 - CANVAS_BORDER[1] * canvas_ratio,
    bottom= CANVAS_BORDER[0] * canvas_ratio)
    -
    +
    # Update position of items labels
    if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
    num_item = len(self.Items)
    for idx in xrange(num_item):
    -
    +
    # In 3D graph items variable label are not displayed
    if not self.Is3DCanvas():
    # Items variable labels are in figure upper left corner
    self.AxesLabels[idx].set_position(
    - (0.05,
    - 1.0 - (CANVAS_PADDING +
    + (0.05,
    + 1.0 - (CANVAS_PADDING +
    AXES_LABEL_HEIGHT * idx) * graph_ratio))
    -
    +
    # Items variable labels are in figure lower right corner
    self.Labels[idx].set_position(
    - (0.95,
    - CANVAS_PADDING * graph_ratio +
    + (0.95,
    + CANVAS_PADDING * graph_ratio +
    (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
    else:
    # X coordinate labels are in figure lower side
    @@ -1118,16 +1118,16 @@
    (0.1, CANVAS_PADDING * graph_ratio))
    self.Labels[0].set_position(
    (0.95, CANVAS_PADDING * graph_ratio))
    -
    +
    # Y coordinate labels are vertical and in figure left side
    self.AxesLabels[1].set_position(
    (0.05, 2 * CANVAS_PADDING * graph_ratio))
    self.Labels[1].set_position(
    (0.05, 1.0 - CANVAS_PADDING * graph_ratio))
    -
    +
    # Update subplots
    self.Figure.subplots_adjust()
    -
    +
    def RefreshViewer(self, refresh_graphics=True):
    """
    Function called to refresh displayed by matplotlib canvas
    @@ -1138,55 +1138,55 @@
    if refresh_graphics:
    # Get tick range of values to display
    start_tick, end_tick = self.ParentWindow.GetRange()
    -
    +
    # Graph is parallel
    - if self.GraphType == GRAPH_PARALLEL:
    + if self.GraphType == GRAPH_PARALLEL:
    # Init list of data range for each variable displayed
    ranges = []
    -
    +
    # Get data and range for each variable displayed
    for idx, item in enumerate(self.Items):
    data, min_value, max_value = item.GetDataAndValueRange(
    start_tick, end_tick, not self.ZoomFit)
    -
    +
    # Check that data is not empty
    if data is not None:
    # Add variable range to list of variable data range
    ranges.append((min_value, max_value))
    -
    +
    # Add plot to canvas if not yet created
    if len(self.Plots) <= idx:
    self.Plots.append(
    self.Axes.plot(data[:, 0], data[:, 1])[0])
    -
    +
    # Set data to already created plot in canvas
    else:
    self.Plots[idx].set_data(data[:, 0], data[:, 1])
    -
    +
    # Get X and Y axis ranges
    x_min, x_max = start_tick, end_tick
    y_min, y_max = merge_ranges(ranges)
    -
    +
    # Display cursor in canvas if a cursor tick is defined and it is
    # include in values tick range
    - if (self.CursorTick is not None and
    + if (self.CursorTick is not None and
    start_tick <= self.CursorTick <= end_tick):
    -
    +
    # Define a vertical line to display cursor position if no
    # line is already defined
    if self.VLine is None:
    - self.VLine = self.Axes.axvline(self.CursorTick,
    + self.VLine = self.Axes.axvline(self.CursorTick,
    color=CURSOR_COLOR)
    -
    +
    # Set value of vertical line if already defined
    else:
    self.VLine.set_xdata((self.CursorTick, self.CursorTick))
    self.VLine.set_visible(True)
    -
    +
    # Hide vertical line if cursor tick is not defined or reset
    elif self.VLine is not None:
    self.VLine.set_visible(False)
    -
    +
    # Graph is orthogonal
    else:
    # Update tick range, removing ticks that don't have a value for
    @@ -1194,76 +1194,76 @@
    start_tick = max(start_tick, self.GetItemsMinCommonTick())
    end_tick = max(end_tick, start_tick)
    items = self.ItemsDict.values()
    -
    +
    # Get data and range for first variable (X coordinate)
    x_data, x_min, x_max = items[0].GetDataAndValueRange(
    start_tick, end_tick, not self.ZoomFit)
    # Get data and range for second variable (Y coordinate)
    y_data, y_min, y_max = items[1].GetDataAndValueRange(
    start_tick, end_tick, not self.ZoomFit)
    -
    +
    # Normalize X and Y coordinates value range
    x_min, x_max = merge_ranges([(x_min, x_max)])
    y_min, y_max = merge_ranges([(y_min, y_max)])
    -
    - # Get X and Y coordinates for cursor if cursor tick is defined
    +
    + # Get X and Y coordinates for cursor if cursor tick is defined
    if self.CursorTick is not None:
    x_cursor, x_forced = items[0].GetValue(
    self.CursorTick, raw=True)
    y_cursor, y_forced = items[1].GetValue(
    self.CursorTick, raw=True)
    -
    +
    # Get common data length so that each value has an x and y
    # coordinate
    length = (min(len(x_data), len(y_data))
    if x_data is not None and y_data is not None
    else 0)
    -
    - # Graph is orthogonal 2D
    +
    + # Graph is orthogonal 2D
    if len(self.Items) < 3:
    -
    +
    # Check that x and y data are not empty
    if x_data is not None and y_data is not None:
    -
    +
    # Add plot to canvas if not yet created
    if len(self.Plots) == 0:
    self.Plots.append(
    - self.Axes.plot(x_data[:, 1][:length],
    + self.Axes.plot(x_data[:, 1][:length],
    y_data[:, 1][:length])[0])
    -
    +
    # Set data to already created plot in canvas
    else:
    self.Plots[0].set_data(
    - x_data[:, 1][:length],
    + x_data[:, 1][:length],
    y_data[:, 1][:length])
    -
    +
    # Display cursor in canvas if a cursor tick is defined and it is
    # include in values tick range
    - if (self.CursorTick is not None and
    + if (self.CursorTick is not None and
    start_tick <= self.CursorTick <= end_tick):
    -
    +
    # Define a vertical line to display cursor x coordinate
    # if no line is already defined
    if self.VLine is None:
    - self.VLine = self.Axes.axvline(x_cursor,
    + self.VLine = self.Axes.axvline(x_cursor,
    color=CURSOR_COLOR)
    # Set value of vertical line if already defined
    else:
    self.VLine.set_xdata((x_cursor, x_cursor))
    -
    -
    +
    +
    # Define a horizontal line to display cursor y
    # coordinate if no line is already defined
    if self.HLine is None:
    - self.HLine = self.Axes.axhline(y_cursor,
    + self.HLine = self.Axes.axhline(y_cursor,
    color=CURSOR_COLOR)
    # Set value of horizontal line if already defined
    else:
    self.HLine.set_ydata((y_cursor, y_cursor))
    -
    +
    self.VLine.set_visible(True)
    self.HLine.set_visible(True)
    -
    +
    # Hide vertical and horizontal line if cursor tick is not
    # defined or reset
    else:
    @@ -1271,42 +1271,42 @@
    self.VLine.set_visible(False)
    if self.HLine is not None:
    self.HLine.set_visible(False)
    -
    +
    # Graph is orthogonal 3D
    else:
    # Remove all plots already defined in 3D canvas
    while len(self.Axes.lines) > 0:
    self.Axes.lines.pop()
    -
    +
    # Get data and range for third variable (Z coordinate)
    z_data, z_min, z_max = items[2].GetDataAndValueRange(
    start_tick, end_tick, not self.ZoomFit)
    -
    +
    # Normalize Z coordinate value range
    z_min, z_max = merge_ranges([(z_min, z_max)])
    -
    +
    # Check that x, y and z data are not empty
    - if (x_data is not None and y_data is not None and
    + if (x_data is not None and y_data is not None and
    z_data is not None):
    -
    +
    # Get common data length so that each value has an x, y
    # and z coordinate
    length = min(length, len(z_data))
    -
    +
    # Add plot to canvas
    self.Axes.plot(x_data[:, 1][:length],
    y_data[:, 1][:length],
    zs = z_data[:, 1][:length])
    -
    +
    # Display cursor in canvas if a cursor tick is defined and
    # it is include in values tick range
    - if (self.CursorTick is not None and
    + if (self.CursorTick is not None and
    start_tick <= self.CursorTick <= end_tick):
    -
    +
    # Get Z coordinate for cursor
    z_cursor, z_forced = items[2].GetValue(
    self.CursorTick, raw=True)
    -
    +
    # Add 3 lines parallel to x, y and z axis to display
    # cursor position in 3D
    for kwargs in [{"xs": numpy.array([x_min, x_max])},
    @@ -1319,14 +1319,14 @@
    kwargs.setdefault(param, value)
    kwargs["color"] = CURSOR_COLOR
    self.Axes.plot(**kwargs)
    -
    +
    # Set Z axis limits
    self.Axes.set_zlim(z_min, z_max)
    -
    +
    # Set X and Y axis limits
    self.Axes.set_xlim(x_min, x_max)
    self.Axes.set_ylim(y_min, y_max)
    -
    +
    # Get value and forced flag for each variable displayed in graph
    # If cursor tick is not defined get value and flag of last received
    # or get value and flag of variable at cursor tick
    @@ -1335,34 +1335,34 @@
    if self.CursorTick is not None
    else (item.GetValue(), item.IsForced()))
    for item in self.Items])
    -
    +
    # Get path of each variable displayed simplified using panel variable
    # name mask
    - labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask())
    + labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask())
    for item in self.Items]
    -
    - # Get style for each variable according to
    +
    + # Get style for each variable according to
    styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced)
    -
    +
    # Graph is orthogonal 3D, set variables path as 3D axis label
    if self.Is3DCanvas():
    - for idx, label_func in enumerate([self.Axes.set_xlabel,
    + for idx, label_func in enumerate([self.Axes.set_xlabel,
    self.Axes.set_ylabel,
    self.Axes.set_zlabel]):
    label_func(labels[idx], fontdict={'size': 'small',
    'color': COLOR_CYCLE[idx]})
    -
    +
    # Graph is not orthogonal 3D, set variables path in axes labels
    else:
    for label, text in zip(self.AxesLabels, labels):
    label.set_text(text)
    -
    +
    # Set value label text and style according to value and forced flag for
    # each variable displayed
    for label, value, style in zip(self.Labels, values, styles):
    label.set_text(value)
    label.set_style(style)
    -
    +
    # Refresh figure
    self.draw()
    @@ -1372,51 +1372,49 @@
    """
    # Render figure using agg
    FigureCanvasAgg.draw(self)
    -
    +
    # Get bitmap of figure rendered
    self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
    - if wx.VERSION < (3, 0, 0):
    + if wx.VERSION < (3, 0, 0):
    self.bitmap.UseAlpha()
    -
    +
    # Create DC for rendering graphics in bitmap
    destDC = wx.MemoryDC()
    destDC.SelectObject(self.bitmap)
    -
    +
    # Get Graphics Context for DC, for anti-aliased and transparent
    # rendering
    destGC = wx.GCDC(destDC)
    -
    +
    destGC.BeginDrawing()
    -
    +
    # Get canvas size and figure bounding box in canvas
    width, height = self.GetSize()
    bbox = self.GetAxesBoundingBox()
    -
    +
    # If highlight to display is resize, draw thick grey line at bottom
    - # side of canvas
    + # side of canvas
    if self.Highlight == HIGHLIGHT_RESIZE:
    destGC.SetPen(HIGHLIGHT_RESIZE_PEN)
    destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH)
    destGC.DrawRectangle(0, height - 5, width, 5)
    -
    +
    # If highlight to display is merging graph, draw 50% transparent blue
    # rectangle on left or right part of figure depending on highlight type
    elif self.Highlight in [HIGHLIGHT_LEFT, HIGHLIGHT_RIGHT]:
    destGC.SetPen(HIGHLIGHT_DROP_PEN)
    destGC.SetBrush(HIGHLIGHT_DROP_BRUSH)
    -
    - x_offset = (bbox.width / 2
    +
    + x_offset = (bbox.width / 2
    if self.Highlight == HIGHLIGHT_RIGHT
    else 0)
    - destGC.DrawRectangle(bbox.x + x_offset, bbox.y,
    + destGC.DrawRectangle(bbox.x + x_offset, bbox.y,
    bbox.width / 2, bbox.height)
    -
    +
    # Draw other Viewer common elements
    self.DrawCommonElements(destGC, self.GetButtons())
    -
    +
    destGC.EndDrawing()
    -
    +
    self._isDrawn = True
    self.gui_repaint(drawDC=drawDC)
    -
    -
    --- a/controls/DebugVariablePanel/DebugVariableItem.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/DebugVariableItem.py Mon Aug 14 19:13:01 2017 +0300
    @@ -44,7 +44,7 @@
    """
    class DebugVariableItem(DebugDataConsumer):
    -
    +
    def __init__(self, parent, variable, store_data=False):
    """
    Constructor
    @@ -52,24 +52,24 @@
    @param variable: Path of variable to debug
    """
    DebugDataConsumer.__init__(self)
    -
    +
    self.Parent = parent
    self.Variable = variable
    self.StoreData = store_data
    -
    +
    # Get Variable data type
    self.RefreshVariableType()
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Reset reference to debug variable panel
    self.Parent = None
    -
    +
    def SetVariable(self, variable):
    """
    - Set path of variable
    + Set path of variable
    @param variable: Path of variable to debug
    """
    if self.Parent is not None and self.Variable != variable:
    @@ -77,46 +77,46 @@
    self.Variable = variable
    # Get Variable data type
    self.RefreshVariableType()
    -
    +
    # Refresh debug variable panel
    self.Parent.RefreshView()
    -
    +
    def GetVariable(self, mask=None):
    """
    - Return path of variable
    + Return path of variable
    @param mask: Mask to apply to variable path [var_name, '*',...]
    @return: String containing masked variable path
    """
    # Apply mask to variable name
    if mask is not None:
    # '#' correspond to parts that are different between all items
    -
    +
    # Extract variable path parts
    parts = self.Variable.split('.')
    # Adjust mask size to size of variable path
    mask = mask + ['*'] * max(0, len(parts) - len(mask))
    -
    +
    # Store previous mask
    last = None
    # Init masked variable path
    variable = ""
    -
    +
    for m, p in zip(mask, parts):
    # Part is not masked, add part prefixed with '.' is previous
    # wasn't masked
    if m == '*':
    variable += ('.' if last == '*' else '') + p
    -
    +
    # Part is mask, add '..' if first or previous wasn't masked
    elif last is None or last == '*':
    variable += '..'
    -
    +
    last = m
    -
    +
    return variable
    -
    +
    return self.Variable
    -
    +
    def RefreshVariableType(self):
    """
    Get and store variable data type
    @@ -124,14 +124,14 @@
    self.VariableType = self.Parent.GetDataType(self.Variable)
    # Reset data stored
    self.ResetData()
    -
    +
    def GetVariableType(self):
    """
    Return variable data type
    @return: Variable data type
    """
    return self.VariableType
    -
    +
    def GetData(self, start_tick=None, end_tick=None):
    """
    Return data stored contained in given range
    @@ -142,7 +142,7 @@
    # Return immediately if data empty or none
    if self.Data is None or len(self.Data) == 0:
    return self.Data
    -
    +
    # Find nearest data outside given range indexes
    start_idx = (self.GetNearestData(start_tick, -1)
    if start_tick is not None
    @@ -150,10 +150,10 @@
    end_idx = (self.GetNearestData(end_tick, 1)
    if end_tick is not None
    else len(self.Data))
    -
    +
    # Return data between indexes
    return self.Data[start_idx:end_idx]
    -
    +
    def GetRawValue(self, index):
    """
    Return raw value at given index for string variables
    @@ -164,14 +164,14 @@
    index < len(self.RawData)):
    return self.RawData[index][0]
    return ""
    -
    +
    def GetValueRange(self):
    """
    Return variable value range
    @return: (minimum_value, maximum_value)
    """
    return self.MinValue, self.MaxValue
    -
    +
    def GetDataAndValueRange(self, start_tick, end_tick, full_range=True):
    """
    Return variable data and value range for a given tick range
    @@ -179,16 +179,16 @@
    @param end_tick: end tick of given range (default None, last data)
    @param full_range: Value range is calculated on whole data (False: only
    calculated on data in given range)
    - @return: (numpy.array([(tick, value, forced),...]),
    + @return: (numpy.array([(tick, value, forced),...]),
    min_value, max_value)
    """
    # Get data in given tick range
    data = self.GetData(start_tick, end_tick)
    -
    +
    # Value range is calculated on whole data
    if full_range:
    return data, self.MinValue, self.MaxValue
    -
    +
    # Check that data in given range is not empty
    values = data[:, 1]
    if len(values) > 0:
    @@ -196,10 +196,10 @@
    return (data,
    data[numpy.argmin(values), 1],
    data[numpy.argmax(values), 1])
    -
    +
    # Return default values
    return data, None, None
    -
    +
    def ResetData(self):
    """
    Reset data stored when store data option enabled
    @@ -207,31 +207,31 @@
    if self.StoreData and self.IsNumVariable():
    # Init table storing data
    self.Data = numpy.array([]).reshape(0, 3)
    -
    +
    # Init table storing raw data if variable is strin
    self.RawData = ([]
    if self.VariableType in ["STRING", "WSTRING"]
    else None)
    -
    +
    # Init Value range variables
    self.MinValue = None
    self.MaxValue = None
    -
    +
    else:
    self.Data = None
    -
    +
    # Init variable value
    self.Value = ""
    -
    +
    def IsNumVariable(self):
    """
    Return if variable data type is numeric. String variables are
    considered as numeric (string CRC)
    @return: True if data type is numeric
    """
    - return (self.Parent.IsNumType(self.VariableType) or
    + return (self.Parent.IsNumType(self.VariableType) or
    self.VariableType in ["STRING", "WSTRING"])
    -
    +
    def NewValues(self, ticks, values):
    """
    Function called by debug thread when a new debug value is available
    @@ -240,24 +240,24 @@
    @param forced: Forced flag, True if value is forced (default: False)
    """
    DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None)
    -
    +
    if self.Data is not None:
    -
    +
    if self.VariableType in ["STRING", "WSTRING"]:
    last_raw_data = (self.RawData[-1]
    if len(self.RawData) > 0 else None)
    last_raw_data_idx = len(self.RawData) - 1
    -
    +
    data_values = []
    for tick, (value, forced) in zip(ticks, values):
    # Translate forced flag to float for storing in Data table
    forced_value = float(forced)
    -
    +
    # String data value is CRC
    num_value = (binascii.crc32(value) & STRING_CRC_MASK
    if self.VariableType in ["STRING", "WSTRING"]
    else float(value))
    -
    +
    # Update variable range values
    self.MinValue = (min(self.MinValue, num_value)
    if self.MinValue is not None
    @@ -265,10 +265,10 @@
    self.MaxValue = (max(self.MaxValue, num_value)
    if self.MaxValue is not None
    else num_value)
    -
    +
    # In the case of string variables, we store raw string value and
    # forced flag in raw data table. Only changes in this two values
    - # are stored. Index to the corresponding raw value is stored in
    + # are stored. Index to the corresponding raw value is stored in
    # data third column
    if self.VariableType in ["STRING", "WSTRING"]:
    raw_data = (value, forced_value)
    @@ -277,20 +277,20 @@
    last_raw_data = raw_data
    self.RawData.append(raw_data)
    extra_value = last_raw_data_idx
    -
    +
    # In other case, data third column is forced flag
    else:
    extra_value = forced_value
    -
    +
    data_values.append(
    [float(tick), num_value, extra_value])
    -
    +
    # Add New data to stored data table
    self.Data = numpy.append(self.Data, data_values, axis=0)
    -
    +
    # Signal to debug variable panel to refresh
    self.Parent.HasNewData = True
    -
    +
    def SetForced(self, forced):
    """
    Update Forced flag
    @@ -299,10 +299,10 @@
    # Store forced flag
    if self.Forced != forced:
    self.Forced = forced
    -
    +
    # Signal to debug variable panel to refresh
    self.Parent.HasNewData = True
    -
    +
    def SetValue(self, value):
    """
    Update value.
    @@ -314,14 +314,14 @@
    self.VariableType == "WSTRING" and
    value.startswith('"') and value.endswith('"')):
    value = value[1:-1]
    -
    +
    # Store variable value
    if self.Value != value:
    self.Value = value
    -
    +
    # Signal to debug variable panel to refresh
    self.Parent.HasNewData = True
    -
    +
    def GetValue(self, tick=None, raw=False):
    """
    Return current value or value and forced flag for tick given
    @@ -329,26 +329,26 @@
    """
    # If tick given and stored data option enabled
    if tick is not None and self.Data is not None:
    -
    +
    # Return current value and forced flag if data empty
    if len(self.Data) == 0:
    return self.Value, self.IsForced()
    -
    +
    # Get index of nearest data from tick given
    idx = self.GetNearestData(tick, 0)
    -
    +
    # Get value and forced flag at given index
    value, forced = self.RawData[int(self.Data[idx, 2])] \
    if self.VariableType in ["STRING", "WSTRING"] \
    else self.Data[idx, 1:3]
    -
    +
    # Get raw value if asked
    if not raw:
    value = TYPE_TRANSLATOR.get(
    self.VariableType, str)(value)
    -
    +
    return value, forced
    -
    +
    # Return raw value if asked
    if not raw and self.VariableType in ["STRING", "WSTRING"]:
    return TYPE_TRANSLATOR.get(
    @@ -368,16 +368,16 @@
    # Return immediately if data is empty
    if self.Data is None:
    return None
    -
    +
    # Extract data ticks
    ticks = self.Data[:, 0]
    -
    +
    # Get nearest data from tick
    idx = numpy.argmin(abs(ticks - tick))
    -
    +
    # Adjust data index according to constraint
    if (adjust < 0 and ticks[idx] > tick and idx > 0 or
    adjust > 0 and ticks[idx] < tick and idx < len(ticks)):
    idx += adjust
    -
    +
    return idx
    --- a/controls/DebugVariablePanel/DebugVariablePanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/DebugVariablePanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -67,11 +67,11 @@
    for item, data in variables:
    if len(data) == 0:
    continue
    -
    +
    next_tick = (data[0][0]
    if next_tick is None
    else min(next_tick, data[0][0]))
    -
    +
    return next_tick
    #-------------------------------------------------------------------------------
    @@ -84,7 +84,7 @@
    """
    class DebugVariableDropTarget(wx.TextDropTarget):
    -
    +
    def __init__(self, window):
    """
    Constructor
    @@ -92,14 +92,14 @@
    """
    wx.TextDropTarget.__init__(self)
    self.ParentWindow = window
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Remove reference to Debug Variable Panel
    self.ParentWindow = None
    -
    +
    def OnDragOver(self, x, y, d):
    """
    Function called when mouse is dragged over Drop Target
    @@ -110,7 +110,7 @@
    # Signal Debug Variable Panel to refresh highlight giving mouse position
    self.ParentWindow.RefreshHighlight(x, y)
    return wx.TextDropTarget.OnDragOver(self, x, y, d)
    -
    +
    def OnDropText(self, x, y, data):
    """
    Function called when mouse is released in Drop Target
    @@ -120,9 +120,9 @@
    """
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    -
    +
    message = None
    -
    +
    # Check that data is valid regarding DebugVariablePanel
    try:
    values = eval(data)
    @@ -131,23 +131,23 @@
    except:
    message = _("Invalid value \"%s\" for debug variable")%data
    values = None
    -
    +
    # Display message if data is invalid
    if message is not None:
    wx.CallAfter(self.ShowMessage, message)
    -
    +
    # Data contain a reference to a variable to debug
    elif values[1] == "debug":
    -
    +
    # Drag'n Drop is an internal is an internal move inside Debug
    - # Variable Panel
    + # Variable Panel
    if len(values) > 2 and values[2] == "move":
    self.ParentWindow.MoveValue(values[0])
    -
    +
    # Drag'n Drop was initiated by another control of Beremiz
    else:
    self.ParentWindow.InsertValue(values[0], force=True)
    -
    +
    def OnLeave(self):
    """
    Function called when mouse is leave Drop Target
    @@ -155,15 +155,15 @@
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    return wx.TextDropTarget.OnLeave(self)
    -
    +
    def ShowMessage(self, message):
    """
    Show error message in Error Dialog
    @param message: Error message to display
    """
    - dialog = wx.MessageDialog(self.ParentWindow,
    - message,
    - _("Error"),
    + dialog = wx.MessageDialog(self.ParentWindow,
    + message,
    + _("Error"),
    wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    @@ -178,7 +178,7 @@
    """
    class DebugVariablePanel(wx.Panel, DebugViewer):
    -
    +
    def __init__(self, parent, producer, window):
    """
    Constructor
    @@ -188,48 +188,48 @@
    @param window: Reference to Beremiz frame
    """
    wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
    -
    +
    # Save Reference to Beremiz frame
    self.ParentWindow = window
    -
    +
    # Variable storing flag indicating that variable displayed in table
    # received new value and then table need to be refreshed
    self.HasNewData = False
    -
    +
    # Variable storing flag indicating that refresh has been forced, and
    # that next time refresh is possible, it will be done even if no new
    # data is available
    self.Force = False
    -
    +
    self.SetBackgroundColour(wx.WHITE)
    -
    +
    main_sizer = wx.BoxSizer(wx.VERTICAL)
    -
    +
    self.Ticks = numpy.array([]) # List of tick received
    self.StartTick = 0 # Tick starting range of data displayed
    self.Fixed = False # Flag that range of data is fixed
    self.CursorTick = None # Tick of cursor for displaying values
    -
    +
    self.DraggingAxesPanel = None
    self.DraggingAxesBoundingBox = None
    self.DraggingAxesMousePos = None
    self.VetoScrollEvent = False
    -
    +
    self.VariableNameMask = []
    -
    +
    self.GraphicPanels = []
    -
    +
    graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
    main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
    -
    +
    range_label = wx.StaticText(self, label=_('Range:'))
    graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
    - graphics_button_sizer.AddWindow(self.CanvasRange, 1,
    + graphics_button_sizer.AddWindow(self.CanvasRange, 1,
    border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
    -
    +
    self.CanvasRange.Clear()
    default_range_idx = 0
    for idx, (text, value) in enumerate(RANGE_VALUES):
    @@ -237,44 +237,44 @@
    if text == "1s":
    default_range_idx = idx
    self.CanvasRange.SetSelection(default_range_idx)
    -
    +
    for name, bitmap, help in [
    ("CurrentButton", "current", _("Go to current value")),
    ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
    - button = wx.lib.buttons.GenBitmapButton(self,
    - bitmap=GetBitmap(bitmap),
    + button = wx.lib.buttons.GenBitmapButton(self,
    + bitmap=GetBitmap(bitmap),
    size=wx.Size(28, 28), style=wx.NO_BORDER)
    button.SetToolTipString(help)
    setattr(self, name, button)
    self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
    graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
    -
    - self.CanvasPosition = wx.ScrollBar(self,
    +
    + self.CanvasPosition = wx.ScrollBar(self,
    size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
    - self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK,
    + self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK,
    self.OnPositionChanging, self.CanvasPosition)
    - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP,
    + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP,
    self.OnPositionChanging, self.CanvasPosition)
    - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN,
    + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN,
    self.OnPositionChanging, self.CanvasPosition)
    - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP,
    + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP,
    self.OnPositionChanging, self.CanvasPosition)
    - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN,
    + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN,
    self.OnPositionChanging, self.CanvasPosition)
    main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
    -
    +
    self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
    main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
    -
    +
    self.TickLabel = wx.StaticText(self)
    self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
    -
    +
    self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
    self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
    -
    +
    self.TickTimeLabel = wx.StaticText(self)
    self.TickSizer.AddWindow(self.TickTimeLabel)
    -
    +
    self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
    self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
    self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
    @@ -282,17 +282,17 @@
    self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint)
    self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
    self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel)
    -
    +
    main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
    -
    +
    self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
    self.GraphicsWindow.SetSizer(self.GraphicsSizer)
    -
    +
    DebugViewer.__init__(self, producer, True)
    -
    +
    self.SetSizer(main_sizer)
    self.SetTickTime()
    -
    +
    def SetTickTime(self, ticktime=0):
    """
    Set Ticktime for calculate data range according to time range selected
    @@ -300,26 +300,26 @@
    """
    # Save ticktime
    self.Ticktime = ticktime
    -
    +
    # Set ticktime to millisecond if undefined
    if self.Ticktime == 0:
    self.Ticktime = MILLISECOND
    -
    +
    # Calculate range to apply to data
    self.CurrentRange = RANGE_VALUES[
    self.CanvasRange.GetSelection()][1] / self.Ticktime
    -
    +
    def SetDataProducer(self, producer):
    """
    Set Data Producer
    @param producer: Data Producer
    """
    DebugViewer.SetDataProducer(self, producer)
    -
    +
    # Set ticktime if data producer is available
    if self.DataProducer is not None:
    self.SetTickTime(self.DataProducer.GetTicktime())
    -
    +
    def RefreshNewData(self):
    """
    Called to refresh Panel according to values received by variables
    @@ -329,50 +329,50 @@
    if self.HasNewData or self.Force:
    self.HasNewData = False
    self.RefreshView()
    -
    +
    DebugViewer.RefreshNewData(self)
    -
    +
    def NewDataAvailable(self, ticks):
    """
    Called by DataProducer for each tick captured or by panel to refresh
    graphs
    @param tick: PLC tick captured
    - All other parameters are passed to refresh function
    + All other parameters are passed to refresh function
    """
    # If tick given
    if ticks is not None:
    tick = ticks[-1]
    -
    +
    # Save tick as start tick for range if data is still empty
    if len(self.Ticks) == 0:
    self.StartTick = ticks[0]
    -
    +
    # Add tick to list of ticks received
    self.Ticks = numpy.append(self.Ticks, ticks)
    -
    +
    # Update start tick for range if range follow ticks received
    if not self.Fixed or tick < self.StartTick + self.CurrentRange:
    self.StartTick = max(self.StartTick, tick - self.CurrentRange)
    -
    +
    # Force refresh if graph is fixed because range of data received
    # is too small to fill data range selected
    if self.Fixed and \
    self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
    self.Force = True
    -
    +
    self.HasNewData = False
    self.RefreshView()
    -
    +
    else:
    DebugViewer.NewDataAvailable(self, ticks)
    -
    +
    def ForceRefresh(self):
    """
    Called to force refresh of graphs
    """
    self.Force = True
    wx.CallAfter(self.NewDataAvailable, None)
    -
    +
    def SetCursorTick(self, cursor_tick):
    """
    Set Cursor for displaying values of items at a tick given
    @@ -381,37 +381,37 @@
    # Save cursor tick
    self.CursorTick = cursor_tick
    self.Fixed = cursor_tick is not None
    - self.UpdateCursorTick()
    -
    + self.UpdateCursorTick()
    +
    def MoveCursorTick(self, move):
    if self.CursorTick is not None:
    - cursor_tick = max(self.Ticks[0],
    - min(self.CursorTick + move,
    + cursor_tick = max(self.Ticks[0],
    + min(self.CursorTick + move,
    self.Ticks[-1]))
    cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
    if self.Ticks[cursor_tick_idx] == self.CursorTick:
    - cursor_tick_idx = max(0,
    - min(cursor_tick_idx + abs(move) / move,
    + cursor_tick_idx = max(0,
    + min(cursor_tick_idx + abs(move) / move,
    len(self.Ticks) - 1))
    self.CursorTick = self.Ticks[cursor_tick_idx]
    self.StartTick = max(self.Ticks[
    - numpy.argmin(numpy.abs(self.Ticks -
    + numpy.argmin(numpy.abs(self.Ticks -
    self.CursorTick + self.CurrentRange))],
    min(self.StartTick, self.CursorTick))
    self.RefreshCanvasPosition()
    - self.UpdateCursorTick()
    -
    + self.UpdateCursorTick()
    +
    def ResetCursorTick(self):
    self.CursorTick = None
    self.Fixed = False
    self.UpdateCursorTick()
    -
    +
    def UpdateCursorTick(self):
    for panel in self.GraphicPanels:
    if isinstance(panel, DebugVariableGraphicViewer):
    panel.SetCursorTick(self.CursorTick)
    self.ForceRefresh()
    -
    +
    def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
    if len(panel.GetItems()) > 1:
    self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
    @@ -424,21 +424,21 @@
    self.DraggingAxesPanel = panel
    self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True)
    self.DraggingAxesMousePos = wx.Point(
    - x_mouse_start - self.DraggingAxesBoundingBox.x,
    + x_mouse_start - self.DraggingAxesBoundingBox.x,
    y_mouse_start - self.DraggingAxesBoundingBox.y)
    self.MoveDragNDrop(x_mouse, y_mouse)
    -
    +
    def MoveDragNDrop(self, x_mouse, y_mouse):
    self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
    self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
    self.RefreshHighlight(x_mouse, y_mouse)
    -
    +
    def RefreshHighlight(self, x_mouse, y_mouse):
    for idx, panel in enumerate(self.GraphicPanels):
    x, y = panel.GetPosition()
    width, height = panel.GetSize()
    rect = wx.Rect(x, y, width, height)
    - if (rect.InsideXY(x_mouse, y_mouse) or
    + if (rect.InsideXY(x_mouse, y_mouse) or
    idx == 0 and y_mouse < 0 or
    idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
    panel.RefreshHighlight(x_mouse - x, y_mouse - y)
    @@ -448,7 +448,7 @@
    self.RefreshView()
    else:
    self.ForceRefresh()
    -
    +
    def ResetHighlight(self):
    for panel in self.GraphicPanels:
    panel.SetHighlight(HIGHLIGHT_NONE)
    @@ -456,10 +456,10 @@
    self.RefreshView()
    else:
    self.ForceRefresh()
    -
    +
    def IsDragging(self):
    return self.DraggingAxesPanel is not None
    -
    +
    def GetDraggingAxesClippingRegion(self, panel):
    x, y = panel.GetPosition()
    width, height = panel.GetSize()
    @@ -468,12 +468,12 @@
    bbox.x -= x
    bbox.y -= y
    return bbox
    -
    +
    def GetDraggingAxesPosition(self, panel):
    x, y = panel.GetPosition()
    return wx.Point(self.DraggingAxesBoundingBox.x - x,
    self.DraggingAxesBoundingBox.y - y)
    -
    +
    def StopDragNDrop(self, variable, x_mouse, y_mouse):
    if self.DraggingAxesPanel not in self.GraphicPanels:
    self.DraggingAxesPanel.Destroy()
    @@ -504,25 +504,25 @@
    idx += 1
    wx.CallAfter(self.MoveValue, variable, idx, True)
    self.ForceRefresh()
    - return
    + return
    width, height = self.GraphicsWindow.GetVirtualSize()
    rect = wx.Rect(0, 0, width, height)
    if rect.InsideXY(x_mouse, y_mouse):
    wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True)
    self.ForceRefresh()
    -
    +
    def RefreshGraphicsSizer(self):
    self.GraphicsSizer.Clear()
    -
    +
    for panel in self.GraphicPanels:
    self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
    -
    +
    self.GraphicsSizer.Layout()
    self.RefreshGraphicsWindowScrollbars()
    -
    +
    def RefreshView(self):
    self.RefreshCanvasPosition()
    -
    +
    width, height = self.GraphicsWindow.GetVirtualSize()
    bitmap = wx.EmptyBitmap(width, height)
    dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
    @@ -531,22 +531,22 @@
    if self.DraggingAxesPanel is not None:
    destBBox = self.DraggingAxesBoundingBox
    srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
    -
    +
    srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
    srcDC = wx.MemoryDC()
    srcDC.SelectObject(srcBmp)
    -
    - dc.Blit(destBBox.x, destBBox.y,
    - int(destBBox.width), int(destBBox.height),
    +
    + dc.Blit(destBBox.x, destBBox.y,
    + int(destBBox.width), int(destBBox.height),
    srcDC, srcBBox.x, srcBBox.y)
    dc.EndDrawing()
    -
    +
    if not self.Fixed or self.Force:
    self.Force = False
    refresh_graphics = True
    else:
    refresh_graphics = False
    -
    +
    if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
    self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
    for panel in self.GraphicPanels:
    @@ -554,7 +554,7 @@
    panel.RefreshViewer(refresh_graphics)
    else:
    panel.RefreshViewer()
    -
    +
    if self.CursorTick is not None:
    tick = self.CursorTick
    elif len(self.Ticks) > 0:
    @@ -570,27 +570,27 @@
    ((tick_duration % DAY) / HOUR, _("%dh")),
    ((tick_duration % HOUR) / MINUTE, _("%dm")),
    ((tick_duration % MINUTE) / SECOND, _("%ds"))]:
    -
    +
    if value > 0 or not_null:
    duration += format % value
    not_null = True
    -
    - duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND)
    +
    + duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND)
    self.TickTimeLabel.SetLabel("t: %s" % duration)
    else:
    self.TickLabel.SetLabel("")
    self.TickTimeLabel.SetLabel("")
    self.TickSizer.Layout()
    -
    +
    def SubscribeAllDataConsumers(self):
    DebugViewer.SubscribeAllDataConsumers(self)
    -
    +
    if self.DataProducer is not None:
    if self.DataProducer is not None:
    self.SetTickTime(self.DataProducer.GetTicktime())
    -
    +
    self.ResetCursorTick()
    -
    +
    for panel in self.GraphicPanels[:]:
    panel.SubscribeAllDataConsumers()
    if panel.ItemsIsEmpty():
    @@ -598,28 +598,28 @@
    panel.ReleaseMouse()
    self.GraphicPanels.remove(panel)
    panel.Destroy()
    -
    +
    self.ResetVariableNameMask()
    self.RefreshGraphicsSizer()
    self.ForceRefresh()
    -
    +
    def ResetView(self):
    self.UnsubscribeAllDataConsumers()
    -
    +
    self.Fixed = False
    for panel in self.GraphicPanels:
    panel.Destroy()
    self.GraphicPanels = []
    self.ResetVariableNameMask()
    self.RefreshGraphicsSizer()
    -
    +
    def SetCanvasPosition(self, tick):
    tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
    self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
    self.Fixed = True
    self.RefreshCanvasPosition()
    self.ForceRefresh()
    -
    +
    def RefreshCanvasPosition(self):
    if len(self.Ticks) > 0:
    pos = int(self.StartTick - self.Ticks[0])
    @@ -628,7 +628,7 @@
    pos = 0
    range = 0
    self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
    -
    +
    def ChangeRange(self, dir, tick=None):
    current_range = self.CurrentRange
    current_range_idx = self.CanvasRange.GetSelection()
    @@ -644,7 +644,7 @@
    self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
    self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange
    self.ForceRefresh()
    -
    +
    def RefreshRange(self):
    if len(self.Ticks) > 0:
    if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
    @@ -654,7 +654,7 @@
    else:
    self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
    self.ForceRefresh()
    -
    +
    def OnRangeChanged(self, event):
    try:
    self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime
    @@ -662,13 +662,13 @@
    self.CanvasRange.SetValue(str(self.CurrentRange))
    wx.CallAfter(self.RefreshRange)
    event.Skip()
    -
    +
    def OnCurrentButton(self, event):
    if len(self.Ticks) > 0:
    self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
    self.ResetCursorTick()
    event.Skip()
    -
    +
    def CopyDataToClipboard(self, variables):
    text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
    next_tick = NextTick(variables)
    @@ -693,7 +693,7 @@
    text += "%d;%s;\n" % (next_tick, ";".join(values))
    next_tick = NextTick(variables)
    self.ParentWindow.SetCopyBuffer(text)
    -
    +
    def OnExportGraphButton(self, event):
    items = reduce(lambda x, y: x + y,
    [panel.GetItems() for panel in self.GraphicPanels],
    @@ -703,25 +703,25 @@
    if item.IsNumVariable()]
    wx.CallAfter(self.CopyDataToClipboard, variables)
    event.Skip()
    -
    +
    def OnPositionChanging(self, event):
    if len(self.Ticks) > 0:
    self.StartTick = self.Ticks[0] + event.GetPosition()
    self.Fixed = True
    self.ForceRefresh()
    event.Skip()
    -
    +
    def GetRange(self):
    return self.StartTick, self.StartTick + self.CurrentRange
    -
    +
    def GetViewerIndex(self, viewer):
    if viewer in self.GraphicPanels:
    return self.GraphicPanels.index(viewer)
    return None
    -
    +
    def IsViewerFirst(self, viewer):
    return viewer == self.GraphicPanels[0]
    -
    +
    def HighlightPreviousViewer(self, viewer):
    if self.IsViewerFirst(viewer):
    return
    @@ -729,7 +729,7 @@
    if idx is None:
    return
    self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
    -
    +
    def ResetVariableNameMask(self):
    items = []
    for panel in self.GraphicPanels:
    @@ -743,10 +743,10 @@
    self.VariableNameMask = []
    self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
    self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
    -
    +
    def GetVariableNameMask(self):
    return self.VariableNameMask
    -
    +
    def InsertValue(self, iec_path, idx = None, force=False, graph=False):
    for panel in self.GraphicPanels:
    if panel.GetItem(iec_path) is not None:
    @@ -758,7 +758,7 @@
    item = DebugVariableItem(self, iec_path, True)
    result = self.AddDataConsumer(iec_path.upper(), item, True)
    if result is not None or force:
    -
    +
    self.Freeze()
    if item.IsNumVariable() and graph:
    panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
    @@ -774,7 +774,7 @@
    self.RefreshGraphicsSizer()
    self.Thaw()
    self.ForceRefresh()
    -
    +
    def MoveValue(self, iec_path, idx = None, graph=False):
    if idx is None:
    idx = len(self.GraphicPanels)
    @@ -787,9 +787,9 @@
    break
    if source_panel is not None:
    source_panel_idx = self.GraphicPanels.index(source_panel)
    -
    +
    if (len(source_panel.GetItems()) == 1):
    -
    +
    if source_panel_idx < idx:
    self.GraphicPanels.insert(idx, source_panel)
    self.GraphicPanels.pop(source_panel_idx)
    @@ -798,7 +798,7 @@
    self.GraphicPanels.insert(idx, source_panel)
    else:
    return
    -
    +
    else:
    source_panel.RemoveItem(item)
    source_size = source_panel.GetSize()
    @@ -807,22 +807,22 @@
    panel.SetCanvasHeight(source_size.height)
    if self.CursorTick is not None:
    panel.SetCursorTick(self.CursorTick)
    -
    +
    else:
    panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item])
    -
    +
    self.GraphicPanels.insert(idx, panel)
    -
    +
    if source_panel.ItemsIsEmpty():
    if source_panel.HasCapture():
    source_panel.ReleaseMouse()
    source_panel.Destroy()
    self.GraphicPanels.remove(source_panel)
    -
    +
    self.ResetVariableNameMask()
    self.RefreshGraphicsSizer()
    self.ForceRefresh()
    -
    +
    def MergeGraphs(self, source, target_idx, merge_type, force=False):
    source_item = None
    source_panel = None
    @@ -846,11 +846,11 @@
    graph_type = target_panel.GraphType
    if target_panel != source_panel:
    if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
    - merge_type == GRAPH_ORTHOGONAL and
    + merge_type == GRAPH_ORTHOGONAL and
    (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
    graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
    return
    -
    +
    if source_panel is not None:
    source_panel.RemoveItem(source_item)
    if source_panel.ItemsIsEmpty():
    @@ -862,7 +862,7 @@
    target_panel.RemoveItem(source_item)
    else:
    target_panel = None
    -
    +
    if target_panel is not None:
    target_panel.AddItem(source_item)
    target_panel.GraphType = merge_type
    @@ -874,15 +874,15 @@
    else:
    target_panel.SetCanvasHeight(size.height)
    target_panel.ResetGraphics()
    -
    +
    self.ResetVariableNameMask()
    self.RefreshGraphicsSizer()
    self.ForceRefresh()
    -
    +
    def DeleteValue(self, source_panel, item=None):
    source_idx = self.GetViewerIndex(source_panel)
    if source_idx is not None:
    -
    +
    if item is None:
    source_panel.ClearItems()
    source_panel.Destroy()
    @@ -900,7 +900,7 @@
    self.Fixed = False
    self.ResetCursorTick()
    self.ForceRefresh()
    -
    +
    def ToggleViewerType(self, panel):
    panel_idx = self.GetViewerIndex(panel)
    if panel_idx is not None:
    @@ -916,7 +916,7 @@
    panel.Destroy()
    self.RefreshGraphicsSizer()
    self.ForceRefresh()
    -
    +
    def ResetGraphicsValues(self):
    self.Ticks = numpy.array([])
    self.StartTick = 0
    @@ -931,22 +931,22 @@
    posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
    posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
    self.GraphicsWindow.Scroll(posx, posy)
    - self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
    + self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
    vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
    -
    +
    def OnGraphicsWindowEraseBackground(self, event):
    pass
    -
    +
    def OnGraphicsWindowPaint(self, event):
    self.RefreshView()
    event.Skip()
    -
    +
    def OnGraphicsWindowResize(self, event):
    size = self.GetSize()
    for panel in self.GraphicPanels:
    panel_size = panel.GetSize()
    - if (isinstance(panel, DebugVariableGraphicViewer) and
    - panel.GraphType == GRAPH_ORTHOGONAL and
    + if (isinstance(panel, DebugVariableGraphicViewer) and
    + panel.GraphType == GRAPH_ORTHOGONAL and
    panel_size.width == panel_size.height):
    panel.SetCanvasHeight(size.width)
    self.RefreshGraphicsWindowScrollbars()
    --- a/controls/DebugVariablePanel/DebugVariableTextViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/DebugVariableTextViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,7 +39,7 @@
    """
    class DebugVariableTextDropTarget(wx.TextDropTarget):
    -
    +
    def __init__(self, parent, window):
    """
    Constructor
    @@ -49,7 +49,7 @@
    wx.TextDropTarget.__init__(self)
    self.ParentControl = parent
    self.ParentWindow = window
    -
    +
    def __del__(self):
    """
    Destructor
    @@ -58,7 +58,7 @@
    # Panel
    self.ParentControl = None
    self.ParentWindow = None
    -
    +
    def OnDragOver(self, x, y, d):
    """
    Function called when mouse is dragged over Drop Target
    @@ -68,9 +68,9 @@
    """
    # Signal parent that mouse is dragged over
    self.ParentControl.OnMouseDragging(x, y)
    -
    +
    return wx.TextDropTarget.OnDragOver(self, x, y, d)
    -
    +
    def OnDropText(self, x, y, data):
    """
    Function called when mouse is released in Drop Target
    @@ -80,9 +80,9 @@
    """
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    -
    +
    message = None
    -
    +
    # Check that data is valid regarding DebugVariablePanel
    try:
    values = eval(data)
    @@ -91,50 +91,50 @@
    except:
    message = _("Invalid value \"%s\" for debug variable") % data
    values = None
    -
    +
    # Display message if data is invalid
    if message is not None:
    wx.CallAfter(self.ShowMessage, message)
    -
    +
    # Data contain a reference to a variable to debug
    elif values[1] == "debug":
    -
    +
    # Get Before which Viewer the variable has to be moved or added
    # according to the position of mouse in Viewer.
    width, height = self.ParentControl.GetSize()
    target_idx = self.ParentControl.GetIndex()
    if y > height / 2:
    target_idx += 1
    -
    +
    # Drag'n Drop is an internal is an internal move inside Debug
    - # Variable Panel
    + # Variable Panel
    if len(values) > 2 and values[2] == "move":
    - self.ParentWindow.MoveValue(values[0],
    + self.ParentWindow.MoveValue(values[0],
    target_idx)
    -
    +
    # Drag'n Drop was initiated by another control of Beremiz
    else:
    - self.ParentWindow.InsertValue(values[0],
    - target_idx,
    + self.ParentWindow.InsertValue(values[0],
    + target_idx,
    force=True)
    -
    +
    def OnLeave(self):
    """
    Function called when mouse is leave Drop Target
    """
    # Signal Debug Variable Panel to reset highlight
    self.ParentWindow.ResetHighlight()
    -
    +
    return wx.TextDropTarget.OnLeave(self)
    -
    +
    def ShowMessage(self, message):
    """
    Show error message in Error Dialog
    @param message: Error message to display
    """
    - dialog = wx.MessageDialog(self.ParentWindow,
    - message,
    - _("Error"),
    + dialog = wx.MessageDialog(self.ParentWindow,
    + message,
    + _("Error"),
    wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    @@ -149,7 +149,7 @@
    """
    class DebugVariableTextViewer(DebugVariableViewer, wx.Panel):
    -
    +
    def __init__(self, parent, window, items=[]):
    """
    Constructor
    @@ -158,13 +158,13 @@
    @param items: List of DebugVariableItem displayed by Viewer
    """
    DebugVariableViewer.__init__(self, window, items)
    -
    +
    wx.Panel.__init__(self, parent)
    # Set panel background colour
    self.SetBackgroundColour(wx.WHITE)
    # Define panel drop target
    self.SetDropTarget(DebugVariableTextDropTarget(self, window))
    -
    +
    # Bind events
    self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
    self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
    @@ -174,16 +174,16 @@
    self.Bind(wx.EVT_SIZE, self.OnResize)
    self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
    self.Bind(wx.EVT_PAINT, self.OnPaint)
    -
    +
    # Define panel min size for parent sizer layout
    self.SetMinSize(wx.Size(0, 25))
    -
    +
    # Add buttons to Viewer
    for bitmap, callback in [("force", self.OnForceButton),
    ("release", self.OnReleaseButton),
    ("delete_graph", self.OnCloseButton)]:
    self.Buttons.append(GraphButton(0, 0, bitmap, callback))
    -
    +
    def RefreshViewer(self):
    """
    Method that refresh the content displayed by Viewer
    @@ -193,24 +193,24 @@
    bitmap = wx.EmptyBitmap(width, height)
    dc = wx.BufferedDC(wx.ClientDC(self), bitmap)
    dc.Clear()
    -
    +
    # Get Graphics Context for DC, for anti-aliased and transparent
    # rendering
    gc = wx.GCDC(dc)
    -
    +
    gc.BeginDrawing()
    -
    +
    # Get first item
    item = self.ItemsDict.values()[0]
    -
    +
    # Get item variable path masked according Debug Variable Panel mask
    item_path = item.GetVariable(
    self.ParentWindow.GetVariableNameMask())
    -
    +
    # Draw item variable path at Viewer left side
    w, h = gc.GetTextExtent(item_path)
    gc.DrawText(item_path, 20, (height - h) / 2)
    -
    +
    # Update 'Release' button state and text color according to item forced
    # flag value
    item_forced = item.IsForced()
    @@ -218,17 +218,17 @@
    self.RefreshButtonsPosition()
    if item_forced:
    gc.SetTextForeground(wx.BLUE)
    -
    +
    # Draw item current value at right side of Viewer
    item_value = item.GetValue()
    w, h = gc.GetTextExtent(item_value)
    gc.DrawText(item_value, width - 40 - w, (height - h) / 2)
    -
    +
    # Draw other Viewer common elements
    self.DrawCommonElements(gc)
    -
    +
    gc.EndDrawing()
    -
    +
    def OnLeftDown(self, event):
    """
    Function called when mouse left button is pressed
    @@ -236,15 +236,15 @@
    """
    # Get first item
    item = self.ItemsDict.values()[0]
    -
    +
    # Calculate item path bounding box
    width, height = self.GetSize()
    item_path = item.GetVariable(
    self.ParentWindow.GetVariableNameMask())
    w, h = self.GetTextExtent(item_path)
    -
    +
    # Test if mouse has been pressed in this bounding box. In that case
    - # start a move drag'n drop of item variable
    + # start a move drag'n drop of item variable
    x, y = event.GetPosition()
    item_path_bbox = wx.Rect(20, (height - h) / 2, w, h)
    if item_path_bbox.InsideXY(x, y):
    @@ -253,11 +253,11 @@
    dragSource = wx.DropSource(self)
    dragSource.SetData(data)
    dragSource.DoDragDrop()
    -
    +
    # In other case handle event normally
    else:
    event.Skip()
    -
    +
    def OnLeftUp(self, event):
    """
    Function called when mouse left button is released
    @@ -267,7 +267,7 @@
    x, y = event.GetPosition()
    wx.CallAfter(self.HandleButton, x, y)
    event.Skip()
    -
    +
    def OnLeftDClick(self, event):
    """
    Function called when mouse left button is double clicked
    @@ -276,7 +276,7 @@
    # Only numeric variables can be toggled to graph canvas
    if self.ItemsDict.values()[0].IsNumVariable():
    self.ParentWindow.ToggleViewerType(self)
    -
    +
    def OnPaint(self, event):
    """
    Function called when redrawing Viewer content is needed
    --- a/controls/DebugVariablePanel/DebugVariableViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/DebugVariableViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -56,7 +56,7 @@
    """
    class DebugVariableViewer:
    -
    +
    def __init__(self, window, items=[]):
    """
    Constructor
    @@ -67,26 +67,26 @@
    self.ItemsDict = OrderedDict([(item.GetVariable(), item)
    for item in items])
    self.Items = self.ItemsDict.viewvalues()
    -
    +
    # Variable storing current highlight displayed in Viewer
    self.Highlight = HIGHLIGHT_NONE
    # List of buttons
    self.Buttons = []
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Remove reference to Debug Variable Panel
    self.ParentWindow = None
    -
    +
    def GetIndex(self):
    """
    Return position of Viewer in Debug Variable Panel
    @return: Position of Viewer
    """
    return self.ParentWindow.GetViewerIndex(self)
    -
    +
    def GetItem(self, variable):
    """
    Return item storing values of a variable
    @@ -94,28 +94,28 @@
    @return: Item storing values of this variable
    """
    return self.ItemsDict.get(variable, None)
    -
    +
    def GetItems(self):
    """
    Return items displayed by Viewer
    @return: List of items displayed in Viewer
    """
    return self.ItemsDict.values()
    -
    +
    def AddItem(self, item):
    """
    Add an item to the list of items displayed by Viewer
    @param item: Item to add to the list
    """
    self.ItemsDict[item.GetVariable()] = item
    -
    +
    def RemoveItem(self, item):
    """
    Remove an item from the list of items displayed by Viewer
    @param item: Item to remove from the list
    """
    self.ItemsDict.pop(item.GetVariable(), None)
    -
    +
    def ClearItems(self):
    """
    Clear list of items displayed by Viewer
    @@ -123,17 +123,17 @@
    # Unsubscribe every items of the list
    for item in self.Items:
    self.ParentWindow.RemoveDataConsumer(item)
    -
    +
    # Clear list
    self.ItemsDict.clear()
    -
    +
    def ItemsIsEmpty(self):
    """
    Return if list of items displayed by Viewer is empty
    @return: True if list is empty
    """
    return len(self.Items) == 0
    -
    +
    def SubscribeAllDataConsumers(self):
    """
    Function that unsubscribe and remove every item that store values of
    @@ -141,7 +141,7 @@
    """
    for item in self.ItemsDict.values()[:]:
    iec_path = item.GetVariable()
    -
    +
    # Check that variablepath exist in PLC
    if self.ParentWindow.GetDataType(iec_path) is None:
    # If not, unsubscribe and remove it
    @@ -151,30 +151,30 @@
    # If it exist, resubscribe and refresh data type
    self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True)
    item.RefreshVariableType()
    -
    +
    def ResetItemsData(self):
    """
    Reset data stored in every items displayed in Viewer
    """
    for item in self.Items:
    item.ResetData()
    -
    +
    def GetItemsMinCommonTick(self):
    """
    Return the minimum tick common to all iems displayed in Viewer
    @return: Minimum common tick between items
    """
    - return reduce(max, [item.GetData()[0, 0]
    + return reduce(max, [item.GetData()[0, 0]
    for item in self.Items
    if len(item.GetData()) > 0], 0)
    -
    +
    def RefreshViewer(self):
    """
    Method that refresh the content displayed by Viewer
    Need to be overridden by inherited classes
    """
    pass
    -
    +
    def SetHighlight(self, highlight):
    """
    Set Highlight type displayed in Viewer
    @@ -183,17 +183,17 @@
    # Return immediately if highlight don't change
    if self.Highlight == highlight:
    return False
    -
    +
    self.Highlight = highlight
    return True
    -
    +
    def GetButtons(self):
    """
    Return list of buttons defined in Viewer
    @return: List of buttons
    """
    return self.Buttons
    -
    +
    def IsOverButton(self, x, y):
    """
    Return if point is over one button of Viewer
    @@ -205,7 +205,7 @@
    if button.HitTest(x, y):
    return button
    return None
    -
    +
    def HandleButton(self, x, y):
    """
    Search for the button under point and if found execute associated
    @@ -217,10 +217,10 @@
    button = self.IsOverButton(x, y)
    if button is None:
    return False
    -
    +
    button.ProcessCallback()
    return True
    -
    +
    def ShowButtons(self, show):
    """
    Set display state of buttons in Viewer
    @@ -229,27 +229,27 @@
    # Change display of every buttons
    for button in self.Buttons:
    button.Show(show)
    -
    +
    # Refresh button positions
    self.RefreshButtonsPosition()
    self.RefreshViewer()
    -
    +
    def RefreshButtonsPosition(self):
    """
    Function that refresh buttons position in Viewer
    """
    # Get Viewer size
    width, height = self.GetSize()
    -
    +
    # Buttons are align right so we calculate buttons positions in
    # reverse order
    buttons = self.Buttons[:]
    buttons.reverse()
    -
    +
    # Position offset on x coordinate
    x_offset = 0
    for button in buttons:
    - # Buttons are stacked right, removing those that are not active
    + # Buttons are stacked right, removing those that are not active
    if button.IsEnabled():
    # Update button position according to button width and offset
    # on x coordinate
    @@ -257,7 +257,7 @@
    button.SetPosition(width - 5 - w - x_offset, 5)
    # Update offset on x coordinate
    x_offset += w + 2
    -
    +
    def DrawCommonElements(self, dc, buttons=None):
    """
    Function that draw common graphics for every Viewers
    @@ -268,26 +268,26 @@
    """
    # Get Viewer size
    width, height = self.GetSize()
    -
    +
    # Set dc styling for drop before or drop after highlight
    dc.SetPen(HIGHLIGHT_DROP_PEN)
    dc.SetBrush(HIGHLIGHT_DROP_BRUSH)
    -
    +
    # Draw line at upper side of Viewer if highlight is drop before
    if self.Highlight == HIGHLIGHT_BEFORE:
    dc.DrawLine(0, 1, width - 1, 1)
    -
    +
    # Draw line at lower side of Viewer if highlight is drop before
    elif self.Highlight == HIGHLIGHT_AFTER:
    dc.DrawLine(0, height - 1, width - 1, height - 1)
    -
    +
    # If no specific buttons are defined, get default buttons
    if buttons is None:
    buttons = self.Buttons
    # Draw buttons
    for button in buttons:
    button.Draw(dc)
    -
    +
    # If graph dragging is processing
    if self.ParentWindow.IsDragging():
    destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self)
    @@ -295,55 +295,55 @@
    if destBBox.width > 0 and destBBox.height > 0:
    srcPanel = self.ParentWindow.DraggingAxesPanel
    srcBBox = srcPanel.GetAxesBoundingBox()
    -
    +
    srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0)
    srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0)
    -
    +
    srcBmp = _convert_agg_to_wx_bitmap(
    srcPanel.get_renderer(), None)
    srcDC = wx.MemoryDC()
    srcDC.SelectObject(srcBmp)
    -
    - dc.Blit(destBBox.x, destBBox.y,
    - int(destBBox.width), int(destBBox.height),
    +
    + dc.Blit(destBBox.x, destBBox.y,
    + int(destBBox.width), int(destBBox.height),
    srcDC, srcX, srcY)
    -
    +
    def OnEnter(self, event):
    """
    Function called when entering Viewer
    - @param event: wx.MouseEvent
    + @param event: wx.MouseEvent
    """
    # Display buttons
    self.ShowButtons(True)
    event.Skip()
    -
    +
    def OnLeave(self, event):
    """
    Function called when leaving Viewer
    - @param event: wx.MouseEvent
    + @param event: wx.MouseEvent
    """
    # Hide buttons
    self.ShowButtons(False)
    event.Skip()
    -
    +
    def OnCloseButton(self):
    """
    Function called when Close button is pressed
    """
    wx.CallAfter(self.ParentWindow.DeleteValue, self)
    -
    +
    def OnForceButton(self):
    """
    Function called when Force button is pressed
    """
    self.ForceValue(self.ItemsDict.values()[0])
    -
    +
    def OnReleaseButton(self):
    """
    Function called when Release button is pressed
    """
    self.ReleaseValue(self.ItemsDict.values()[0])
    -
    +
    def OnMouseDragging(self, x, y):
    """
    Function called when mouse is dragged over Viewer
    @@ -354,7 +354,7 @@
    # Refresh highlight in Debug Variable Panel (highlight can be displayed
    # in another Viewer
    self.ParentWindow.RefreshHighlight(x + xw, y + yw)
    -
    +
    def RefreshHighlight(self, x, y):
    """
    Function called by Debug Variable Panel asking Viewer to refresh
    @@ -364,22 +364,22 @@
    """
    # Get Viewer size
    width, height = self.GetSize()
    -
    +
    # Mouse is in the first half of Viewer
    if y < height / 2:
    # If Viewer is the upper one, draw drop before highlight
    if self.ParentWindow.IsViewerFirst(self):
    self.SetHighlight(HIGHLIGHT_BEFORE)
    -
    +
    # Else draw drop after highlight in previous Viewer
    else:
    self.SetHighlight(HIGHLIGHT_NONE)
    self.ParentWindow.HighlightPreviousViewer(self)
    -
    - # Mouse is in the second half of Viewer, draw drop after highlight
    +
    + # Mouse is in the second half of Viewer, draw drop after highlight
    else:
    self.SetHighlight(HIGHLIGHT_AFTER)
    -
    +
    def OnEraseBackground(self, event):
    """
    Function called when Viewer background is going to be erase
    @@ -387,7 +387,7 @@
    """
    # Prevent flicker on Windows
    pass
    -
    +
    def OnResize(self, event):
    """
    Function called when Viewer size changed
    @@ -397,7 +397,7 @@
    self.RefreshButtonsPosition()
    self.ParentWindow.ForceRefresh()
    event.Skip()
    -
    +
    def ForceValue(self, item):
    """
    Force value of item given
    @@ -409,13 +409,13 @@
    # Return immediately if not found
    if iec_type is None:
    return
    -
    +
    # Open a dialog to enter varaible forced value
    dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
    if dialog.ShowModal() == wx.ID_OK:
    self.ParentWindow.ForceDataValue(iec_path.upper(),
    dialog.GetValue())
    -
    +
    def ReleaseValue(self, item):
    """
    Release value of item given
    --- a/controls/DebugVariablePanel/GraphButton.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DebugVariablePanel/GraphButton.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,7 +35,7 @@
    """
    class GraphButton():
    -
    +
    def __init__(self, x, y, bitmap, callback):
    """
    Constructor
    @@ -48,21 +48,21 @@
    self.SetPosition(x, y)
    # Set button bitmap
    self.SetBitmap(bitmap)
    -
    +
    # By default button is hide and enabled
    self.Shown = False
    self.Enabled = True
    -
    +
    # Save reference to callback function
    self.Callback = callback
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Remove reference to callback function
    self.callback = None
    -
    +
    def SetBitmap(self, bitmap):
    """
    Set bitmap to use for button
    @@ -70,7 +70,7 @@
    """
    # Get wx.Bitmap object corresponding to bitmap
    self.Bitmap = GetBitmap(bitmap)
    -
    +
    def GetSize(self):
    """
    Return size of button
    @@ -78,7 +78,7 @@
    """
    # Button size is size of bitmap
    return self.Bitmap.GetSize()
    -
    +
    def SetPosition(self, x, y):
    """
    Set button position
    @@ -86,7 +86,7 @@
    @param y: Y coordinate of Button in Graphic Viewer
    """
    self.Position = wx.Point(x, y)
    -
    +
    def Show(self, show=True):
    """
    Mark if button to be displayed in Graphic Viewer
    @@ -94,20 +94,20 @@
    (default True)
    """
    self.Shown = show
    -
    +
    def Hide(self):
    """
    Hide button from Graphic Viewer
    """
    self.Show(False)
    -
    +
    def IsShown(self):
    """
    Return if button is displayed in Graphic Viewer
    @return: True if button is displayed in Graphic Viewer
    """
    return self.Shown
    -
    +
    def Enable(self, enable=True):
    """
    Mark if button is active in Graphic Viewer
    @@ -115,44 +115,44 @@
    (default True)
    """
    self.Enabled = enable
    -
    +
    def Disable(self):
    """
    Deactivate button in Graphic Viewer
    """
    self.Enabled = False
    -
    +
    def IsEnabled(self):
    """
    Return if button is active in Graphic Viewer
    @return: True if button is active in Graphic Viewer
    """
    return self.Enabled
    -
    +
    def HitTest(self, x, y):
    """
    Test if point is inside button
    @param x: X coordinate of point
    @param y: Y coordinate of point
    @return: True if button is active and displayed and point is inside
    - button
    + button
    """
    # Return immediately if button is hidden or inactive
    if not (self.IsShown() and self.IsEnabled()):
    return False
    -
    +
    # Test if point is inside button
    w, h = self.Bitmap.GetSize()
    rect = wx.Rect(self.Position.x, self.Position.y, w, h)
    return rect.InsideXY(x, y)
    -
    +
    def ProcessCallback(self):
    """
    Call callback function if defined
    """
    if self.Callback is not None:
    self.Callback()
    -
    +
    def Draw(self, dc):
    """
    Draw button in Graphic Viewer
    --- a/controls/DurationCellEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/DurationCellEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -27,39 +27,39 @@
    from dialogs.DurationEditorDialog import DurationEditorDialog
    class DurationCellControl(wx.PyControl):
    -
    +
    '''
    Custom cell editor control with a text box and a button that launches
    the DurationEditorDialog.
    '''
    def __init__(self, parent):
    wx.Control.__init__(self, parent)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    # create location text control
    - self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1),
    + self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1),
    style=wx.TE_PROCESS_ENTER)
    self.Duration.Bind(wx.EVT_KEY_DOWN, self.OnDurationChar)
    main_sizer.AddWindow(self.Duration, flag=wx.GROW)
    -
    +
    # create browse button
    self.EditButton = wx.Button(self, label='...', size=wx.Size(30, -1))
    self.Bind(wx.EVT_BUTTON, self.OnEditButtonClick, self.EditButton)
    main_sizer.AddWindow(self.EditButton, flag=wx.GROW)
    -
    +
    self.Bind(wx.EVT_SIZE, self.OnSize)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    self.Default = None
    -
    +
    def SetValue(self, value):
    self.Default = value
    self.Duration.SetValue(value)
    -
    +
    def GetValue(self):
    return self.Duration.GetValue()
    @@ -90,7 +90,7 @@
    def SetInsertionPoint(self, i):
    self.Duration.SetInsertionPoint(i)
    -
    +
    def SetFocus(self):
    self.Duration.SetFocus()
    @@ -100,13 +100,13 @@
    '''
    def __init__(self, table, colname):
    wx.grid.PyGridCellEditor.__init__(self)
    -
    +
    self.Table = table
    self.Colname = colname
    -
    +
    def __del__(self):
    self.CellControl = None
    -
    +
    def Create(self, parent, id, evt_handler):
    self.CellControl = DurationCellControl(parent)
    self.SetControl(self.CellControl)
    @@ -131,8 +131,8 @@
    return self.EndEditInternal(row, col, grid, oldval)
    else:
    def EndEdit(self, row, col, grid):
    - oldval = self.Table.GetValueByName(row, self.Colname)
    - return self.EndEditInternal(row, col, grid, oldval)
    + oldval = self.Table.GetValueByName(row, self.Colname)
    + return self.EndEditInternal(row, col, grid, oldval)
    def SetSize(self, rect):
    self.CellControl.SetDimensions(rect.x + 1, rect.y,
    --- a/controls/EnhancedStatusBar.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/EnhancedStatusBar.py Mon Aug 14 19:13:01 2017 +0300
    @@ -16,7 +16,7 @@
    #
    # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
    # Write To Me At:
    -#
    +#
    #
    # andrea.gavana@agip.it
    # andrea_gavan@tin.it
    @@ -47,7 +47,7 @@
    And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_VERTICAL, ESB_ALIGN_BOTTOM And
    ESB_ALIGN_LEFT;
    -EnhancedStatusBar Is Freeware And Distributed Under The wxPython License.
    +EnhancedStatusBar Is Freeware And Distributed Under The wxPython License.
    Latest Revision: 21 September 2005, 19.57.20 GMT+2
    Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2
    @@ -90,17 +90,17 @@
    style=wx.ST_SIZEGRIP,
    name="EnhancedStatusBar")
    """
    -
    +
    wx.StatusBar.__init__(self, parent, id, style, name)
    -
    +
    self._items = {}
    self._curPos = 0
    self._parent = parent
    -
    - wx.EVT_SIZE(self, self.OnSize)
    +
    + wx.EVT_SIZE(self, self.OnSize)
    wx.CallAfter(self.OnSize, None)
    -
    +
    def OnSize(self, event):
    """Handles The wx.EVT_SIZE Events For The StatusBar.
    @@ -109,15 +109,15 @@
    for pos, item in self._items.items():
    widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment
    -
    +
    rect = self.GetFieldRect(pos)
    widgetpos = widget.GetPosition()
    widgetsize = widget.GetSize()
    rect = self.GetFieldRect(pos)
    -
    +
    if horizontalalignment == ESB_EXACT_FIT:
    -
    +
    if verticalalignment == ESB_EXACT_FIT:
    """ 1 September 2015 Fix fit align """
    widget.SetSize((rect.width-4, rect.height-4))
    @@ -138,7 +138,7 @@
    widget.SetPosition((rect.x-1, rect.height-widgetsize[1]))
    elif horizontalalignment == ESB_ALIGN_LEFT:
    -
    +
    xpos = rect.x - 1
    if verticalalignment == ESB_EXACT_FIT:
    widget.SetSize((widgetsize[0], rect.height-2))
    @@ -154,9 +154,9 @@
    widget.SetPosition((xpos, rect.y))
    elif verticalalignment == ESB_ALIGN_BOTTOM:
    widget.SetPosition((xpos, rect.height-widgetsize[1]))
    -
    +
    elif horizontalalignment == ESB_ALIGN_RIGHT:
    -
    +
    xpos = rect.x + rect.width - widgetsize[0] - 1
    if verticalalignment == ESB_EXACT_FIT:
    widget.SetSize((widgetsize[0], rect.height-2))
    @@ -174,7 +174,7 @@
    widget.SetPosition((xpos, rect.height-widgetsize[1]))
    elif horizontalalignment == ESB_ALIGN_CENTER_HORIZONTAL:
    -
    +
    xpos = rect.x + (rect.width - widgetsize[0])/2 - 1
    if verticalalignment == ESB_EXACT_FIT:
    widget.SetSize((widgetsize[0], rect.height))
    @@ -191,11 +191,11 @@
    elif verticalalignment == ESB_ALIGN_BOTTOM:
    widget.SetPosition((xpos, rect.height-widgetsize[1]))
    -
    +
    if event is not None:
    event.Skip()
    -
    -
    +
    +
    def AddWidget(self, widget, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL,
    verticalalignment=ESB_ALIGN_CENTER_VERTICAL, pos = -1):
    """Add A Widget To The EnhancedStatusBar.
    @@ -221,7 +221,7 @@
    if pos == -1:
    pos = self._curPos
    self._curPos += 1
    -
    +
    if self.GetFieldsCount() <= pos:
    raise "\nERROR: EnhancedStatusBar has a max of %d items, you tried to set item #%d" % (self.GetFieldsCount(), pos)
    @@ -236,13 +236,13 @@
    raise '\nERROR: Parameter "verticalalignment" Should Be One Of '\
    '"ESB_ALIGN_CENTER_VERTICAL", "ESB_ALIGN_TOP", "ESB_ALIGN_BOTTOM"' \
    '"ESB_EXACT_FIT"'
    -
    +
    try:
    self.RemoveChild(self._items[pos].widget)
    self._items[pos].widget.Destroy()
    except KeyError: pass
    -
    +
    self._items[pos] = EnhancedStatusBarItem(widget, pos, horizontalalignment, verticalalignment)
    -
    +
    wx.CallAfter(self.OnSize, None)
    --- a/controls/FolderTree.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/FolderTree.py Mon Aug 14 19:13:01 2017 +0300
    @@ -47,13 +47,13 @@
    return splitpath(head) + [tail]
    class FolderTree(wx.Panel):
    -
    +
    def __init__(self, parent, folder, filter=None, editable=True):
    wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    -
    +
    main_sizer = wx.BoxSizer(wx.VERTICAL)
    -
    - self.Tree = wx.TreeCtrl(self,
    +
    + self.Tree = wx.TreeCtrl(self,
    style=wx.TR_HAS_BUTTONS|
    wx.TR_SINGLE|
    wx.SUNKEN_BORDER|
    @@ -69,19 +69,19 @@
    self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree)
    self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree)
    main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW)
    -
    +
    if filter is not None:
    self.Filter = wx.ComboBox(self, style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter)
    main_sizer.AddWindow(self.Filter, flag=wx.GROW)
    else:
    self.Filter = None
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    self.Folder = folder
    self.Editable = editable
    -
    +
    self.TreeImageList = wx.ImageList(16, 16)
    self.TreeImageDict = {}
    for item_type, bitmap in [(DRIVE, "tree_drive"),
    @@ -89,7 +89,7 @@
    (FILE, "tree_file")]:
    self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap))
    self.Tree.SetImageList(self.TreeImageList)
    -
    +
    self.Filters = {}
    if self.Filter is not None:
    filter_parts = filter.split("|")
    @@ -101,11 +101,11 @@
    self.Filter.Append(filter_parts[idx])
    if idx == 0:
    self.Filter.SetStringSelection(filter_parts[idx])
    -
    +
    self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
    else:
    self.CurrentFilter = ""
    -
    +
    def _GetFolderChildren(self, folderpath, recursive=True):
    items = []
    if wx.Platform == '__WXMSW__' and folderpath == "/":
    @@ -127,25 +127,25 @@
    else:
    children = 0
    items.append((filename, FOLDER, children))
    - elif (self.CurrentFilter == "" or
    + elif (self.CurrentFilter == "" or
    os.path.splitext(filename)[1] == self.CurrentFilter):
    items.append((filename, FILE, None))
    if recursive:
    items.sort(sort_folder)
    return items
    -
    +
    def SetFilter(self, filter):
    self.CurrentFilter = filter
    -
    +
    def GetTreeCtrl(self):
    return self.Tree
    -
    +
    def RefreshTree(self):
    root = self.Tree.GetRootItem()
    if not root.IsOk():
    root = self.Tree.AddRoot("")
    self.GenerateTreeBranch(root, self.Folder)
    -
    +
    def GenerateTreeBranch(self, root, folderpath):
    item, item_cookie = self.Tree.GetFirstChild(root)
    for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)):
    @@ -172,18 +172,18 @@
    def ExpandItem(self, item):
    self.GenerateTreeBranch(item, self.GetPath(item))
    self.Tree.Expand(item)
    -
    +
    def OnTreeItemActivated(self, event):
    self.ExpandItem(event.GetItem())
    event.Skip()
    -
    +
    def OnTreeLeftDown(self, event):
    item, flags = self.Tree.HitTest(event.GetPosition())
    if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item):
    self.ExpandItem(item)
    else:
    event.Skip()
    -
    +
    def OnTreeItemExpanded(self, event):
    item = event.GetItem()
    self.GenerateTreeBranch(item, self.GetPath(item))
    @@ -201,7 +201,7 @@
    event.Skip()
    else:
    event.Veto()
    -
    +
    def OnTreeEndLabelEdit(self, event):
    new_name = event.GetLabel()
    if new_name != "":
    @@ -212,20 +212,20 @@
    os.rename(old_filepath, new_filepath)
    event.Skip()
    else:
    - message = wx.MessageDialog(self,
    - _("File '%s' already exists!") % new_name,
    + message = wx.MessageDialog(self,
    + _("File '%s' already exists!") % new_name,
    _("Error"), wx.OK|wx.ICON_ERROR)
    message.ShowModal()
    message.Destroy()
    event.Veto()
    else:
    event.Skip()
    -
    +
    def OnFilterChanged(self, event):
    self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()]
    self.RefreshTree()
    event.Skip()
    -
    +
    def _SelectItem(self, root, parts):
    if len(parts) == 0:
    self.Tree.SelectItem(root)
    @@ -233,7 +233,7 @@
    item, item_cookie = self.Tree.GetFirstChild(root)
    while item.IsOk():
    if self.Tree.GetItemText(item) == parts[0]:
    - if (self.Tree.ItemHasChildren(item) and
    + if (self.Tree.ItemHasChildren(item) and
    not self.Tree.IsExpanded(item)):
    self.Tree.Expand(item)
    wx.CallAfter(self._SelectItem, item, parts[1:])
    @@ -241,14 +241,14 @@
    self._SelectItem(item, parts[1:])
    return
    item, item_cookie = self.Tree.GetNextChild(root, item_cookie)
    -
    +
    def SetPath(self, path):
    if path.startswith(self.Folder):
    root = self.Tree.GetRootItem()
    if root.IsOk():
    relative_path = path.replace(os.path.join(self.Folder, ""), "")
    self._SelectItem(root, splitpath(relative_path))
    -
    +
    def GetPath(self, item=None):
    if item is None:
    item = self.Tree.GetSelection()
    --- a/controls/LibraryPanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/LibraryPanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -42,7 +42,7 @@
    """
    class LibraryPanel(wx.Panel):
    -
    +
    def __init__(self, parent, enable_drag=False):
    """
    Constructor
    @@ -51,19 +51,19 @@
    be drag'n drop from LibraryPanel (default: False)
    """
    wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
    -
    +
    # Define LibraryPanel main sizer
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    # Add SearchCtrl to main sizer
    self.SearchCtrl = wx.SearchCtrl(self)
    # Add a button with a magnifying glass, essentially to show that this
    # control is for searching in tree
    self.SearchCtrl.ShowSearchButton(True)
    self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl)
    - self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
    + self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
    self.OnSearchButtonClick, self.SearchCtrl)
    # Bind keyboard event on SearchCtrl text control to catch UP and DOWN
    # for search previous and next occurrence
    @@ -74,16 +74,16 @@
    search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown)
    main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW)
    -
    +
    # Add Splitter window for tree and block comment to main sizer
    splitter_window = wx.SplitterWindow(self)
    splitter_window.SetSashGravity(1.0)
    main_sizer.AddWindow(splitter_window, flag=wx.GROW)
    -
    +
    # Add TreeCtrl for functions and function blocks library in splitter
    # window
    self.Tree = wx.TreeCtrl(splitter_window,
    - size=wx.Size(0, 0),
    + size=wx.Size(0, 0),
    style=wx.TR_HAS_BUTTONS|
    wx.TR_SINGLE|
    wx.SUNKEN_BORDER|
    @@ -95,35 +95,35 @@
    # tree to start a drag'n drop
    if enable_drag:
    self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree)
    -
    +
    # Add TextCtrl for function and function block informations
    - self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80),
    + self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80),
    style=wx.TE_READONLY|wx.TE_MULTILINE)
    -
    +
    splitter_window.SplitHorizontally(self.Tree, self.Comment, -80)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    # Reference to the project controller
    self.Controller = None
    -
    +
    # Variable storing functions and function blocks library to display
    self.BlockList = None
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Remove reference to project controller
    self.Controller = None
    -
    +
    def SetController(self, controller):
    """
    Set reference to project controller
    @param controller: Reference to project controller
    """
    self.Controller = controller
    -
    +
    def SetBlockList(self, blocklist):
    """
    Set function and function block library to display in TreeCtrl
    @@ -133,15 +133,15 @@
    self.BlockList = blocklist
    # Refresh TreeCtrl values
    self.RefreshTree()
    -
    +
    def SetFocus(self):
    """
    Called to give focus to LibraryPanel
    - Override wx.Window SetFocus method
    + Override wx.Window SetFocus method
    """
    # Give focus to SearchCtrl
    self.SearchCtrl.SetFocus()
    -
    +
    def ResetTree(self):
    """
    Reset LibraryPanel values displayed in controls
    @@ -150,7 +150,7 @@
    self.SearchCtrl.SetValue("")
    self.Tree.DeleteAllItems()
    self.Comment.SetValue("")
    -
    +
    def RefreshTree(self):
    """
    Refresh LibraryPanel values displayed in controls
    @@ -160,13 +160,13 @@
    if blocktypes is None and self.Controller is not None:
    # Get library from project controller if not defined
    blocktypes = self.Controller.GetBlockTypes()
    -
    +
    # Refresh TreeCtrl values if a library is defined
    if blocktypes is not None:
    # List that will contain tree items to be deleted when TreeCtrl
    # will be refreshed
    items_to_delete = []
    -
    +
    # Get current selected item for selected it when values refreshed
    selected_item = self.Tree.GetSelection()
    selected_pydata = (self.Tree.GetPyData(selected_item)
    @@ -179,16 +179,16 @@
    if selected_pydata is not None and
    selected_pydata["type"] == BLOCK
    else (None, None))
    -
    +
    # Get TreeCtrl root item (hidden)
    root = self.Tree.GetRootItem()
    if not root.IsOk():
    # Create root if not present
    root = self.Tree.AddRoot("")
    -
    +
    # Iterate over functions and function blocks library categories and
    # add a tree item to root item for each of them
    -
    +
    # Get first child under root item
    category_item, root_cookie = self.Tree.GetFirstChild(root)
    for category in blocktypes:
    @@ -196,11 +196,11 @@
    # extracting translated strings for gettext to consider "name"
    # to be translated
    category_name = category["name"]
    -
    +
    # Tree item already exists, set item label
    if category_item.IsOk():
    self.Tree.SetItemText(category_item, _(category_name))
    -
    +
    # Tree item doesn't exist, add new one to root
    else:
    category_item = self.Tree.AppendItem(root, _(category_name))
    @@ -209,24 +209,24 @@
    if wx.Platform != '__WXMSW__':
    category_item, root_cookie = \
    self.Tree.GetNextChild(root, root_cookie)
    -
    - # Set data associated to tree item (only save that item is a
    +
    + # Set data associated to tree item (only save that item is a
    # category)
    self.Tree.SetPyData(category_item, {"type" : CATEGORY})
    -
    +
    # Iterate over functions and function blocks defined in library
    - # category add a tree item to category tree item for each of
    + # category add a tree item to category tree item for each of
    # them
    -
    +
    # Get first child under category tree item
    blocktype_item, category_cookie = \
    self.Tree.GetFirstChild(category_item)
    for blocktype in category["list"]:
    -
    +
    # Tree item already exists, set item label
    if blocktype_item.IsOk():
    self.Tree.SetItemText(blocktype_item, blocktype["name"])
    -
    +
    # Tree item doesn't exist, add new one to category item
    else:
    blocktype_item = self.Tree.AppendItem(
    @@ -234,58 +234,58 @@
    # See comment when adding category
    if wx.Platform != '__WXMSW__':
    blocktype_item, category_cookie = \
    - self.Tree.GetNextChild(category_item,
    + self.Tree.GetNextChild(category_item,
    category_cookie)
    -
    +
    # Define data to associate to block tree item
    comment = blocktype["comment"]
    - block_data = {"type" : BLOCK,
    - "block_type" : blocktype["type"],
    - "inputs" : tuple([type
    + block_data = {"type" : BLOCK,
    + "block_type" : blocktype["type"],
    + "inputs" : tuple([type
    for name, type, modifier
    - in blocktype["inputs"]]),
    + in blocktype["inputs"]]),
    "extension" : (len(blocktype["inputs"])
    if blocktype["extensible"]
    else None),
    - "comment": _(comment) +
    + "comment": _(comment) +
    blocktype.get("usage", "")}
    self.Tree.SetPyData(blocktype_item, block_data)
    -
    +
    # Select block tree item in tree if it corresponds to
    # previously selected one
    - if selected_infos == (blocktype["name"],
    + if selected_infos == (blocktype["name"],
    blocktype["inputs"]):
    self.Tree.SelectItem(blocktype_item)
    -
    +
    # Update TextCtrl value
    self.Comment.SetValue(block_data["comment"])
    -
    +
    # Get next block tree item under category tree item
    blocktype_item, category_cookie = \
    self.Tree.GetNextChild(category_item, category_cookie)
    -
    +
    # Add every remaining tree item under category tree item after
    # updating all block items to the list of items to delete
    while blocktype_item.IsOk():
    items_to_delete.append(blocktype_item)
    blocktype_item, category_cookie = \
    self.Tree.GetNextChild(category_item, category_cookie)
    -
    +
    # Get next category tree item under root item
    category_item, root_cookie = \
    self.Tree.GetNextChild(root, root_cookie)
    -
    - # Add every remaining tree item under root item after updating all
    +
    + # Add every remaining tree item under root item after updating all
    # category items to the list of items to delete
    while category_item.IsOk():
    items_to_delete.append(category_item)
    category_item, root_cookie = \
    self.Tree.GetNextChild(root, root_cookie)
    -
    +
    # Remove all items in list of items to delete from TreeCtrl
    for item in items_to_delete:
    self.Tree.Delete(item)
    -
    +
    def GetSelectedBlock(self):
    """
    Get selected block informations
    @@ -298,17 +298,17 @@
    if selected_item.IsOk() and
    selected_item != self.Tree.GetRootItem()
    else None)
    -
    +
    # Return value is None if selected tree item is root or a category
    - return ({"type": self.Tree.GetItemText(selected_item),
    + return ({"type": self.Tree.GetItemText(selected_item),
    "inputs": selected_pydata["inputs"]}
    - if selected_pydata is not None and
    + if selected_pydata is not None and
    selected_pydata["type"] == BLOCK
    else None)
    -
    +
    def SelectTreeItem(self, name, inputs):
    """
    - Select Tree item corresponding to block informations given
    + Select Tree item corresponding to block informations given
    @param name: Block type name
    @param inputs: List of block inputs type [input_type,...]
    """
    @@ -318,7 +318,7 @@
    # Select tree item found
    self.Tree.SelectItem(item)
    self.Tree.EnsureVisible(item)
    -
    +
    def FindTreeItem(self, item, name, inputs = None):
    """
    Find Tree item corresponding to block informations given
    @@ -330,12 +330,12 @@
    # Return immediately if item isn't valid
    if not item.IsOk():
    return None
    -
    +
    # Get data associated to item to test
    item_pydata = self.Tree.GetPyData(item)
    if item_pydata is not None and item_pydata["type"] == BLOCK:
    # Only test item corresponding to block
    -
    +
    # Test if block inputs type are the same than those given
    type_inputs = item_pydata.get("inputs", None)
    type_extension = item_pydata.get("extension", None)
    @@ -351,11 +351,11 @@
    True)
    else:
    same_inputs = True
    -
    +
    # Return item if block data corresponds to informations given
    if self.Tree.GetItemText(item) == name and same_inputs:
    return item
    -
    +
    # Test item children if item doesn't correspond
    child, child_cookie = self.Tree.GetFirstChild(item)
    while child.IsOk():
    @@ -363,9 +363,9 @@
    if result:
    return result
    child, child_cookie = self.Tree.GetNextChild(item, child_cookie)
    -
    +
    return None
    -
    +
    def SearchInTree(self, value, mode="first"):
    """
    Search in Tree and select item that name contains string given
    @@ -378,13 +378,13 @@
    root = self.Tree.GetRootItem()
    if not root.IsOk():
    return False
    -
    +
    # Set function to navigate in Tree item sibling according to search
    - # mode defined
    + # mode defined
    sibling_function = (self.Tree.GetPrevSibling
    if mode == "previous"
    else self.Tree.GetNextSibling)
    -
    +
    # Get current selected item (for next and previous mode)
    item = self.Tree.GetSelection()
    if not item.IsOk() or mode == "first":
    @@ -392,29 +392,29 @@
    selected = None
    else:
    selected = item
    -
    +
    # Navigate through tree items until one matching found or reach tree
    # starting or ending
    while item.IsOk():
    -
    +
    # Get item data to get item type
    item_pydata = self.Tree.GetPyData(item)
    -
    +
    # Item is a block category
    if (item == root) or item_pydata["type"] == CATEGORY:
    -
    - # Get category first or last child according to search mode
    +
    + # Get category first or last child according to search mode
    # defined
    child = (self.Tree.GetLastChild(item)
    if mode == "previous"
    else self.Tree.GetFirstChild(item)[0])
    -
    +
    # If category has no child, go to sibling category
    item = (child if child.IsOk() else sibling_function(item))
    -
    +
    # Item is a block
    else:
    -
    +
    # Extract item block name
    name = self.Tree.GetItemText(item)
    # Test if block name contains string given
    @@ -428,17 +428,17 @@
    self.Tree.SelectItem(item)
    self.Tree.EnsureVisible(item)
    return True
    -
    +
    # Go to next item sibling if block not found
    next = sibling_function(item)
    -
    +
    # If category has no other child, go to next category sibling
    item = (next
    if next.IsOk()
    else sibling_function(self.Tree.GetItemParent(item)))
    -
    +
    return False
    -
    +
    def OnSearchCtrlChanged(self, event):
    """
    Called when SearchCtrl text control value changed
    @@ -447,7 +447,7 @@
    # Search for block containing SearchCtrl value in 'first' mode
    self.SearchInTree(self.SearchCtrl.GetValue())
    event.Skip()
    -
    +
    def OnSearchButtonClick(self, event):
    """
    Called when SearchCtrl search button was clicked
    @@ -456,7 +456,7 @@
    # Search for block containing SearchCtrl value in 'next' mode
    self.SearchInTree(self.SearchCtrl.GetValue(), "next")
    event.Skip()
    -
    +
    def OnTreeItemSelected(self, event):
    """
    Called when tree item is selected
    @@ -468,13 +468,13 @@
    item_pydata["comment"]
    if item_pydata is not None and item_pydata["type"] == BLOCK
    else "")
    -
    +
    # Call extra function defined when tree item is selected
    if getattr(self, "_OnTreeItemSelected", None) is not None:
    self._OnTreeItemSelected(event)
    -
    +
    event.Skip()
    -
    +
    def OnTreeBeginDrag(self, event):
    """
    Called when a drag is started in tree
    @@ -482,19 +482,19 @@
    """
    selected_item = event.GetItem()
    item_pydata = self.Tree.GetPyData(selected_item)
    -
    +
    # Item dragged is a block
    if item_pydata is not None and item_pydata["type"] == BLOCK:
    # Start a drag'n drop
    data = wx.TextDataObject(str(
    - (self.Tree.GetItemText(selected_item),
    - item_pydata["block_type"],
    - "",
    + (self.Tree.GetItemText(selected_item),
    + item_pydata["block_type"],
    + "",
    item_pydata["inputs"])))
    dragSource = wx.DropSource(self.Tree)
    dragSource.SetData(data)
    dragSource.DoDragDrop()
    -
    +
    def OnKeyDown(self, event):
    """
    Called when key is pressed in SearchCtrl text control
    @@ -503,17 +503,17 @@
    # Get event keycode and value in SearchCtrl
    keycode = event.GetKeyCode()
    search_value = self.SearchCtrl.GetValue()
    -
    +
    # Up key was pressed and SearchCtrl isn't empty, search for block in
    - # 'previous' mode
    + # 'previous' mode
    if keycode == wx.WXK_UP and search_value != "":
    self.SearchInTree(search_value, "previous")
    -
    +
    # Down key was pressed and SearchCtrl isn't empty, search for block in
    - # 'next' mode
    + # 'next' mode
    elif keycode == wx.WXK_DOWN and search_value != "":
    self.SearchInTree(search_value, "next")
    -
    +
    # Handle key normally
    else:
    event.Skip()
    --- a/controls/LocationCellEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/LocationCellEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -27,31 +27,31 @@
    from dialogs.BrowseLocationsDialog import BrowseLocationsDialog
    class LocationCellControl(wx.PyControl):
    -
    +
    '''
    Custom cell editor control with a text box and a button that launches
    the BrowseLocationsDialog.
    '''
    def __init__(self, parent):
    wx.Control.__init__(self, parent)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    # create location text control
    - self.Location = wx.TextCtrl(self, size=wx.Size(0, -1),
    + self.Location = wx.TextCtrl(self, size=wx.Size(0, -1),
    style=wx.TE_PROCESS_ENTER)
    self.Location.Bind(wx.EVT_KEY_DOWN, self.OnLocationChar)
    main_sizer.AddWindow(self.Location, flag=wx.GROW)
    -
    +
    # create browse button
    self.BrowseButton = wx.Button(self, label='...', size=wx.Size(30, -1))
    self.BrowseButton.Bind(wx.EVT_BUTTON, self.OnBrowseButtonClick)
    main_sizer.AddWindow(self.BrowseButton, flag=wx.GROW)
    -
    +
    self.Bind(wx.EVT_SIZE, self.OnSize)
    -
    +
    self.SetSizer(main_sizer)
    self.Controller = None
    @@ -73,7 +73,7 @@
    def SetValue(self, value):
    self.Default = value
    self.Location.SetValue(value)
    -
    +
    def GetValue(self):
    return self.Location.GetValue()
    @@ -88,14 +88,14 @@
    else:
    infos = None
    dialog.Destroy()
    -
    +
    if infos is not None:
    location = infos["location"]
    # set the location
    if not infos["location"].startswith("%"):
    - dialog = wx.SingleChoiceDialog(self,
    - _("Select a variable class:"), _("Variable class"),
    - [_("Input"), _("Output"), _("Memory")],
    + dialog = wx.SingleChoiceDialog(self,
    + _("Select a variable class:"), _("Variable class"),
    + [_("Input"), _("Output"), _("Memory")],
    wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
    if dialog.ShowModal() == wx.ID_OK:
    selected = dialog.GetSelection()
    @@ -111,7 +111,7 @@
    location = "%Q" + location
    else:
    location = "%M" + location
    -
    +
    self.Location.SetValue(location)
    self.VarType = infos["IEC_type"]
    @@ -129,7 +129,7 @@
    def SetInsertionPoint(self, i):
    self.Location.SetInsertionPoint(i)
    -
    +
    def SetFocus(self):
    self.Location.SetFocus()
    @@ -139,14 +139,14 @@
    '''
    def __init__(self, table, controller):
    wx.grid.PyGridCellEditor.__init__(self)
    -
    +
    self.Table = table
    self.Controller = controller
    def __del__(self):
    self.CellControl = None
    self.Controller = None
    -
    +
    def Create(self, parent, id, evt_handler):
    self.CellControl = LocationCellControl(parent)
    self.SetControl(self.CellControl)
    @@ -169,15 +169,15 @@
    self.Table.SetValueByName(row, 'Type', self.CellControl.GetVarType())
    self.CellControl.Disable()
    return changed
    -
    +
    if wx.VERSION >= (3, 0, 0):
    def EndEdit(self, row, col, grid, oldval):
    return self.EndEditInternal(row, col, grid, oldval)
    else:
    def EndEdit(self, row, col, grid):
    - old_loc = self.Table.GetValueByName(row, 'Location')
    + old_loc = self.Table.GetValueByName(row, 'Location')
    return self.EndEditInternal(row, col, grid, old_loc)
    -
    +
    def SetSize(self, rect):
    self.CellControl.SetDimensions(rect.x + 1, rect.y,
    rect.width, rect.height,
    @@ -185,4 +185,3 @@
    def Clone(self):
    return LocationCellEditor(self.Table, self.Controller)
    -
    --- a/controls/PouInstanceVariablesPanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/PouInstanceVariablesPanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -59,14 +59,14 @@
    w, h = self.GetClientSize()
    total_h = self.GetLineHeight(item)
    r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
    -
    +
    bbox_width = (r_image_w + 4) * len(rightimages) + 4
    bbox_height = r_image_h + 8
    bbox_x = w - bbox_width
    bbox_y = item.GetY() + ((total_h > r_image_h) and [(total_h-r_image_h)/2] or [0])[0]
    -
    +
    return wx.Rect(bbox_x, bbox_y, bbox_width, bbox_height)
    -
    +
    return None
    def IsOverItemRightImage(self, item, point):
    @@ -75,30 +75,30 @@
    point = self.CalcUnscrolledPosition(point)
    r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
    images_bbx = self.GetItemRightImagesBBox(item)
    -
    +
    rect = wx.Rect(images_bbx.x + 4, images_bbx.y + 4,
    r_image_w, r_image_h)
    for r_image in rightimages:
    if rect.Inside(point):
    return r_image
    rect.x += r_image_w + 4
    -
    +
    return None
    -
    +
    def PaintItem(self, item, dc, level, align):
    CT.CustomTreeCtrl.PaintItem(self, item, dc, level, align)
    -
    +
    rightimages = item.GetRightImages()
    if len(rightimages) > 0:
    images_bbx = self.GetItemRightImagesBBox(item)
    r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
    -
    +
    dc.SetBrush(wx.TRANSPARENT_BRUSH)
    dc.SetPen(wx.TRANSPARENT_PEN)
    -
    +
    bg_width = (r_image_w + 4) * len(rightimages) + 4
    bg_height = r_image_h + 8
    - dc.DrawRectangle(images_bbx.x, images_bbx.y,
    + dc.DrawRectangle(images_bbx.x, images_bbx.y,
    images_bbx.width, images_bbx.height)
    x_pos = images_bbx.x + 4
    for r_image in rightimages:
    @@ -106,7 +106,7 @@
    r_image, dc, x_pos, images_bbx.y + 4,
    wx.IMAGELIST_DRAW_TRANSPARENT)
    x_pos += r_image_w + 4
    -
    +
    _ButtonCallbacks = namedtuple("ButtonCallbacks", ["leftdown", "dclick"])
    from PLCControler import ITEMS_VARIABLE, ITEM_CONFIGURATION, ITEM_RESOURCE, ITEM_POU, ITEM_TRANSITION, ITEM_ACTION
    @@ -115,26 +115,26 @@
    class PouInstanceVariablesPanel(wx.Panel):
    def __init__(self, parent, window, controller, debug):
    - wx.Panel.__init__(self, name='PouInstanceTreePanel',
    - parent=parent, pos=wx.Point(0, 0),
    + wx.Panel.__init__(self, name='PouInstanceTreePanel',
    + parent=parent, pos=wx.Point(0, 0),
    size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
    -
    +
    self.ParentButton = wx.lib.buttons.GenBitmapButton(self,
    bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER)
    self.ParentButton.SetToolTipString(_("Parent instance"))
    - self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick,
    + self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick,
    self.ParentButton)
    -
    +
    self.InstanceChoice = wx.ComboBox(self, size=wx.Size(0, 0), style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnInstanceChoiceChanged,
    self.InstanceChoice)
    -
    - self.DebugButton = wx.lib.buttons.GenBitmapButton(self,
    +
    + self.DebugButton = wx.lib.buttons.GenBitmapButton(self,
    bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER)
    self.DebugButton.SetToolTipString(_("Debug instance"))
    - self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick,
    + self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick,
    self.DebugButton)
    -
    +
    self.VariablesList = CustomTreeCtrlWithRightImage(self,
    style=wx.SUNKEN_BORDER,
    agwStyle=CT.TR_NO_BUTTONS|
    @@ -150,54 +150,54 @@
    self.OnVariablesListItemActivated)
    self.VariablesList.Bind(wx.EVT_LEFT_DOWN, self.OnVariablesListLeftDown)
    self.VariablesList.Bind(wx.EVT_KEY_DOWN, self.OnVariablesListKeyDown)
    -
    +
    self.TreeRightImageList = wx.ImageList(24, 24)
    self.EditImage = self.TreeRightImageList.Add(GetBitmap("edit"))
    self.DebugInstanceImage = self.TreeRightImageList.Add(GetBitmap("debug_instance"))
    self.VariablesList.SetRightImageList(self.TreeRightImageList)
    -
    +
    self.ButtonCallBacks = {
    self.EditImage: _ButtonCallbacks(
    self.EditButtonCallback, None),
    self.DebugInstanceImage: _ButtonCallbacks(
    self.DebugButtonCallback, self.DebugButtonDClickCallback)}
    -
    +
    buttons_sizer = wx.FlexGridSizer(cols=3, hgap=0, rows=1, vgap=0)
    buttons_sizer.AddWindow(self.ParentButton)
    buttons_sizer.AddWindow(self.InstanceChoice, flag=wx.GROW)
    buttons_sizer.AddWindow(self.DebugButton)
    buttons_sizer.AddGrowableCol(1)
    buttons_sizer.AddGrowableRow(0)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    main_sizer.AddSizer(buttons_sizer, flag=wx.GROW)
    main_sizer.AddWindow(self.VariablesList, flag=wx.GROW)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    self.ParentWindow = window
    self.Controller = controller
    self.Debug = debug
    if not self.Debug:
    self.DebugButton.Hide()
    -
    +
    self.PouTagName = None
    self.PouInfos = None
    self.PouInstance = None
    -
    +
    def __del__(self):
    self.Controller = None
    -
    +
    def SetTreeImageList(self, tree_image_list):
    self.VariablesList.SetImageList(tree_image_list)
    -
    +
    def SetController(self, controller):
    self.Controller = controller
    -
    +
    self.RefreshView()
    -
    +
    def SetPouType(self, tagname, pou_instance=None):
    if self.Controller is not None:
    if tagname == "Project":
    @@ -206,7 +206,7 @@
    tagname = self.Controller.ComputeConfigurationName(config_name)
    if pou_instance is not None:
    self.PouInstance = pou_instance
    -
    +
    if self.PouTagName != tagname:
    self.PouTagName = tagname
    self.RefreshView()
    @@ -214,20 +214,20 @@
    self.RefreshInstanceChoice()
    else:
    self.RefreshView()
    -
    +
    def ResetView(self):
    self.Controller = None
    -
    +
    self.PouTagName = None
    self.PouInfos = None
    self.PouInstance = None
    -
    +
    self.RefreshView()
    -
    +
    def RefreshView(self):
    self.Freeze()
    self.VariablesList.DeleteAllItems()
    -
    +
    if self.Controller is not None and self.PouTagName is not None:
    if self.PouTagName.split('::')[0] in ['A', 'T']:
    self.PouInfos = self.Controller.GetPouVariables('P::%s' % self.PouTagName.split('::')[1], self.Debug)
    @@ -244,24 +244,24 @@
    text = "%s (%s)" % (var_infos.name, var_infos.type)
    else:
    text = var_infos.name
    -
    +
    right_images = []
    if var_infos.edit:
    right_images.append(self.EditImage)
    -
    +
    if var_infos.debug and self.Debug:
    right_images.append(self.DebugInstanceImage)
    -
    +
    item = self.VariablesList.AppendItem(root, text)
    item.SetRightImages(right_images)
    self.VariablesList.SetItemImage(item, self.ParentWindow.GetTreeImage(var_infos.var_class))
    self.VariablesList.SetPyData(item, var_infos)
    -
    +
    self.RefreshInstanceChoice()
    self.RefreshButtons()
    -
    +
    self.Thaw()
    -
    +
    def RefreshInstanceChoice(self):
    self.InstanceChoice.Clear()
    self.InstanceChoice.SetValue("")
    @@ -279,12 +279,12 @@
    else:
    self.PouInstance = None
    self.InstanceChoice.SetValue(_("Select an instance"))
    -
    +
    def RefreshButtons(self):
    enabled = self.InstanceChoice.GetSelection() != -1
    self.ParentButton.Enable(enabled and self.PouInfos.var_class != ITEM_CONFIGURATION)
    self.DebugButton.Enable(enabled and self.PouInfos.debug and self.Debug)
    -
    +
    root = self.VariablesList.GetRootItem()
    if root is not None and root.IsOk():
    item, item_cookie = self.VariablesList.GetFirstChild(root)
    @@ -295,12 +295,12 @@
    if child.GetName() != "edit":
    child.Enable(enabled)
    item, item_cookie = self.VariablesList.GetNextChild(root, item_cookie)
    -
    +
    def EditButtonCallback(self, infos):
    var_class = infos.var_class
    if var_class == ITEM_RESOURCE:
    tagname = self.Controller.ComputeConfigurationResourceName(
    - self.InstanceChoice.GetStringSelection(),
    + self.InstanceChoice.GetStringSelection(),
    infos.name)
    elif var_class == ITEM_TRANSITION:
    tagname = self.Controller.ComputePouTransitionName(
    @@ -314,7 +314,7 @@
    var_class = ITEM_POU
    tagname = self.Controller.ComputePouName(infos.type)
    self.ParentWindow.EditProjectElement(var_class, tagname)
    -
    +
    def DebugButtonCallback(self, infos):
    if self.InstanceChoice.GetSelection() != -1:
    var_class = infos.var_class
    @@ -344,16 +344,16 @@
    var_class,
    var_path,
    self.Controller.ComputePouName(infos.type))
    -
    +
    def DebugButtonDClickCallback(self, infos):
    if self.InstanceChoice.GetSelection() != -1:
    if infos.var_class in ITEMS_VARIABLE:
    self.ParentWindow.AddDebugVariable(
    - "%s.%s" % (self.InstanceChoice.GetStringSelection(),
    - infos.name),
    + "%s.%s" % (self.InstanceChoice.GetStringSelection(),
    + infos.name),
    force=True,
    graph=True)
    -
    +
    def ShowInstanceChoicePopup(self):
    self.InstanceChoice.SetFocusFromKbd()
    size = self.InstanceChoice.GetSize()
    @@ -364,7 +364,7 @@
    #event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType())
    #event.m_keyCode = wx.WXK_SPACE
    self.InstanceChoice.GetEventHandler().ProcessEvent(event)
    -
    +
    def OnParentButtonClick(self, event):
    if self.InstanceChoice.GetSelection() != -1:
    parent_path = self.InstanceChoice.GetStringSelection().rsplit(".", 1)[0]
    @@ -373,11 +373,11 @@
    wx.CallAfter(self.SetPouType, tagname, parent_path)
    wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, tagname)
    event.Skip()
    -
    +
    def OnInstanceChoiceChanged(self, event):
    self.RefreshButtons()
    event.Skip()
    -
    +
    def OnDebugButtonClick(self, event):
    if self.InstanceChoice.GetSelection() != -1:
    self.ParentWindow.OpenDebugViewer(
    @@ -385,26 +385,26 @@
    self.InstanceChoice.GetStringSelection(),
    self.PouTagName)
    event.Skip()
    -
    +
    def OnVariablesListItemActivated(self, event):
    selected_item = event.GetItem()
    if selected_item is not None and selected_item.IsOk():
    item_infos = self.VariablesList.GetPyData(selected_item)
    if item_infos is not None:
    -
    +
    item_button = self.VariablesList.IsOverItemRightImage(
    selected_item, event.GetPoint())
    if item_button is not None:
    callback = self.ButtonCallBacks[item_button].dclick
    if callback is not None:
    callback(item_infos)
    -
    +
    elif item_infos.var_class not in ITEMS_VARIABLE:
    instance_path = self.InstanceChoice.GetStringSelection()
    if item_infos.var_class == ITEM_RESOURCE:
    if instance_path != "":
    tagname = self.Controller.ComputeConfigurationResourceName(
    - instance_path,
    + instance_path,
    item_infos.name)
    else:
    tagname = None
    @@ -424,7 +424,7 @@
    self.SetPouType(tagname, item_path)
    self.ParentWindow.SelectProjectTreeItem(tagname)
    event.Skip()
    -
    +
    def OnVariablesListLeftDown(self, event):
    if self.InstanceChoice.GetSelection() == -1:
    wx.CallAfter(self.ShowInstanceChoicePopup)
    @@ -434,15 +434,15 @@
    if item is not None:
    item_infos = self.VariablesList.GetPyData(item)
    if item_infos is not None:
    -
    +
    item_button = self.VariablesList.IsOverItemRightImage(
    item, event.GetPosition())
    if item_button is not None:
    callback = self.ButtonCallBacks[item_button].leftdown
    if callback is not None:
    callback(item_infos)
    -
    - elif (flags & CT.TREE_HITTEST_ONITEMLABEL and
    +
    + elif (flags & CT.TREE_HITTEST_ONITEMLABEL and
    item_infos.var_class in ITEMS_VARIABLE):
    self.ParentWindow.EnsureTabVisible(
    self.ParentWindow.DebugVariablePanel)
    @@ -457,4 +457,3 @@
    keycode = event.GetKeyCode()
    if keycode != wx.WXK_LEFT:
    event.Skip()
    -
    --- a/controls/ProjectPropertiesPanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/ProjectPropertiesPanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -31,7 +31,7 @@
    REQUIRED_PARAMS = ["projectName", "productName", "productVersion", "companyName"]
    -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
    +[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
    POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
    ] = range(10)
    @@ -40,7 +40,7 @@
    #-------------------------------------------------------------------------------
    class ProjectPropertiesPanel(wx.Notebook):
    -
    +
    def AddSizerParams(self, parent, sizer, params):
    for idx, (name, label) in enumerate(params):
    border = 0
    @@ -48,17 +48,17 @@
    border |= wx.TOP
    elif idx == len(params) - 1:
    border |= wx.BOTTOM
    -
    +
    st = wx.StaticText(parent, label=label)
    - sizer.AddWindow(st, border=10,
    + sizer.AddWindow(st, border=10,
    flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT)
    -
    +
    tc = wx.TextCtrl(parent, style=wx.TE_PROCESS_ENTER)
    setattr(self, name, tc)
    callback = self.GetTextCtrlChangedFunction(tc, name)
    self.Bind(wx.EVT_TEXT_ENTER, callback, tc)
    tc.Bind(wx.EVT_KILL_FOCUS, callback)
    - sizer.AddWindow(tc, border=10,
    + sizer.AddWindow(tc, border=10,
    flag=wx.GROW|border|wx.RIGHT)
    def __init__(self, parent, controller=None, window=None, enable_required=True):
    @@ -67,36 +67,36 @@
    self.Controller = controller
    self.ParentWindow = window
    self.Values = None
    -
    +
    # Project Panel elements
    self.ProjectPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
    projectpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=5, vgap=15)
    projectpanel_sizer.AddGrowableCol(1)
    self.ProjectPanel.SetSizer(projectpanel_sizer)
    -
    +
    self.AddSizerParams(self.ProjectPanel, projectpanel_sizer,
    [("projectName", _('Project Name (required):')),
    ("projectVersion", _('Project Version (optional):')),
    ("productName", _('Product Name (required):')),
    ("productVersion", _('Product Version (required):')),
    ("productRelease", _('Product Release (optional):'))])
    -
    +
    self.AddPage(self.ProjectPanel, _("Project"))
    -
    +
    # Author Panel elements
    self.AuthorPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
    authorpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=15)
    authorpanel_sizer.AddGrowableCol(1)
    self.AuthorPanel.SetSizer(authorpanel_sizer)
    -
    +
    self.AddSizerParams(self.AuthorPanel, authorpanel_sizer,
    [("companyName", _('Company Name (required):')),
    ("companyURL", _('Company URL (optional):')),
    ("authorName", _('Author Name (optional):')),
    ("organization", _('Organization (optional):'))])
    -
    +
    self.AddPage(self.AuthorPanel, _("Author"))
    # Graphics Panel elements
    @@ -106,47 +106,47 @@
    graphicpanel_sizer.AddGrowableCol(0)
    graphicpanel_sizer.AddGrowableRow(3)
    self.GraphicsPanel.SetSizer(graphicpanel_sizer)
    -
    +
    pageSize_st = wx.StaticText(self.GraphicsPanel,
    label=_('Page Size (optional):'))
    - graphicpanel_sizer.AddWindow(pageSize_st, border=10,
    + graphicpanel_sizer.AddWindow(pageSize_st, border=10,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    pageSize_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
    pageSize_sizer.AddGrowableCol(1)
    - graphicpanel_sizer.AddSizer(pageSize_sizer, border=10,
    + graphicpanel_sizer.AddSizer(pageSize_sizer, border=10,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    for name, label in [('PageWidth', _('Width:')),
    ('PageHeight', _('Height:'))]:
    st = wx.StaticText(self.GraphicsPanel, label=label)
    - pageSize_sizer.AddWindow(st, border=12,
    + pageSize_sizer.AddWindow(st, border=12,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT)
    -
    - sp = wx.SpinCtrl(self.GraphicsPanel,
    +
    + sp = wx.SpinCtrl(self.GraphicsPanel,
    min=0, max=2**16, style=wx.TE_PROCESS_ENTER)
    setattr(self, name, sp)
    callback = self.GetPageSizeChangedFunction(sp, name)
    self.Bind(wx.EVT_TEXT_ENTER, callback, sp)
    sp.Bind(wx.EVT_KILL_FOCUS, callback)
    pageSize_sizer.AddWindow(sp, flag=wx.GROW)
    -
    +
    scaling_st = wx.StaticText(self.GraphicsPanel,
    label=_('Grid Resolution:'))
    - graphicpanel_sizer.AddWindow(scaling_st, border=10,
    + graphicpanel_sizer.AddWindow(scaling_st, border=10,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    scaling_nb = wx.Notebook(self.GraphicsPanel)
    - graphicpanel_sizer.AddWindow(scaling_nb, border=10,
    + graphicpanel_sizer.AddWindow(scaling_nb, border=10,
    flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.Scalings = {}
    for language, translation in [("FBD",_("FBD")), ("LD",_("LD")), ("SFC",_("SFC"))]:
    scaling_panel = wx.Panel(scaling_nb, style=wx.TAB_TRAVERSAL)
    scalingpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
    scalingpanel_sizer.AddGrowableCol(1)
    scaling_panel.SetSizer(scalingpanel_sizer)
    -
    +
    scaling_controls = []
    for idx, (name, label) in enumerate([('XScale', _('Horizontal:')),
    ('YScale', _('Vertical:'))]):
    @@ -154,67 +154,67 @@
    border = wx.TOP
    else:
    border = wx.BOTTOM
    -
    +
    st = wx.StaticText(scaling_panel, label=label)
    - scalingpanel_sizer.AddWindow(st, border=10,
    + scalingpanel_sizer.AddWindow(st, border=10,
    flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT)
    -
    - sp = wx.SpinCtrl(scaling_panel,
    +
    + sp = wx.SpinCtrl(scaling_panel,
    min=0, max=2**16, style=wx.TE_PROCESS_ENTER)
    scaling_controls.append(sp)
    callback = self.GetScalingChangedFunction(sp, language, name)
    self.Bind(wx.EVT_TEXT_ENTER, callback, sp)
    sp.Bind(wx.EVT_KILL_FOCUS, callback)
    - scalingpanel_sizer.AddWindow(sp, border=10,
    + scalingpanel_sizer.AddWindow(sp, border=10,
    flag=wx.GROW|border|wx.RIGHT)
    -
    +
    self.Scalings[language] = scaling_controls
    scaling_nb.AddPage(scaling_panel, translation)
    -
    +
    self.AddPage(self.GraphicsPanel, _("Graphics"))
    # Miscellaneous Panel elements
    - self.MiscellaneousPanel = wx.Panel(id=-1, parent=self,
    + self.MiscellaneousPanel = wx.Panel(id=-1, parent=self,
    name='MiscellaneousPanel', pos=wx.Point(0, 0),
    size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
    miscellaneouspanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15)
    miscellaneouspanel_sizer.AddGrowableCol(1)
    miscellaneouspanel_sizer.AddGrowableRow(1)
    self.MiscellaneousPanel.SetSizer(miscellaneouspanel_sizer)
    -
    +
    language_label = wx.StaticText(self.MiscellaneousPanel,
    label=_('Language (optional):'))
    - miscellaneouspanel_sizer.AddWindow(language_label, border=10,
    + miscellaneouspanel_sizer.AddWindow(language_label, border=10,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT)
    -
    - self.Language = wx.ComboBox(self.MiscellaneousPanel,
    +
    + self.Language = wx.ComboBox(self.MiscellaneousPanel,
    style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnLanguageChanged, self.Language)
    - miscellaneouspanel_sizer.AddWindow(self.Language, border=10,
    + miscellaneouspanel_sizer.AddWindow(self.Language, border=10,
    flag=wx.GROW|wx.TOP|wx.RIGHT)
    -
    +
    description_label = wx.StaticText(self.MiscellaneousPanel,
    label=_('Content Description (optional):'))
    - miscellaneouspanel_sizer.AddWindow(description_label, border=10,
    + miscellaneouspanel_sizer.AddWindow(description_label, border=10,
    flag=wx.BOTTOM|wx.LEFT)
    -
    - self.ContentDescription = wx.TextCtrl(self.MiscellaneousPanel,
    +
    + self.ContentDescription = wx.TextCtrl(self.MiscellaneousPanel,
    size=wx.Size(240,150), style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER)
    - self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged,
    + self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged,
    self.ContentDescription)
    - self.ContentDescription.Bind(wx.EVT_KILL_FOCUS,
    + self.ContentDescription.Bind(wx.EVT_KILL_FOCUS,
    self.OnContentDescriptionChanged)
    - miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10,
    + miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10,
    flag=wx.GROW|wx.BOTTOM|wx.RIGHT)
    -
    +
    self.AddPage(self.MiscellaneousPanel, _("Miscellaneous"))
    -
    +
    for param in REQUIRED_PARAMS:
    getattr(self, param).Enable(enable_required)
    -
    +
    languages = ["", "en-US", "fr-FR", "zh-CN", "ru-RU"]
    -
    +
    for language in languages:
    self.Language.Append(language)
    @@ -241,13 +241,13 @@
    tc = getattr(self, item, None)
    if tc is not None:
    tc.SetValue(value)
    -
    +
    def GetValues(self):
    values = {}
    for param in ["projectName", "projectVersion",
    "productName", "productVersion",
    "productRelease", "companyName",
    - "companyURL", "authorName",
    + "companyURL", "authorName",
    "organization"]:
    value = getattr(self, param).GetValue()
    if param in REQUIRED_PARAMS or value != "":
    @@ -270,7 +270,7 @@
    values["scaling"][language] = (self.Scalings[language][0].GetValue(),
    self.Scalings[language][1].GetValue())
    return values
    -
    +
    def GetTextCtrlChangedFunction(self, textctrl, name):
    def TextCtrlChangedFunction(event):
    if self.Controller is not None and self.Values is not None:
    @@ -304,7 +304,7 @@
    wx.CallAfter(self.RefreshView)
    event.Skip()
    return PageSizeChangedFunction
    -
    +
    def GetScalingChangedFunction(self, spinctrl, language, name):
    def ScalingChangedFunction(event):
    if self.Controller is not None:
    @@ -324,7 +324,7 @@
    wx.CallAfter(self.RefreshView)
    event.Skip()
    return ScalingChangedFunction
    -
    +
    def OnLanguageChanged(self, event):
    if self.Controller is not None:
    if self.Values is not None:
    @@ -339,7 +339,7 @@
    self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
    wx.CallAfter(self.RefreshView)
    event.Skip()
    -
    +
    def OnContentDescriptionChanged(self, event):
    if self.Controller is not None:
    if self.Values is not None:
    --- a/controls/SearchResultPanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/SearchResultPanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -45,7 +45,7 @@
    #-------------------------------------------------------------------------------
    [ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL,
    - ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON,
    + ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON,
    ] = [wx.NewId() for _init_ctrls in range(4)]
    class SearchResultPanel(wx.Panel):
    @@ -60,7 +60,7 @@
    def _init_coll_MainSizer_Items(self, parent):
    parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW)
    parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW)
    -
    +
    def _init_coll_MainSizer_Growables(self, parent):
    parent.AddGrowableCol(0)
    parent.AddGrowableRow(1)
    @@ -68,18 +68,18 @@
    def _init_coll_HeaderSizer_Items(self, parent):
    parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    parent.AddWindow(self.ResetButton, 0, border=0, flag=0)
    -
    +
    def _init_coll_HeaderSizer_Growables(self, parent):
    parent.AddGrowableCol(0)
    -
    +
    def _init_sizers(self):
    self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
    self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL)
    -
    +
    self._init_coll_MainSizer_Items(self.MainSizer)
    self._init_coll_MainSizer_Growables(self.MainSizer)
    self._init_coll_HeaderSizer_Items(self.HeaderSizer)
    -
    +
    self.SetSizer(self.MainSizer)
    def _init_ctrls(self, prnt):
    @@ -90,7 +90,7 @@
    self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL,
    name='HeaderLabel', parent=self,
    pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
    -
    +
    search_results_tree_style = CT.TR_HAS_BUTTONS|CT.TR_NO_LINES|CT.TR_HAS_VARIABLE_ROW_HEIGHT
    self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE,
    name="SearchResultsTree", parent=self,
    @@ -99,23 +99,23 @@
    self.SearchResultsTree.SetAGWWindowStyleFlag(search_results_tree_style)
    self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated,
    id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE)
    -
    +
    self.ResetButton = wx.lib.buttons.GenBitmapButton(self,
    bitmap=GetBitmap("reset"), size=wx.Size(28, 28), style=wx.NO_BORDER)
    self.ResetButton.SetToolTipString(_("Reset search result"))
    self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton)
    -
    +
    self._init_sizers()
    def __init__(self, parent, window):
    self.ParentWindow = window
    -
    +
    self._init_ctrls(parent)
    -
    +
    # Define Tree item icon list
    self.TreeImageList = wx.ImageList(16, 16)
    self.TreeImageDict = {}
    -
    +
    # Icons for other items
    for imgname, itemtype in [
    #editables
    @@ -129,40 +129,40 @@
    ("IL", "IL"),
    ("ST", "ST")]:
    self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
    -
    +
    for itemtype in ["function", "functionBlock", "program",
    "comment", "block", "io_variable",
    "connector", "contact", "coil",
    - "step", "transition", "jump",
    - "var_local", "var_input",
    + "step", "transition", "jump",
    + "var_local", "var_input",
    "var_inout", "var_output"]:
    self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(itemtype.upper()))
    -
    +
    # Assign icon list to TreeCtrl
    self.SearchResultsTree.SetImageList(self.TreeImageList)
    -
    +
    self.ResetSearchResults()
    def SetSearchResults(self, criteria, search_results):
    self.Criteria = criteria
    self.SearchResults = {}
    self.ElementsOrder = []
    -
    +
    for infos, start, end, text in search_results:
    if infos[0] not in self.ElementsOrder:
    self.ElementsOrder.append(infos[0])
    -
    +
    results = self.SearchResults.setdefault(infos[0], [])
    results.append((infos, start, end, text))
    -
    +
    self.RefreshView()
    -
    +
    def ResetSearchResults(self):
    self.Criteria = None
    self.ElementsOrder = []
    self.SearchResults = {}
    self.RefreshView()
    -
    +
    def RefreshView(self):
    self.SearchResultsTree.DeleteAllItems()
    if self.Criteria is None:
    @@ -180,19 +180,19 @@
    for tagname in self.ElementsOrder:
    results = self.SearchResults.get(tagname, [])
    matches_number += len(results)
    -
    +
    words = tagname.split("::")
    -
    +
    element_type = self.ParentWindow.Controler.GetElementType(tagname)
    if element_type == ITEM_POU:
    element_type = self.ParentWindow.Controler.GetPouType(words[1])
    -
    +
    element_infos = {"name": words[-1],
    "type": element_type,
    "data": tagname,
    "text": None,
    "matches": len(results)}
    -
    +
    children = element_infos.setdefault("children", [])
    for infos, start, end, text in results:
    if infos[1] == "name" or element_type == ITEM_DATATYPE:
    @@ -223,7 +223,7 @@
    "children": [],
    }
    children.append(child_infos)
    -
    +
    if len(words) > 2:
    for _element_infos in search_results_tree_children:
    if _element_infos["name"] == words[1]:
    @@ -234,41 +234,41 @@
    search_results_tree_children.append(element_infos)
    else:
    search_results_tree_children.append(element_infos)
    -
    +
    if matches_number < 2:
    header_format = _("'{a1}' - {a2} match in project")
    else:
    header_format = _("'{a1}' - {a2} matches in project")
    -
    +
    self.HeaderLabel.SetLabel(header_format.format(a1 = self.Criteria["find_pattern"], a2 = matches_number))
    self.ResetButton.Enable(True)
    -
    +
    if matches_number > 0:
    root = self.SearchResultsTree.GetRootItem()
    if root is None:
    root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"])
    self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos)
    self.SearchResultsTree.Expand(root)
    -
    +
    def GetTextCtrlClickFunction(self, item):
    def OnTextCtrlClick(event):
    self.SearchResultsTree.SelectItem(item)
    event.Skip()
    return OnTextCtrlClick
    -
    +
    def GetTextCtrlDClickFunction(self, item):
    def OnTextCtrlDClick(event):
    self.ShowSearchResults(item)
    event.Skip()
    return OnTextCtrlDClick
    -
    +
    def GenerateSearchResultsTreeBranch(self, root, infos):
    to_delete = []
    if infos["name"] == "body":
    item_name = "%d:" % infos["data"][1][0]
    else:
    item_name = infos["name"]
    -
    +
    self.SearchResultsTree.SetItemText(root, item_name)
    self.SearchResultsTree.SetPyData(root, infos["data"])
    self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE)
    @@ -278,7 +278,7 @@
    self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])])
    else:
    self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
    -
    +
    text = None
    if infos["text"] is not None:
    text = infos["text"]
    @@ -291,12 +291,12 @@
    text = _("(%d matches)") % infos["matches"]
    start_idx, end_idx = 0, len(text)
    style = wx.TextAttr(wx.Colour(0, 127, 174))
    -
    +
    if text is not None:
    text_ctrl_style = wx.BORDER_NONE|wx.TE_READONLY|wx.TE_RICH2
    if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1:
    text_ctrl_style |= wx.TE_MULTILINE
    - text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0),
    + text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0),
    value=text, style=text_ctrl_style)
    width, height = text_ctrl.GetTextExtent(text)
    text_ctrl.SetClientSize(wx.Size(width + 1, height))
    @@ -306,7 +306,7 @@
    text_ctrl.SetInsertionPoint(0)
    text_ctrl.SetStyle(start_idx, end_idx, style)
    self.SearchResultsTree.SetItemWindow(root, text_ctrl)
    -
    +
    if wx.VERSION >= (2, 6, 0):
    item, root_cookie = self.SearchResultsTree.GetFirstChild(root)
    else:
    @@ -317,7 +317,7 @@
    item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
    self.GenerateSearchResultsTreeBranch(item, child)
    item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
    -
    +
    def ShowSearchResults(self, item):
    data = self.SearchResultsTree.GetPyData(item)
    if isinstance(data, TupleType):
    @@ -327,11 +327,11 @@
    self.ParentWindow.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
    for infos, start, end, text in search_results:
    self.ParentWindow.ShowSearchResult(infos, start, end)
    -
    +
    def OnSearchResultsTreeItemActivated(self, event):
    self.ShowSearchResults(event.GetItem())
    event.Skip()
    -
    +
    def OnResetButton(self, event):
    self.ResetSearchResults()
    self.ParentWindow.ClearSearchResults()
    --- a/controls/VariablePanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/controls/VariablePanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -533,7 +533,7 @@
    self.ColFixedSizeFlag=[True,False, True, False, True, True, False]
    self.PanelWidthMin = sum(self.ColSizes)
    -
    +
    self.ElementType = element_type
    self.BodyType = None
    @@ -643,7 +643,7 @@
    panel_width = window.Parent.ScreenRect.Width - 35
    if panel_width > self.PanelWidthMin:
    stretch_cols_width = panel_width
    - stretch_cols_sum = 0
    + stretch_cols_sum = 0
    for col in range(len(self.ColFixedSizeFlag)):
    if self.ColFixedSizeFlag[col]:
    stretch_cols_width -= self.ColSizes[col]
    @@ -759,7 +759,7 @@
    dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    -
    +
    def OnVariablesGridCellChange(self, event):
    row, col = event.GetRow(), event.GetCol()
    colname = self.Table.GetColLabelValue(col, False)
    @@ -793,7 +793,7 @@
    if message is not None:
    wx.CallAfter(self.ShowErrorMessage, message)
    - event.Veto()
    + event.Veto()
    else:
    event.Skip()
    --- a/dialogs/ActionBlockDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/ActionBlockDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -47,7 +47,7 @@
    #-------------------------------------------------------------------------------
    class ActionTable(CustomTable):
    -
    +
    def GetValue(self, row, col):
    if row < self.GetNumberRows():
    colname = self.GetColLabelValue(col, False)
    @@ -55,7 +55,7 @@
    if colname == "Type":
    return _(value)
    return value
    -
    +
    def SetValue(self, row, col, value):
    if col < len(self.colnames):
    colname = self.GetColLabelValue(col, False)
    @@ -64,7 +64,7 @@
    elif colname == "Qualifier" and not self.Parent.DurationList[value]:
    self.data[row].duration = ""
    setattr(self.data[row], colname.lower(), value)
    -
    +
    def _updateColAttrs(self, grid):
    """
    wx.Grid -> update the column attributes to add the
    @@ -72,7 +72,7 @@
    Otherwise default to the default renderer.
    """
    -
    +
    for row in range(self.GetNumberRows()):
    for col in range(self.GetNumberCols()):
    editor = None
    @@ -103,11 +103,11 @@
    elif colname == "Indicator":
    editor = wx.grid.GridCellChoiceEditor()
    editor.SetParameters(self.Parent.VariableList)
    -
    +
    grid.SetCellEditor(row, col, editor)
    grid.SetCellRenderer(row, col, renderer)
    grid.SetReadOnly(row, col, readonly)
    -
    +
    grid.SetCellBackgroundColour(row, col, wx.WHITE)
    self.ResizeRow(grid, row)
    @@ -116,56 +116,56 @@
    #-------------------------------------------------------------------------------
    class ActionBlockDialog(wx.Dialog):
    -
    +
    def __init__(self, parent):
    wx.Dialog.__init__(self, parent, title=_('Edit action block properties'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    top_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
    top_sizer.AddGrowableCol(0)
    top_sizer.AddGrowableRow(0)
    main_sizer.AddSizer(top_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    actions_label = wx.StaticText(self, label=_('Actions:'))
    top_sizer.AddWindow(actions_label, flag=wx.ALIGN_BOTTOM)
    -
    +
    for name, bitmap, help in [
    ("AddButton", "add_element", _("Add action")),
    ("DeleteButton", "remove_element", _("Remove action")),
    ("UpButton", "up", _("Move action up")),
    ("DownButton", "down", _("Move action down"))]:
    - button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
    + button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
    size=wx.Size(28, 28), style=wx.NO_BORDER)
    button.SetToolTipString(help)
    setattr(self, name, button)
    top_sizer.AddWindow(button)
    -
    +
    self.ActionsGrid = CustomGrid(self, size=wx.Size(-1, 250), style=wx.VSCROLL)
    self.ActionsGrid.DisableDragGridSize()
    self.ActionsGrid.EnableScrolling(False, True)
    - self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
    + self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE,
    self.OnActionsGridCellChange)
    main_sizer.AddSizer(self.ActionsGrid, border=20,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
    - main_sizer.AddSizer(button_sizer, border=20,
    + main_sizer.AddSizer(button_sizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    self.Table = ActionTable(self, [], GetActionTableColnames())
    - typelist = GetTypeList()
    + typelist = GetTypeList()
    self.TypeList = ",".join(map(_,typelist))
    self.TranslateType = dict([(_(value), value) for value in typelist])
    self.ColSizes = [60, 90, 130, 200, 50]
    self.ColAlignements = [wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
    -
    +
    self.ActionsGrid.SetTable(self.Table)
    self.ActionsGrid.SetDefaultValue(_ActionInfos("N", "Action", "", "", ""))
    self.ActionsGrid.SetButtons({"Add": self.AddButton,
    @@ -173,19 +173,19 @@
    "Up": self.UpButton,
    "Down": self.DownButton})
    self.ActionsGrid.SetRowLabelSize(0)
    -
    +
    for col in range(self.Table.GetNumberCols()):
    attr = wx.grid.GridCellAttr()
    attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE)
    self.ActionsGrid.SetColAttr(col, attr)
    self.ActionsGrid.SetColMinimalWidth(col, self.ColSizes[col])
    self.ActionsGrid.AutoSizeColumn(col, False)
    -
    +
    self.Table.ResetView(self.ActionsGrid)
    self.ActionsGrid.SetFocus()
    self.ActionsGrid.RefreshButtons()
    self.Fit()
    -
    +
    def OnOK(self, event):
    self.ActionsGrid.CloseEditControl()
    self.EndModal(wx.ID_OK)
    @@ -193,14 +193,14 @@
    def OnActionsGridCellChange(self, event):
    wx.CallAfter(self.Table.ResetView, self.ActionsGrid)
    event.Skip()
    -
    +
    def SetQualifierList(self, list):
    self.QualifierList = ",".join(list)
    self.DurationList = list
    def SetVariableList(self, list):
    self.VariableList = "," + ",".join([variable.Name for variable in list])
    -
    +
    def SetActionList(self, list):
    self.ActionList = "," + ",".join(list)
    @@ -218,7 +218,7 @@
    if len(actions) > 0:
    self.ActionsGrid.SetGridCursor(0, 0)
    self.ActionsGrid.RefreshButtons()
    -
    +
    def GetValues(self):
    actions = self.Table.GetData()
    for action in actions:
    --- a/dialogs/ArrayTypeDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/ArrayTypeDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,56 +39,56 @@
    #-------------------------------------------------------------------------------
    class ArrayTypeDialog(wx.Dialog):
    -
    +
    def __init__(self, parent, datatypes, infos):
    wx.Dialog.__init__(self, parent, title=_('Edit array type properties'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    top_sizer = wx.BoxSizer(wx.HORIZONTAL)
    - main_sizer.AddSizer(top_sizer, border=20,
    + main_sizer.AddSizer(top_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    basetype_label = wx.StaticText(self, label=_('Base Type:'))
    top_sizer.AddWindow(basetype_label, 1, flag=wx.ALIGN_BOTTOM)
    -
    +
    self.BaseType = wx.ComboBox(self, style=wx.CB_READONLY)
    top_sizer.AddWindow(self.BaseType, 1, flag=wx.GROW)
    -
    - self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"),
    +
    + self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"),
    style=wx.gizmos.EL_ALLOW_NEW|
    wx.gizmos.EL_ALLOW_EDIT|
    wx.gizmos.EL_ALLOW_DELETE)
    - for func in ["_OnLabelEndEdit",
    - "_OnAddButton",
    - "_OnDelButton",
    - "_OnUpButton",
    + for func in ["_OnLabelEndEdit",
    + "_OnAddButton",
    + "_OnDelButton",
    + "_OnUpButton",
    "_OnDownButton"]:
    setattr(self.Dimensions, func, self.OnDimensionsChanged)
    - main_sizer.AddSizer(self.Dimensions, border=20,
    + main_sizer.AddSizer(self.Dimensions, border=20,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
    - main_sizer.AddSizer(button_sizer, border=20,
    + main_sizer.AddSizer(button_sizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    for datatype in datatypes:
    self.BaseType.Append(datatype)
    -
    +
    if isinstance(infos, TupleType) and infos[0] == "array":
    self.BaseType.SetStringSelection(infos[1])
    self.Dimensions.SetStrings(map(lambda x : "..".join(x), infos[2]))
    elif infos in datatypes:
    self.BaseType.SetStringSelection(infos)
    -
    +
    self.BaseType.SetFocus()
    self.Fit()
    -
    +
    def GetDimensions(self):
    message = None
    dimensions_list = []
    @@ -113,14 +113,14 @@
    dlg.Destroy()
    return None
    return dimensions_list
    -
    +
    def OnDimensionsChanged(self, event):
    wx.CallAfter(self.GetDimensions)
    event.Skip()
    -
    +
    def OnOK(self, event):
    if self.GetDimensions() is not None:
    self.EndModal(wx.ID_OK)
    -
    +
    def GetValue(self):
    return "array", self.BaseType.GetStringSelection(), self.GetDimensions()
    --- a/dialogs/BlockPreviewDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/BlockPreviewDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,7 +38,7 @@
    """
    class BlockPreviewDialog(wx.Dialog):
    -
    +
    def __init__(self, parent, controller, tagname, title):
    """
    Constructor
    @@ -48,50 +48,50 @@
    @param title: Title of dialog frame
    """
    wx.Dialog.__init__(self, parent, title=title)
    -
    +
    # Save reference to
    self.Controller = controller
    self.TagName = tagname
    -
    +
    # Label for preview
    self.PreviewLabel = wx.StaticText(self, label=_('Preview:'))
    -
    +
    # Create Preview panel
    self.Preview = wx.Panel(self, style=wx.SIMPLE_BORDER)
    self.Preview.SetBackgroundColour(wx.WHITE)
    -
    +
    # Add function to preview panel so that it answers to graphic elements
    # like Viewer
    setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE)
    setattr(self.Preview, "GetScaling", lambda:None)
    setattr(self.Preview, "GetBlockType", controller.GetBlockType)
    setattr(self.Preview, "IsOfType", controller.IsOfType)
    -
    +
    # Bind paint event on Preview panel
    self.Preview.Bind(wx.EVT_PAINT, self.OnPaint)
    -
    +
    # Add default dialog buttons sizer
    self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    self.ButtonSizer.GetAffirmativeButton())
    -
    +
    self.Element = None # Graphic element to display in preview
    self.MinElementSize = None # Graphic element minimal size
    -
    +
    # Variable containing the graphic element name when dialog is opened
    self.DefaultElementName = None
    self.Fit()
    -
    +
    # List of variables defined in POU {var_name: (var_class, var_type),...}
    self.VariableList = {}
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Remove reference to project controller
    self.Controller = None
    -
    +
    def _init_sizers(self, main_rows, main_growable_row,
    left_rows, left_growable_row,
    right_rows, right_growable_row):
    @@ -108,44 +108,44 @@
    None if no row is growable
    """
    # Create dialog main sizer
    - self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0,
    + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0,
    rows=main_rows, vgap=10)
    self.MainSizer.AddGrowableCol(0)
    if main_growable_row is not None:
    self.MainSizer.AddGrowableRow(main_growable_row)
    -
    +
    # Create a sizer for dividing parameters in two columns
    self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL)
    - self.MainSizer.AddSizer(self.ColumnSizer, border=20,
    + self.MainSizer.AddSizer(self.ColumnSizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    # Create a sizer for left column
    - self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0,
    + self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0,
    rows=left_rows, vgap=5)
    self.LeftGridSizer.AddGrowableCol(0)
    if left_growable_row is not None:
    self.LeftGridSizer.AddGrowableRow(left_growable_row)
    - self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5,
    + self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5,
    flag=wx.GROW|wx.RIGHT|wx.EXPAND)
    -
    +
    # Create a sizer for right column
    - self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0,
    + self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0,
    rows=right_rows, vgap=0)
    self.RightGridSizer.AddGrowableCol(0)
    if right_growable_row is not None:
    self.RightGridSizer.AddGrowableRow(right_growable_row)
    - self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5,
    + self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5,
    flag=wx.GROW|wx.LEFT)
    -
    +
    self.SetSizer(self.MainSizer)
    -
    +
    def SetMinElementSize(self, size):
    """
    Define minimal graphic element size
    @param size: Tuple containing minimal size (width, height)
    """
    self.MinElementSize = size
    -
    +
    def GetMinElementSize(self):
    """
    Get minimal graphic element size
    @@ -155,16 +155,16 @@
    """
    if self.Element is None:
    return None
    -
    +
    return self.Element.GetMinSize()
    -
    +
    def SetPreviewFont(self, font):
    """
    Set font of Preview panel
    @param font: wx.Font object containing font style
    """
    self.Preview.SetFont(font)
    -
    +
    def RefreshVariableList(self):
    """
    Extract list of variables defined in POU
    @@ -175,20 +175,20 @@
    for var in self.Controller.GetEditedElementInterfaceVars(
    self.TagName)
    if var.Edit}
    -
    - # Add POU name to variable list if POU is a function
    +
    + # Add POU name to variable list if POU is a function
    returntype = self.Controller.GetEditedElementInterfaceReturnType(
    self.TagName)
    if returntype is not None:
    self.VariableList[
    self.Controller.GetEditedElementName(self.TagName)] = \
    ("Output", returntype)
    -
    +
    # Add POU name if POU is a transition
    words = self.TagName.split("::")
    if words[0] == "T":
    self.VariableList[words[2]] = ("Output", "BOOL")
    -
    +
    def TestElementName(self, element_name):
    """
    Test displayed graphic element name
    @@ -198,47 +198,47 @@
    message_format = None
    # Get graphic element name in upper case
    uppercase_element_name = element_name.upper()
    -
    +
    # Test if graphic element name is a valid identifier
    if not TestIdentifier(element_name):
    message_format = _("\"%s\" is not a valid identifier!")
    -
    +
    # Test that graphic element name isn't a keyword
    elif uppercase_element_name in IEC_KEYWORDS:
    message_format = _("\"%s\" is a keyword. It can't be used!")
    -
    +
    # Test that graphic element name isn't a POU name
    elif uppercase_element_name in self.Controller.GetProjectPouNames():
    message_format = _("\"%s\" pou already exists!")
    -
    +
    # Test that graphic element name isn't already used in POU by a variable
    # or another graphic element
    - elif ((self.DefaultElementName is None or
    - self.DefaultElementName.upper() != uppercase_element_name) and
    + elif ((self.DefaultElementName is None or
    + self.DefaultElementName.upper() != uppercase_element_name) and
    uppercase_element_name in self.Controller.\
    GetEditedElementVariables(self.TagName)):
    message_format = _("\"%s\" element for this pou already exists!")
    -
    +
    # If an error have been identify, show error message dialog
    if message_format is not None:
    self.ShowErrorMessage(message_format % element_name)
    # Test failed
    return False
    -
    +
    # Test succeed
    return True
    -
    +
    def ShowErrorMessage(self, message):
    """
    Show an error message dialog over this dialog
    @param message: Error message to display
    """
    - dialog = wx.MessageDialog(self, message,
    - _("Error"),
    + dialog = wx.MessageDialog(self, message,
    + _("Error"),
    wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    -
    +
    def OnOK(self, event):
    """
    Called when dialog OK button is pressed
    @@ -248,7 +248,7 @@
    """
    # Close dialog
    self.EndModal(wx.ID_OK)
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    @@ -258,42 +258,42 @@
    dc = wx.ClientDC(self.Preview)
    dc.SetFont(self.Preview.GetFont())
    dc.Clear()
    -
    +
    # Return immediately if no graphic element defined
    if self.Element is None:
    return
    -
    +
    # Calculate block size according to graphic element min size due to its
    # parameters and graphic element min size defined
    min_width, min_height = self.GetMinElementSize()
    width = max(self.MinElementSize[0], min_width)
    height = max(self.MinElementSize[1], min_height)
    self.Element.SetSize(width, height)
    -
    +
    # Get element position and bounding box to center in preview
    posx, posy = self.Element.GetPosition()
    bbox = self.Element.GetBoundingBox()
    -
    +
    # Get Preview panel size
    client_size = self.Preview.GetClientSize()
    -
    +
    # If graphic element is too big to be displayed in preview panel,
    # calculate preview panel scale so that graphic element fit inside
    - scale = (max(float(bbox.width) / client_size.width,
    + scale = (max(float(bbox.width) / client_size.width,
    float(bbox.height) / client_size.height) * 1.1
    - if bbox.width * 1.1 > client_size.width or
    + if bbox.width * 1.1 > client_size.width or
    bbox.height * 1.1 > client_size.height
    else 1.0)
    dc.SetUserScale(1.0 / scale, 1.0 / scale)
    -
    +
    # Center graphic element in preview panel
    x = int(client_size.width * scale - bbox.width) / 2 + posx - bbox.x
    y = int(client_size.height * scale - bbox.height) / 2 + posy - bbox.y
    self.Element.SetPosition(x, y)
    -
    +
    # Draw graphic element
    self.Element.Draw(dc)
    -
    +
    def OnPaint(self, event):
    """
    Called when Preview panel need to be redraw
    @@ -301,4 +301,3 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    --- a/dialogs/BrowseLocationsDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/BrowseLocationsDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -37,16 +37,16 @@
    def GetDirFilterChoiceOptions():
    _ = lambda x : x
    - return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]),
    - (_("Input"), [LOCATION_VAR_INPUT]),
    - (_("Output"), [LOCATION_VAR_OUTPUT]),
    + return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]),
    + (_("Input"), [LOCATION_VAR_INPUT]),
    + (_("Output"), [LOCATION_VAR_OUTPUT]),
    (_("Memory"), [LOCATION_VAR_MEMORY])]
    DIRFILTERCHOICE_OPTIONS = dict([(_(option), filter) for option, filter in GetDirFilterChoiceOptions()])
    def GetTypeFilterChoiceOptions():
    _ = lambda x : x
    - return [_("All"),
    - _("Type and derivated"),
    + return [_("All"),
    + _("Type and derivated"),
    _("Type strict")]
    # turn LOCATIONDATATYPES inside-out
    @@ -60,81 +60,81 @@
    #-------------------------------------------------------------------------------
    class BrowseLocationsDialog(wx.Dialog):
    -
    +
    def __init__(self, parent, var_type, controller):
    wx.Dialog.__init__(self, parent, title=_('Browse Locations'),
    style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    locations_label = wx.StaticText(self, label=_('Locations available:'))
    - main_sizer.AddWindow(locations_label, border=20,
    + main_sizer.AddWindow(locations_label, border=20,
    flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
    -
    - self.LocationsTree = wx.TreeCtrl(self,
    +
    + self.LocationsTree = wx.TreeCtrl(self,
    style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT)
    self.LocationsTree.SetInitialSize(wx.Size(-1, 300))
    - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated,
    + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated,
    self.LocationsTree)
    - main_sizer.AddWindow(self.LocationsTree, border=20,
    + main_sizer.AddWindow(self.LocationsTree, border=20,
    flag=wx.LEFT|wx.RIGHT|wx.GROW)
    -
    +
    button_gridsizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
    button_gridsizer.AddGrowableCol(1)
    button_gridsizer.AddGrowableCol(3)
    button_gridsizer.AddGrowableRow(0)
    - main_sizer.AddSizer(button_gridsizer, border=20,
    + main_sizer.AddSizer(button_gridsizer, border=20,
    flag=wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.GROW)
    -
    +
    direction_label = wx.StaticText(self, label=_('Direction:'))
    button_gridsizer.AddWindow(direction_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    self.DirFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.DirFilterChoice)
    button_gridsizer.AddWindow(self.DirFilterChoice,
    flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL)
    -
    +
    filter_label = wx.StaticText(self, label=_('Type:'))
    button_gridsizer.AddWindow(filter_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    self.TypeFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY)
    self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.TypeFilterChoice)
    button_gridsizer.AddWindow(self.TypeFilterChoice,
    flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL)
    -
    +
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
    button_gridsizer.AddSizer(button_sizer, flag=wx.ALIGN_RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    self.Controller = controller
    self.VarType = var_type
    self.BaseVarType = self.Controller.GetBaseType(self.VarType)
    self.VarTypeSize = LOCATION_SIZES[self.BaseVarType]
    self.Locations = self.Controller.GetVariableLocationTree()
    -
    +
    # Define Tree item icon list
    self.TreeImageList = wx.ImageList(16, 16)
    self.TreeImageDict = {}
    -
    +
    # Icons for items
    for imgname, itemtype in [
    - ("CONFIGURATION", LOCATION_CONFNODE),
    - ("RESOURCE", LOCATION_MODULE),
    - ("PROGRAM", LOCATION_GROUP),
    - ("VAR_INPUT", LOCATION_VAR_INPUT),
    - ("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
    + ("CONFIGURATION", LOCATION_CONFNODE),
    + ("RESOURCE", LOCATION_MODULE),
    + ("PROGRAM", LOCATION_GROUP),
    + ("VAR_INPUT", LOCATION_VAR_INPUT),
    + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT),
    ("VAR_LOCAL", LOCATION_VAR_MEMORY)]:
    self.TreeImageDict[itemtype]=self.TreeImageList.Add(GetBitmap(imgname))
    -
    +
    # Assign icon list to TreeCtrls
    self.LocationsTree.SetImageList(self.TreeImageList)
    -
    +
    # Set a options for the choice
    for option, filter in GetDirFilterChoiceOptions():
    self.DirFilterChoice.Append(_(option))
    @@ -143,34 +143,34 @@
    self.TypeFilterChoice.Append(_(option))
    self.TypeFilterChoice.SetStringSelection(_("All"))
    self.RefreshFilters()
    -
    +
    self.RefreshLocationsTree()
    self.Fit()
    -
    +
    def RefreshFilters(self):
    self.DirFilter = DIRFILTERCHOICE_OPTIONS[self.DirFilterChoice.GetStringSelection()]
    self.TypeFilter = self.TypeFilterChoice.GetSelection()
    -
    +
    def RefreshLocationsTree(self):
    root = self.LocationsTree.GetRootItem()
    if not root.IsOk():
    root = self.LocationsTree.AddRoot("")
    self.GenerateLocationsTreeBranch(root, self.Locations)
    -
    +
    def FilterType(self, location_type, location_size):
    if self.TypeFilter == 0:
    return True
    -
    +
    if location_size != self.VarTypeSize:
    return False
    -
    +
    if self.TypeFilter == 1:
    return self.Controller.IsOfType(location_type, self.BaseVarType)
    elif self.TypeFilter == 2:
    return location_type == self.VarType
    -
    +
    return True
    -
    +
    def GenerateLocationsTreeBranch(self, root, locations):
    to_delete = []
    item, root_cookie = self.LocationsTree.GetFirstChild(root)
    @@ -194,21 +194,21 @@
    item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
    for item in to_delete:
    self.LocationsTree.Delete(item)
    -
    +
    def OnLocationsTreeItemActivated(self, event):
    infos = self.LocationsTree.GetPyData(event.GetItem())
    if infos["type"] not in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]:
    wx.CallAfter(self.EndModal, wx.ID_OK)
    event.Skip()
    -
    +
    def OnFilterChoice(self, event):
    self.RefreshFilters()
    self.RefreshLocationsTree()
    -
    +
    def GetValues(self):
    selected = self.LocationsTree.GetSelection()
    return self.LocationsTree.GetPyData(selected)
    -
    +
    def OnOK(self, event):
    selected = self.LocationsTree.GetSelection()
    var_infos = None
    --- a/dialogs/BrowseValuesLibraryDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/BrowseValuesLibraryDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -29,7 +29,7 @@
    class BrowseValuesLibraryDialog(wx.Dialog):
    """
    Modal dialog that helps in selecting predefined XML attributes sets out of hierarchically organized list
    - """
    + """
    def __init__(self, parent, name, library, default=None):
    wx.Dialog.__init__(self,
    @@ -40,27 +40,27 @@
    self.staticText1 = wx.StaticText(
    label=_('Choose a value for %s:') % name, name='staticText1', parent=self,
    pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    -
    +
    self.ValuesLibrary = wx.TreeCtrl(
    name='ValuesLibrary', parent=self, pos=wx.Point(0, 0),
    size=wx.Size(400, 200), style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT)
    -
    +
    self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
    -
    +
    self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    -
    +
    self.flexGridSizer1.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    self.flexGridSizer1.AddWindow(self.ValuesLibrary, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT)
    self.flexGridSizer1.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.flexGridSizer1.AddGrowableCol(0)
    self.flexGridSizer1.AddGrowableRow(1)
    -
    +
    self.SetSizer(self.flexGridSizer1)
    self.Fit()
    -
    +
    root = self.ValuesLibrary.AddRoot("")
    self.GenerateValuesLibraryBranch(root, library, default)
    @@ -85,4 +85,3 @@
    message.Destroy()
    else:
    self.EndModal(wx.ID_OK)
    -
    --- a/dialogs/ConnectionDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/ConnectionDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,7 +39,7 @@
    """
    class ConnectionDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname, apply_button=False):
    """
    Constructor
    @@ -49,49 +49,49 @@
    @param apply_button: Enable button for applying connector modification
    to all connector having the same name in POU (default: False)
    """
    - BlockPreviewDialog.__init__(self, parent, controller, tagname,
    + BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Connection Properties'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 7, 1, 0, None)
    -
    +
    # Create label for connection type
    type_label = wx.StaticText(self, label=_('Type:'))
    self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW)
    -
    +
    # Create radio buttons for selecting connection type
    self.TypeRadioButtons = {}
    first = True
    for type, label in [(CONNECTOR, _('Connector')),
    (CONTINUATION, _('Continuation'))]:
    - radio_button = wx.RadioButton(self, label=label,
    + radio_button = wx.RadioButton(self, label=label,
    style=(wx.RB_GROUP if first else 0))
    radio_button.SetValue(first)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button)
    self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW)
    self.TypeRadioButtons[type] = radio_button
    first = False
    -
    +
    # Create label for connection name
    name_label = wx.StaticText(self, label=_('Name:'))
    self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW)
    -
    +
    # Create text control for defining connection name
    self.ConnectionName = wx.TextCtrl(self)
    self.ConnectionName.SetMinSize(wx.Size(200,-1))
    self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.ConnectionName)
    self.LeftGridSizer.AddWindow(self.ConnectionName, flag=wx.GROW)
    -
    +
    # Add preview panel and associated label to sizers
    self.Preview.SetMinSize(wx.Size(-1,100))
    self.LeftGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.LeftGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    self.ColumnSizer.RemoveSizer(self.RightGridSizer)
    -
    +
    # Add button for applying connection name modification to all connection
    # of POU
    if apply_button:
    @@ -105,10 +105,10 @@
    controller.GenerateNewName(
    tagname, None, "Connection%d", 0))
    self.Fit()
    -
    +
    # Connector radio button is default control having keyboard focus
    self.TypeRadioButtons[CONNECTOR].SetFocus()
    -
    +
    def GetConnectionType(self):
    """
    Return type selected for connection
    @@ -117,7 +117,7 @@
    return (CONNECTOR
    if self.TypeRadioButtons[CONNECTOR].GetValue()
    else CONTINUATION)
    -
    +
    def SetValues(self, values):
    """
    Set default connection parameters
    @@ -125,18 +125,18 @@
    """
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is connection type
    if name == "type":
    self.TypeRadioButtons[value].SetValue(True)
    -
    +
    # Parameter is connection name
    elif name == "name":
    self.ConnectionName.SetValue(value)
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    -
    +
    def GetValues(self):
    """
    Return connection parameters defined in dialog
    @@ -154,23 +154,23 @@
    @return: True if connection name is valid
    """
    message = None
    -
    +
    # Get connection name typed by user
    connection_name = self.ConnectionName.GetValue()
    -
    +
    # Test that a name have been defined
    if connection_name == "":
    message = _("Form isn't complete. Name must be filled!")
    -
    +
    # If an error have been identify, show error message dialog
    if message is not None:
    self.ShowErrorMessage(message)
    # Test failed
    return False
    -
    +
    # Return result of element name test
    return self.TestElementName(connection_name)
    -
    +
    def OnOK(self, event):
    """
    Called when dialog OK button is pressed
    @@ -206,17 +206,16 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    Override BlockPreviewDialog function
    """
    # Set graphic element displayed, creating a FBD connection element
    - self.Element = FBD_Connector(self.Preview,
    + self.Element = FBD_Connector(self.Preview,
    self.GetConnectionType(),
    self.ConnectionName.GetValue())
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    -
    --- a/dialogs/DiscoveryDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/DiscoveryDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,56 +38,56 @@
    wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name)
    listmix.ListCtrlAutoWidthMixin.__init__(self)
    -[ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1,
    - ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON,
    - ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON,
    +[ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1,
    + ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON,
    + ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON,
    ] = [wx.NewId() for _init_ctrls in range(6)]
    class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin):
    -
    +
    def _init_coll_MainSizer_Items(self, parent):
    parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
    parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW)
    parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
    -
    +
    def _init_coll_MainSizer_Growables(self, parent):
    parent.AddGrowableCol(0)
    parent.AddGrowableRow(1)
    -
    +
    def _init_coll_ButtonGridSizer_Items(self, parent):
    parent.AddWindow(self.RefreshButton, 0, border=0, flag=0)
    parent.AddWindow(self.LocalButton, 0, border=0, flag=0)
    parent.AddWindow(self.IpButton, 0, border=0, flag=0)
    parent.AddSizer(self.ButtonSizer, 0, border=0, flag=0)
    -
    +
    def _init_coll_ButtonGridSizer_Growables(self, parent):
    parent.AddGrowableCol(0)
    parent.AddGrowableCol(1)
    parent.AddGrowableRow(0)
    -
    +
    def _init_sizers(self):
    self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    self.ButtonGridSizer = wx.FlexGridSizer(cols=4, hgap=5, rows=1, vgap=0)
    -
    +
    self._init_coll_MainSizer_Items(self.MainSizer)
    self._init_coll_MainSizer_Growables(self.MainSizer)
    self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer)
    self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer)
    -
    +
    self.SetSizer(self.MainSizer)
    -
    +
    def _init_ctrls(self, prnt):
    - wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG,
    + wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG,
    name='DiscoveryDialog', parent=prnt, style=wx.DEFAULT_DIALOG_STYLE,
    title=_('Service Discovery'))
    -
    +
    self.staticText1 = wx.StaticText(id=ID_DISCOVERYDIALOGSTATICTEXT1,
    label=_('Services available:'), name='staticText1', parent=self,
    pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    -
    +
    # Set up list control
    self.ServicesList = AutoWidthListCtrl(id=ID_DISCOVERYDIALOGSERVICESLIST,
    - name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0),
    + name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0),
    style=wx.LC_REPORT|wx.LC_EDIT_LABELS|wx.LC_SORT_ASCENDING|wx.LC_SINGLE_SEL)
    self.ServicesList.InsertColumn(0, _('NAME'))
    self.ServicesList.InsertColumn(1, _('TYPE'))
    @@ -100,14 +100,14 @@
    self.ServicesList.SetInitialSize(wx.Size(-1, 300))
    self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, id=ID_DISCOVERYDIALOGSERVICESLIST)
    self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_DISCOVERYDIALOGSERVICESLIST)
    -
    +
    listmix.ColumnSorterMixin.__init__(self, 4)
    -
    +
    self.RefreshButton = wx.Button(id=ID_DISCOVERYDIALOGREFRESHBUTTON,
    label=_('Refresh'), name='RefreshButton', parent=self,
    pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON)
    -
    +
    self.LocalButton = wx.Button(id=ID_DISCOVERYDIALOGLOCALBUTTON,
    label=_('Local'), name='LocalButton', parent=self,
    pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    @@ -117,29 +117,29 @@
    label=_('Add IP'), name='IpButton', parent=self,
    pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    self.Bind(wx.EVT_BUTTON, self.OnIpButton, id=ID_DISCOVERYDIALOGIPBUTTON)
    -
    +
    self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTER)
    -
    +
    self._init_sizers()
    self.Fit()
    -
    +
    def __init__(self, parent):
    self._init_ctrls(parent)
    -
    +
    self.itemDataMap = {}
    self.nextItemId = 0
    -
    +
    self.URI = None
    self.Browser = None
    -
    +
    self.ZeroConfInstance = Zeroconf()
    self.RefreshList()
    self.LatestSelection=None
    -
    +
    def __del__(self):
    if self.Browser is not None : self.Browser.cancel()
    self.ZeroConfInstance.close()
    -
    +
    def RefreshList(self):
    if self.Browser is not None : self.Browser.cancel()
    self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self)
    @@ -181,18 +181,18 @@
    # connect_type = self.getColumnText(idx, 1)
    # connect_address = self.getColumnText(idx, 2)
    # connect_port = self.getColumnText(idx, 3)
    -#
    +#
    # self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port)
    def SetURI(self, idx):
    self.LatestSelection = idx
    - svcname = self.getColumnText(idx, 0)
    + svcname = self.getColumnText(idx, 0)
    connect_type = self.getColumnText(idx, 1)
    self.URI = "%s://%s"%(connect_type, svcname + '.' + service_type)
    -
    +
    def GetURI(self):
    return self.URI
    -
    +
    def removeService(self, zeroconf, _type, name):
    wx.CallAfter(self._removeService, name)
    @@ -201,7 +201,7 @@
    '''
    called when a service with the desired type goes offline.
    '''
    -
    +
    # loop through the list items looking for the service that went offline
    for idx in xrange(self.ServicesList.GetItemCount()):
    # this is the unique identifier assigned to the item
    @@ -213,7 +213,7 @@
    if item_name == name:
    self.ServicesList.DeleteItem(idx)
    break
    -
    +
    def addService(self, zeroconf, _type, name):
    wx.CallAfter(self._addService, _type, name)
    @@ -240,7 +240,7 @@
    # we assign every list item a unique id (that won't change when items
    # are added or removed)
    self.ServicesList.SetItemData(new_item, self.nextItemId)
    -
    +
    # the value of each column has to be stored in the itemDataMap
    # so that ColumnSorterMixin knows how to sort the column.
    @@ -249,4 +249,3 @@
    self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ]
    self.nextItemId += 1
    -
    --- a/dialogs/DurationEditorDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/DurationEditorDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -57,43 +57,43 @@
    def __init__(self, parent):
    wx.Dialog.__init__(self, parent, title=_('Edit Duration'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    controls_sizer = wx.FlexGridSizer(cols=len(CONTROLS), hgap=10, rows=2, vgap=10)
    - main_sizer.AddSizer(controls_sizer, border=20,
    + main_sizer.AddSizer(controls_sizer, border=20,
    flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
    -
    +
    controls = []
    for i, (name, label) in enumerate(CONTROLS):
    controls_sizer.AddGrowableCol(i)
    -
    +
    st = wx.StaticText(self, label=label)
    txtctrl = wx.TextCtrl(self, value='0', style=wx.TE_PROCESS_ENTER)
    - self.Bind(wx.EVT_TEXT_ENTER,
    - self.GetControlValueTestFunction(txtctrl),
    + self.Bind(wx.EVT_TEXT_ENTER,
    + self.GetControlValueTestFunction(txtctrl),
    txtctrl)
    setattr(self, name, txtctrl)
    -
    +
    controls.append((st, txtctrl))
    -
    +
    for st, txtctrl in controls:
    controls_sizer.AddWindow(st, flag=wx.GROW)
    -
    +
    for st, txtctrl in controls:
    controls_sizer.AddWindow(txtctrl, flag=wx.GROW)
    -
    +
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
    - main_sizer.AddSizer(button_sizer, border=20,
    + main_sizer.AddSizer(button_sizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    self.Fit()
    self.Days.SetFocus()
    -
    +
    def SetDuration(self, value):
    result = IEC_TIME_MODEL.match(value.upper())
    if result is not None:
    @@ -112,7 +112,7 @@
    else:
    self.Milliseconds.SetValue("0")
    self.Microseconds.SetValue("0")
    -
    +
    def GetControlValueTestFunction(self, control):
    def OnValueChanged(event):
    try:
    @@ -130,29 +130,29 @@
    for control, factor in [(self.Days, DAY), (self.Hours, HOUR),
    (self.Minutes, MINUTE), (self.Seconds, SECOND),
    (self.Milliseconds, MILLISECONDS), (self.Microseconds, MICROSECONDS)]:
    -
    +
    milliseconds += float(control.GetValue()) * factor
    -
    +
    not_null = False
    duration = "T#"
    for value, format in [(int(milliseconds) / DAY, "%dd"),
    ((int(milliseconds) % DAY) / HOUR, "%dh"),
    ((int(milliseconds) % HOUR) / MINUTE, "%dm"),
    ((int(milliseconds) % MINUTE) / SECOND, "%ds")]:
    -
    +
    if value > 0 or not_null:
    duration += format % value
    not_null = True
    -
    +
    duration += "%gms" % (milliseconds % SECOND)
    return duration
    -
    +
    def OnOK(self, event):
    self.OnCloseDialog()
    def OnCloseDialog(self):
    errors = []
    - for control, name in [(self.Days, _("days")), (self.Hours, _("hours")),
    + for control, name in [(self.Days, _("days")), (self.Hours, _("hours")),
    (self.Minutes, _("minutes")), (self.Seconds, _("seconds")),
    (self.Milliseconds, _("milliseconds"))]:
    try:
    --- a/dialogs/FBDBlockDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/FBDBlockDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -48,7 +48,7 @@
    """
    class FBDBlockDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname):
    """
    Constructor
    @@ -58,83 +58,83 @@
    """
    BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Block Properties'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 1, 0, 3, 2)
    -
    +
    # Create static box around library panel
    type_staticbox = wx.StaticBox(self, label=_('Type:'))
    left_staticboxsizer = wx.StaticBoxSizer(type_staticbox, wx.VERTICAL)
    self.LeftGridSizer.AddSizer(left_staticboxsizer, border=5, flag=wx.GROW)
    -
    +
    # Create Library panel and add it to static box
    self.LibraryPanel = LibraryPanel(self)
    self.LibraryPanel.SetInitialSize(wx.Size(-1, 400))
    -
    +
    # Set function to call when selection in Library panel changed
    - setattr(self.LibraryPanel, "_OnTreeItemSelected",
    + setattr(self.LibraryPanel, "_OnTreeItemSelected",
    self.OnLibraryTreeItemSelected)
    - left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5,
    + left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5,
    flag=wx.GROW|wx.TOP)
    -
    +
    # Create sizer for other block parameters
    top_right_gridsizer = wx.FlexGridSizer(cols=2, hgap=0, rows=4, vgap=5)
    top_right_gridsizer.AddGrowableCol(1)
    self.RightGridSizer.AddSizer(top_right_gridsizer, flag=wx.GROW)
    -
    +
    # Create label for block name
    name_label = wx.StaticText(self, label=_('Name:'))
    - top_right_gridsizer.AddWindow(name_label,
    + top_right_gridsizer.AddWindow(name_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    # Create text control for defining block name
    self.BlockName = wx.TextCtrl(self)
    self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.BlockName)
    top_right_gridsizer.AddWindow(self.BlockName, flag=wx.GROW)
    -
    +
    # Create label for extended block input number
    inputs_label = wx.StaticText(self, label=_('Inputs:'))
    - top_right_gridsizer.AddWindow(inputs_label,
    + top_right_gridsizer.AddWindow(inputs_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    # Create spin control for defining extended block input number
    self.Inputs = wx.SpinCtrl(self, min=2, max=20,
    style=wx.SP_ARROW_KEYS)
    self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, self.Inputs)
    top_right_gridsizer.AddWindow(self.Inputs, flag=wx.GROW)
    -
    +
    # Create label for block execution order
    - execution_order_label = wx.StaticText(self,
    + execution_order_label = wx.StaticText(self,
    label=_('Execution Order:'))
    - top_right_gridsizer.AddWindow(execution_order_label,
    + top_right_gridsizer.AddWindow(execution_order_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    # Create spin control for defining block execution order
    self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS)
    - self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged,
    + self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged,
    self.ExecutionOrder)
    top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW)
    -
    +
    # Create label for block execution control
    - execution_control_label = wx.StaticText(self,
    + execution_control_label = wx.StaticText(self,
    label=_('Execution Control:'))
    - top_right_gridsizer.AddWindow(execution_control_label,
    + top_right_gridsizer.AddWindow(execution_control_label,
    flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    # Create check box to enable block execution control
    self.ExecutionControl = wx.CheckBox(self)
    - self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged,
    + self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged,
    self.ExecutionControl)
    top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW)
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    # Dictionary containing correspondence between parameter exchanged and
    # control to fill with parameter value
    self.ParamsControl = {
    @@ -142,20 +142,20 @@
    "executionOrder": self.ExecutionOrder,
    "executionControl": self.ExecutionControl
    }
    -
    +
    # Init controls value and sensibility
    self.BlockName.SetValue("")
    self.BlockName.Enable(False)
    self.Inputs.Enable(False)
    -
    +
    # Variable containing last name typed
    self.CurrentBlockName = None
    -
    +
    # Refresh Library panel values
    self.LibraryPanel.SetBlockList(controller.GetBlockTypes(tagname))
    self.Fit()
    self.LibraryPanel.SetFocus()
    -
    +
    def SetValues(self, values):
    """
    Set default block parameters
    @@ -163,38 +163,38 @@
    """
    # Extract block type defined in parameters
    blocktype = values.get("type", None)
    -
    - # Select block type in library panel
    +
    + # Select block type in library panel
    if blocktype is not None:
    - self.LibraryPanel.SelectTreeItem(blocktype,
    + self.LibraryPanel.SelectTreeItem(blocktype,
    values.get("inputs", None))
    -
    +
    # Define regular expression for determine if block name is block
    # default name
    default_name_model = GetBlockTypeDefaultNameModel(blocktype)
    -
    +
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is block name
    if name == "name":
    if value != "":
    # Set default graphic element name for testing
    self.DefaultElementName = value
    -
    +
    # Test if block name is type default block name and save
    # block name if not (name have been typed by user)
    if default_name_model.match(value) is None:
    self.CurrentBlockName = value
    -
    +
    self.BlockName.ChangeValue(value)
    -
    +
    # Set value of other controls
    else:
    control = self.ParamsControl.get(name, None)
    if control is not None:
    control.SetValue(value)
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    @@ -211,7 +211,7 @@
    name: control.GetValue()
    for name, control in self.ParamsControl.iteritems()})
    return values
    -
    +
    def OnOK(self, event):
    """
    Called when dialog OK button is pressed
    @@ -219,31 +219,31 @@
    @param event: wx.Event from OK button
    """
    message = None
    -
    +
    # Get block type selected
    selected = self.LibraryPanel.GetSelectedBlock()
    -
    +
    # Get block type name and if block is a function block
    block_name = self.BlockName.GetValue()
    name_enabled = self.BlockName.IsEnabled()
    -
    +
    # Test that a type has been selected for block
    if selected is None:
    message = _("Form isn't complete. Valid block type must be selected!")
    -
    +
    # Test, if block is a function block, that a name have been defined
    elif name_enabled and block_name == "":
    message = _("Form isn't complete. Name must be filled!")
    -
    +
    # Show error message if an error is detected
    if message is not None:
    self.ShowErrorMessage(message)
    -
    +
    # Test block name validity if necessary
    elif not name_enabled or self.TestElementName(block_name):
    # Call BlockPreviewDialog function
    BlockPreviewDialog.OnOK(self, event)
    -
    +
    def OnLibraryTreeItemSelected(self, event):
    """
    Called when block type selected in library panel
    @@ -251,12 +251,12 @@
    """
    # Get type selected in library panel
    values = self.LibraryPanel.GetSelectedBlock()
    -
    +
    # Get block type informations
    - blocktype = (self.Controller.GetBlockType(values["type"],
    + blocktype = (self.Controller.GetBlockType(values["type"],
    values["inputs"])
    if values is not None else None)
    -
    +
    # Set input number spin control according to block type informations
    if blocktype is not None:
    self.Inputs.SetValue(len(blocktype["inputs"]))
    @@ -264,12 +264,12 @@
    else:
    self.Inputs.SetValue(2)
    self.Inputs.Enable(False)
    -
    +
    # Update block name with default value if block type is a function and
    # current block name wasn't typed by user
    if blocktype is not None and blocktype["type"] != "function":
    self.BlockName.Enable(True)
    -
    +
    if self.CurrentBlockName is None:
    # Generate new block name according to block type, taking
    # default element name if it was already a default name for this
    @@ -277,21 +277,21 @@
    default_name_model = GetBlockTypeDefaultNameModel(values["type"])
    block_name = (
    self.DefaultElementName
    - if (self.DefaultElementName is not None and
    + if (self.DefaultElementName is not None and
    default_name_model.match(self.DefaultElementName))
    else self.Controller.GenerateNewName(
    self.TagName, None, values["type"]+"%d", 0))
    else:
    block_name = self.CurrentBlockName
    -
    +
    self.BlockName.ChangeValue(block_name)
    else:
    self.BlockName.Enable(False)
    self.BlockName.ChangeValue("")
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    -
    +
    def OnNameChanged(self, event):
    """
    Called when block name value changed
    @@ -302,7 +302,7 @@
    self.CurrentBlockName = self.BlockName.GetValue()
    self.RefreshPreview()
    event.Skip()
    -
    +
    def OnInputsChanged(self, event):
    """
    Called when block inputs number changed
    @@ -311,7 +311,7 @@
    if self.Inputs.IsEnabled():
    self.RefreshPreview()
    event.Skip()
    -
    +
    def OnExecutionOrderChanged(self, event):
    """
    Called when block execution order value changed
    @@ -319,7 +319,7 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def OnExecutionControlChanged(self, event):
    """
    Called when block execution control value changed
    @@ -327,7 +327,7 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    @@ -335,22 +335,22 @@
    """
    # Get type selected in library panel
    values = self.LibraryPanel.GetSelectedBlock()
    -
    +
    # If a block type is selected in library panel
    if values is not None:
    # Set graphic element displayed, creating a FBD block element
    - self.Element = FBD_Block(self.Preview, values["type"],
    + self.Element = FBD_Block(self.Preview, values["type"],
    (self.BlockName.GetValue()
    if self.BlockName.IsEnabled()
    - else ""),
    - extension = self.Inputs.GetValue(),
    - inputs = values["inputs"],
    - executionControl = self.ExecutionControl.GetValue(),
    + else ""),
    + extension = self.Inputs.GetValue(),
    + inputs = values["inputs"],
    + executionControl = self.ExecutionControl.GetValue(),
    executionOrder = self.ExecutionOrder.GetValue())
    -
    +
    # Reset graphic element displayed
    else:
    - self.Element = None
    -
    + self.Element = None
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    --- a/dialogs/FBDVariableDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/FBDVariableDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -62,69 +62,69 @@
    """
    BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Variable Properties'))
    -
    +
    # Init common sizers
    self._init_sizers(4, 2, 4, None, 3, 2)
    -
    +
    # Create label for variable class
    class_label = wx.StaticText(self, label=_('Class:'))
    self.LeftGridSizer.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)
    self.LeftGridSizer.AddWindow(self.Class, flag=wx.GROW)
    -
    +
    # Create label for variable execution order
    - execution_order_label = wx.StaticText(self,
    + execution_order_label = wx.StaticText(self,
    label=_('Execution Order:'))
    self.LeftGridSizer.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.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged,
    self.ExecutionOrder)
    self.LeftGridSizer.AddWindow(self.ExecutionOrder, flag=wx.GROW)
    -
    +
    # Create label for variable expression
    name_label = wx.StaticText(self, label=_('Expression:'))
    - self.RightGridSizer.AddWindow(name_label, border=5,
    + self.RightGridSizer.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)
    self.RightGridSizer.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(-1,120),
    style=wx.LB_SINGLE|wx.LB_SORT)
    self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName)
    self.RightGridSizer.AddWindow(self.VariableName, border=4, flag=wx.GROW|wx.TOP)
    -
    +
    # Add preview panel and associated label to sizers
    self.MainSizer.AddWindow(self.PreviewLabel, border=20,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    self.MainSizer.AddWindow(self.Preview, border=20,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    # Set options that can be selected in class combo box
    for var_class, choice in VARIABLE_CLASSES_DICT.iteritems():
    if not exclude_input or var_class != INPUT:
    self.Class.Append(choice)
    self.Class.SetSelection(0)
    -
    +
    # Extract list of variables defined in POU
    self.RefreshVariableList()
    -
    +
    # Refresh values in name list box
    self.RefreshNameList()
    -
    +
    self.Preview.SetInitialSize(wx.Size(-1, 60))
    self.Fit()
    @@ -138,32 +138,32 @@
    # 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.VariableList.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
    + 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:
    @@ -171,10 +171,10 @@
    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
    @@ -184,15 +184,15 @@
    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()
    self.Fit()
    -
    +
    def GetValues(self):
    """
    Return block parameters defined in dialog
    @@ -215,16 +215,16 @@
    @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)
    @@ -236,7 +236,7 @@
    """
    # Refresh name list box values
    self.RefreshNameList()
    -
    +
    self.RefreshPreview()
    event.Skip()
    @@ -249,10 +249,10 @@
    # 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
    @@ -261,10 +261,10 @@
    # 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
    @@ -272,7 +272,7 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    @@ -280,16 +280,14 @@
    """
    # 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,
    + self.Element = FBD_Variable(self.Preview,
    VARIABLE_CLASSES_DICT_REVERSE[
    - self.Class.GetStringSelection()],
    - name,
    - self.VariableList.get(name, ("", ""))[1],
    + self.Class.GetStringSelection()],
    + name,
    + self.VariableList.get(name, ("", ""))[1],
    executionOrder = self.ExecutionOrder.GetValue())
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    -
    -
    --- a/dialogs/FindInPouDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/FindInPouDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -32,80 +32,80 @@
    if parent and parent.icon:
    self.SetIcon(parent.icon)
    -
    +
    def __init__(self, parent):
    - wx.Dialog.__init__(self, parent, title=_("Find"),
    + wx.Dialog.__init__(self, parent, title=_("Find"),
    style=wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.RESIZE_BORDER)
    -
    +
    self._init_icon(parent)
    panel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    controls_sizer = wx.BoxSizer(wx.VERTICAL)
    - main_sizer.AddSizer(controls_sizer, border=20,
    + main_sizer.AddSizer(controls_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    patterns_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5)
    patterns_sizer.AddGrowableCol(1)
    controls_sizer.AddSizer(patterns_sizer, border=5, flag=wx.GROW|wx.BOTTOM)
    -
    +
    find_label = wx.StaticText(panel, label=_("Find:"))
    patterns_sizer.AddWindow(find_label, flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    self.FindPattern = wx.TextCtrl(panel)
    self.Bind(wx.EVT_TEXT, self.OnFindPatternChanged, self.FindPattern)
    self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey)
    patterns_sizer.AddWindow(self.FindPattern, flag=wx.GROW)
    -
    +
    params_sizer = wx.BoxSizer(wx.HORIZONTAL)
    controls_sizer.AddSizer(params_sizer, border=5, flag=wx.GROW|wx.BOTTOM)
    -
    +
    direction_staticbox = wx.StaticBox(panel, label=_("Direction"))
    direction_staticboxsizer = wx.StaticBoxSizer(
    direction_staticbox, wx.VERTICAL)
    - params_sizer.AddSizer(direction_staticboxsizer, 1, border=5,
    + params_sizer.AddSizer(direction_staticboxsizer, 1, border=5,
    flag=wx.GROW|wx.RIGHT)
    -
    - self.Forward = wx.RadioButton(panel, label=_("Forward"),
    +
    + self.Forward = wx.RadioButton(panel, label=_("Forward"),
    style=wx.RB_GROUP)
    - direction_staticboxsizer.AddWindow(self.Forward, border=5,
    + direction_staticboxsizer.AddWindow(self.Forward, border=5,
    flag=wx.ALL|wx.GROW)
    -
    +
    self.Backward = wx.RadioButton(panel, label=_("Backward"))
    - direction_staticboxsizer.AddWindow(self.Backward, border=5,
    + direction_staticboxsizer.AddWindow(self.Backward, border=5,
    flag=wx.ALL|wx.GROW)
    -
    +
    options_staticbox = wx.StaticBox(panel, label=_("Options"))
    options_staticboxsizer = wx.StaticBoxSizer(
    options_staticbox, wx.VERTICAL)
    params_sizer.AddSizer(options_staticboxsizer, 1, flag=wx.GROW)
    -
    +
    self.CaseSensitive = wx.CheckBox(panel, label=_("Case sensitive"))
    self.CaseSensitive.SetValue(True)
    - options_staticboxsizer.AddWindow(self.CaseSensitive, border=5,
    + options_staticboxsizer.AddWindow(self.CaseSensitive, border=5,
    flag=wx.ALL|wx.GROW)
    -
    +
    self.WrapSearch = wx.CheckBox(panel, label=_("Wrap search"))
    self.WrapSearch.SetValue(True)
    - options_staticboxsizer.AddWindow(self.WrapSearch, border=5,
    + options_staticboxsizer.AddWindow(self.WrapSearch, border=5,
    flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
    -
    +
    self.RegularExpressions = wx.CheckBox(panel, label=_("Regular expressions"))
    - options_staticboxsizer.AddWindow(self.RegularExpressions, border=5,
    + options_staticboxsizer.AddWindow(self.RegularExpressions, border=5,
    flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
    -
    +
    buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
    - main_sizer.AddSizer(buttons_sizer, border=20,
    + main_sizer.AddSizer(buttons_sizer, border=20,
    flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_RIGHT)
    -
    +
    self.FindButton = wx.Button(panel, label=_("Find"))
    self.FindButton.SetDefault()
    self.Bind(wx.EVT_BUTTON, self.OnFindButton, self.FindButton)
    buttons_sizer.AddWindow(self.FindButton, border=5, flag=wx.RIGHT)
    -
    +
    self.CloseButton = wx.Button(panel, label=_("Close"))
    self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton)
    buttons_sizer.AddWindow(self.CloseButton)
    @@ -115,30 +115,30 @@
    self.RegExpSyntaxErrMsg = _("Syntax error in regular expression of pattern to search!")
    self.StatusLabel = wx.StaticText(panel, label= self.RegExpSyntaxErrMsg)
    controls_sizer.AddWindow(self.StatusLabel, flag=wx.ALIGN_CENTER_VERTICAL)
    -
    +
    panel.SetSizer(main_sizer)
    main_sizer.Fit(self)
    # clear message after dialog size calculation
    self.SetStatusText("")
    -
    +
    self.ParentWindow = parent
    -
    +
    self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
    self.infosPrev = {}
    self.criteria = {}
    self.FindPattern.SetFocus()
    self.RefreshButtonsState()
    -
    +
    def RefreshButtonsState(self):
    find_pattern = self.FindPattern.GetValue()
    self.FindButton.Enable(find_pattern != "")
    -
    +
    def OnCloseFrame(self, event):
    self.Hide()
    event.Veto()
    -
    +
    def OnCloseButton(self, event):
    self.Hide()
    event.Skip()
    @@ -157,7 +157,7 @@
    def SetStatusText(self, msg):
    self.StatusLabel.SetLabel(msg)
    self.Layout()
    -
    +
    def OnFindButton(self, event):
    infos = {
    "find_pattern": self.FindPattern.GetValue(),
    --- a/dialogs/ForceVariableDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/ForceVariableDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -34,7 +34,7 @@
    "B" : ["SINT", "USINT", "BYTE", "STRING"],
    "W" : ["INT", "UINT", "WORD", "WSTRING"],
    "D" : ["DINT", "UDINT", "REAL", "DWORD"],
    - "L" : ["LINT", "ULINT", "LREAL", "LWORD"]}
    + "L" : ["LINT", "ULINT", "LREAL", "LWORD"]}
    def gen_get_function(f):
    def get_function(v):
    @@ -68,7 +68,7 @@
    IEC_DATETIME_MODEL = re.compile("(?:(?:DT|DATE_AND_TIME)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$")
    IEC_TIMEOFDAY_MODEL = re.compile("(?:(?:TOD|TIME_OF_DAY)#)?([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$")
    -def gettime(v):
    +def gettime(v):
    result = IEC_TIME_MODEL.match(v.upper())
    if result is not None:
    negative, days, hours, minutes, seconds, milliseconds = result.groups()
    @@ -87,8 +87,8 @@
    if negative is not None:
    microseconds = -microseconds
    return datetime.timedelta(microseconds=microseconds)
    -
    - else:
    +
    + else:
    return None
    def getdate(v):
    @@ -101,7 +101,7 @@
    return None
    base_date = datetime.datetime(1970, 1, 1)
    return date - base_date
    - else:
    + else:
    return None
    def getdatetime(v):
    @@ -114,7 +114,7 @@
    return None
    base_date = datetime.datetime(1970, 1, 1)
    return date - base_date
    - else:
    + else:
    return None
    def gettimeofday(v):
    @@ -159,13 +159,13 @@
    class ForceVariableDialog(wx.TextEntryDialog):
    def __init__(self, parent, iec_type, defaultValue=""):
    - wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"),
    - caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue,
    + wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"),
    + caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue,
    style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition)
    -
    - self.IEC_Type = iec_type
    -
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    +
    + self.IEC_Type = iec_type
    +
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton())
    self.ValueTextCtrl=self.GetSizer().GetItem(1).GetWindow()
    if self.IEC_Type == "BOOL":
    --- a/dialogs/LDElementDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/LDElementDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -41,7 +41,7 @@
    """
    class LDElementDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname, type):
    """
    Constructor
    @@ -50,24 +50,24 @@
    @param tagname: Tagname of project POU edited
    @param type: Type of LD element ('contact or 'coil')
    """
    - BlockPreviewDialog.__init__(self, parent, controller, tagname,
    + BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=(_("Edit Contact Values")
    if type == "contact"
    else _("Edit Coil Values")))
    -
    +
    # Init common sizers
    - self._init_sizers(2, 0,
    + self._init_sizers(2, 0,
    (7 if type == "contact" else 9), None, 2, 1)
    -
    +
    # Create label for LD element modifier
    modifier_label = wx.StaticText(self, label=_('Modifier:'))
    - self.LeftGridSizer.AddWindow(modifier_label, border=5,
    + self.LeftGridSizer.AddWindow(modifier_label, border=5,
    flag=wx.GROW|wx.BOTTOM)
    -
    +
    # Create radio buttons for selecting LD element modifier
    self.ModifierRadioButtons = {}
    first = True
    - element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE,
    + element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE,
    CONTACT_RISING, CONTACT_FALLING]
    if type == "contact"
    else [COIL_NORMAL, COIL_REVERSE, COIL_SET,
    @@ -75,55 +75,55 @@
    modifiers_label = [_("Normal"), _("Negated")] + \
    ([_("Set"), _("Reset")] if type == "coil" else []) + \
    [_("Rising Edge"), _("Falling Edge")]
    -
    +
    for modifier, label in zip(element_modifiers, modifiers_label):
    - radio_button = wx.RadioButton(self, label=label,
    + radio_button = wx.RadioButton(self, label=label,
    style=(wx.RB_GROUP if first else 0))
    radio_button.SetValue(first)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnModifierChanged, radio_button)
    self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW)
    self.ModifierRadioButtons[modifier] = radio_button
    first = False
    -
    +
    # Create label for LD element variable
    element_variable_label = wx.StaticText(self, label=_('Variable:'))
    self.LeftGridSizer.AddWindow(element_variable_label, border=5,
    flag=wx.GROW|wx.TOP)
    -
    +
    # Create a combo box for defining LD element variable
    self.ElementVariable = wx.ComboBox(self, style=wx.CB_SORT)
    - self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged,
    + self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged,
    self.ElementVariable)
    - self.Bind(wx.EVT_TEXT, self.OnVariableChanged,
    - self.ElementVariable)
    + self.Bind(wx.EVT_TEXT, self.OnVariableChanged,
    + self.ElementVariable)
    self.LeftGridSizer.AddWindow(self.ElementVariable, border=5,
    flag=wx.GROW|wx.TOP)
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    # Save LD element class
    self.ElementClass = (LD_Contact if type == "contact" else LD_Coil)
    -
    +
    # Extract list of variables defined in POU
    self.RefreshVariableList()
    -
    +
    # Set values in ElementVariable
    for name, (var_type, value_type) in self.VariableList.iteritems():
    # Only select BOOL variable and avoid input for coil
    if (type == "contact" or var_type != "Input") and \
    value_type == "BOOL":
    self.ElementVariable.Append(name)
    -
    +
    self.Fit()
    # Normal radio button is default control having keyboard focus
    self.ModifierRadioButtons[element_modifiers[0]].SetFocus()
    -
    +
    def GetElementModifier(self):
    """
    Return modifier selected for LD element
    @@ -143,15 +143,15 @@
    """
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is LD element variable
    if name == "variable":
    self.ElementVariable.SetValue(value)
    -
    +
    # Set value of other controls
    elif name == "modifier":
    self.ModifierRadioButtons[value].SetValue(True)
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    @@ -188,20 +188,19 @@
    Override BlockPreviewDialog function
    """
    value = self.ElementVariable.GetValue()
    -
    +
    # Set graphic element displayed, creating a LD element
    self.Element = self.ElementClass(
    - self.Preview,
    + self.Preview,
    self.GetElementModifier(),
    value)
    button = self.ButtonSizer.GetAffirmativeButton()
    button.Enable(value != "")
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    -
    +
    def OnOK(self, event):
    if self.ElementVariable.GetValue() != "":
    self.EndModal(wx.ID_OK)
    -
    --- a/dialogs/LDPowerRailDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/LDPowerRailDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,7 +39,7 @@
    """
    class LDPowerRailDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname):
    """
    Constructor
    @@ -49,50 +49,50 @@
    """
    BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Power Rail Properties'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 5, None, 2, 1)
    -
    +
    # Create label for connection type
    type_label = wx.StaticText(self, label=_('Type:'))
    self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW)
    -
    +
    # Create radio buttons for selecting power rail type
    self.TypeRadioButtons = {}
    first = True
    for type, label in [(LEFTRAIL, _('Left PowerRail')),
    (RIGHTRAIL, _('Right PowerRail'))]:
    - radio_button = wx.RadioButton(self, label=label,
    + radio_button = wx.RadioButton(self, label=label,
    style=(wx.RB_GROUP if first else 0))
    radio_button.SetValue(first)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button)
    self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW)
    self.TypeRadioButtons[type] = radio_button
    first = False
    -
    +
    # Create label for power rail pin number
    pin_number_label = wx.StaticText(self, label=_('Pin number:'))
    self.LeftGridSizer.AddWindow(pin_number_label, flag=wx.GROW)
    -
    +
    # Create spin control for defining power rail pin number
    self.PinNumber = wx.SpinCtrl(self, min=1, max=50,
    style=wx.SP_ARROW_KEYS)
    self.PinNumber.SetValue(1)
    self.Bind(wx.EVT_SPINCTRL, self.OnPinNumberChanged, self.PinNumber)
    self.LeftGridSizer.AddWindow(self.PinNumber, flag=wx.GROW)
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    self.Fit()
    -
    +
    # Left Power Rail radio button is default control having keyboard focus
    self.TypeRadioButtons[LEFTRAIL].SetFocus()
    -
    +
    def GetMinElementSize(self):
    """
    Get minimal graphic element size
    @@ -100,7 +100,7 @@
    element defined
    """
    return self.Element.GetMinSize(True)
    -
    +
    def GetPowerRailType(self):
    """
    Return type selected for power rail
    @@ -109,7 +109,7 @@
    return (LEFTRAIL
    if self.TypeRadioButtons[LEFTRAIL].GetValue()
    else RIGHTRAIL)
    -
    +
    def SetValues(self, values):
    """
    Set default power rail parameters
    @@ -117,11 +117,11 @@
    """
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is power rail type
    if name == "type":
    self.TypeRadioButtons[value].SetValue(True)
    -
    +
    # Parameter is power rail pin number
    elif name == "pin_number":
    self.PinNumber.SetValue(value)
    @@ -158,11 +158,11 @@
    Refresh preview panel of graphic element
    Override BlockPreviewDialog function
    """
    -
    +
    # Set graphic element displayed, creating a power rail element
    - self.Element = LD_PowerRail(self.Preview,
    - self.GetPowerRailType(),
    + self.Element = LD_PowerRail(self.Preview,
    + self.GetPowerRailType(),
    connectors = self.PinNumber.GetValue())
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    --- a/dialogs/PouActionDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/PouActionDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -33,48 +33,48 @@
    ACTION_LANGUAGES_DICT = dict([(_(language), language) for language in GetActionLanguages()])
    class PouActionDialog(wx.Dialog):
    -
    +
    def __init__(self, parent):
    wx.Dialog.__init__(self, parent, title=_('Create a new action'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15)
    infos_sizer.AddGrowableCol(1)
    - main_sizer.AddSizer(infos_sizer, border=20,
    + main_sizer.AddSizer(infos_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    actionname_label = wx.StaticText(self, label=_('Action Name:'))
    - infos_sizer.AddWindow(actionname_label, border=4,
    + infos_sizer.AddWindow(actionname_label, border=4,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
    -
    +
    self.ActionName = wx.TextCtrl(self, size=wx.Size(180,-1))
    infos_sizer.AddWindow(self.ActionName, flag=wx.GROW)
    -
    +
    language_label = wx.StaticText(self, label=_('Language:'))
    - infos_sizer.AddWindow(language_label, border=4,
    + infos_sizer.AddWindow(language_label, border=4,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
    -
    +
    self.Language = wx.ComboBox(self, style=wx.CB_READONLY)
    infos_sizer.AddWindow(self.Language, flag=wx.GROW)
    -
    +
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    button_sizer.GetAffirmativeButton())
    - main_sizer.AddSizer(button_sizer, border=20,
    + main_sizer.AddSizer(button_sizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    for option in GetActionLanguages():
    self.Language.Append(_(option))
    -
    +
    self.Fit()
    self.PouNames = []
    self.PouElementNames = []
    -
    +
    def OnOK(self, event):
    error = []
    action_name = self.ActionName.GetValue()
    @@ -91,7 +91,7 @@
    elif i == len(error) - 1:
    text += _(" and %s")%item
    else:
    - text += _(", %s")%item
    + text += _(", %s")%item
    message = _("Form isn't complete. %s must be filled!") % text
    elif not TestIdentifier(action_name):
    message = _("\"%s\" is not a valid identifier!") % action_name
    @@ -107,23 +107,22 @@
    dialog.Destroy()
    else:
    self.EndModal(wx.ID_OK)
    -
    +
    def SetPouNames(self, pou_names):
    self.PouNames = [pou_name.upper() for pou_name in pou_names]
    -
    +
    def SetPouElementNames(self, element_names):
    self.PouElementNames = [element_name.upper() for element_name in element_names]
    -
    +
    def SetValues(self, values):
    for item, value in values.items():
    if item == "actionName":
    self.ActionName.SetValue(value)
    elif item == "language":
    self.Language.SetStringSelection(_(value))
    -
    +
    def GetValues(self):
    values = {}
    values["actionName"] = self.ActionName.GetValue()
    values["language"] = ACTION_LANGUAGES_DICT[self.Language.GetStringSelection()]
    return values
    -
    --- a/dialogs/PouNameDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/PouNameDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,15 +30,15 @@
    class PouNameDialog(wx.TextEntryDialog):
    - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    + def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
    wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
    -
    +
    self.PouNames = []
    -
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    +
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton())
    -
    +
    def OnOK(self, event):
    message = None
    step_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
    @@ -60,4 +60,3 @@
    def SetPouNames(self, pou_names):
    self.PouNames = [pou_name.upper() for pou_name in pou_names]
    -
    --- a/dialogs/PouTransitionDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/PouTransitionDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -37,46 +37,46 @@
    TRANSITION_LANGUAGES_DICT = dict([(_(language), language) for language in GetTransitionLanguages()])
    class PouTransitionDialog(wx.Dialog):
    -
    +
    def __init__(self, parent):
    wx.Dialog.__init__(self, parent, title=_('Create a new transition'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    +
    infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=10)
    infos_sizer.AddGrowableCol(1)
    - main_sizer.AddSizer(infos_sizer, border=20,
    + main_sizer.AddSizer(infos_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    transitionname_label = wx.StaticText(self, label=_('Transition Name:'))
    - infos_sizer.AddWindow(transitionname_label, border=4,
    + infos_sizer.AddWindow(transitionname_label, border=4,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
    self.TransitionName = wx.TextCtrl(self, size=wx.Size(180,-1))
    infos_sizer.AddWindow(self.TransitionName, flag=wx.GROW)
    language_label = wx.StaticText(self, label=_('Language:'))
    - infos_sizer.AddWindow(language_label, border=4,
    + infos_sizer.AddWindow(language_label, border=4,
    flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP)
    -
    +
    self.Language = wx.ComboBox(self, style=wx.CB_READONLY)
    infos_sizer.AddWindow(self.Language, flag=wx.GROW)
    button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton())
    main_sizer.AddSizer(button_sizer, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    for language in GetTransitionLanguages():
    self.Language.Append(_(language))
    -
    +
    self.Fit()
    self.PouNames = []
    self.PouElementNames = []
    -
    +
    def OnOK(self, event):
    error = []
    transition_name = self.TransitionName.GetValue()
    @@ -93,7 +93,7 @@
    elif i == len(error) - 1:
    text += _(" and %s")%item
    else:
    - text += _(", %s")%item
    + text += _(", %s")%item
    message = _("Form isn't complete. %s must be filled!") % text
    elif not TestIdentifier(transition_name):
    message = _("\"%s\" is not a valid identifier!") % transition_name
    @@ -109,20 +109,20 @@
    dialog.Destroy()
    else:
    self.EndModal(wx.ID_OK)
    -
    +
    def SetPouNames(self, pou_names):
    self.PouNames = [pou_name.upper() for pou_name in pou_names]
    -
    +
    def SetPouElementNames(self, pou_names):
    self.PouElementNames = [pou_name.upper() for pou_name in pou_names]
    -
    +
    def SetValues(self, values):
    for item, value in values.items():
    if item == "transitionName":
    self.TransitionName.SetValue(value)
    elif item == "language":
    self.Language.SetSelection(_(value))
    -
    +
    def GetValues(self):
    values = {}
    values["transitionName"] = self.TransitionName.GetValue()
    --- a/dialogs/ProjectDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/ProjectDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -28,29 +28,29 @@
    from controls.ProjectPropertiesPanel import ProjectPropertiesPanel
    class ProjectDialog(wx.Dialog):
    -
    +
    def __init__(self, parent, enable_required=True):
    - wx.Dialog.__init__(self, parent, title=_('Project properties'),
    + wx.Dialog.__init__(self, parent, title=_('Project properties'),
    style=wx.DEFAULT_DIALOG_STYLE)
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(0)
    -
    - self.ProjectProperties = ProjectPropertiesPanel(self,
    +
    + self.ProjectProperties = ProjectPropertiesPanel(self,
    enable_required=enable_required)
    main_sizer.AddWindow(self.ProjectProperties, flag=wx.GROW)
    -
    +
    self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    self.ButtonSizer.GetAffirmativeButton())
    - main_sizer.AddSizer(self.ButtonSizer, border=20,
    + main_sizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.SetSizer(main_sizer)
    self.ProjectProperties.Fit()
    self.Fit()
    -
    +
    def OnOK(self, event):
    values = self.ProjectProperties.GetValues()
    error = []
    @@ -69,8 +69,8 @@
    text += _(" and %s")%item
    else:
    text += ", %s"%item
    - dialog = wx.MessageDialog(self,
    - _("Form isn't complete. %s must be filled!") % text,
    + dialog = wx.MessageDialog(self,
    + _("Form isn't complete. %s must be filled!") % text,
    _("Error"), wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    dialog.Destroy()
    @@ -79,6 +79,6 @@
    def SetValues(self, values):
    self.ProjectProperties.SetValues(values)
    -
    +
    def GetValues(self):
    return self.ProjectProperties.GetValues()
    --- a/dialogs/SFCDivergenceDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/SFCDivergenceDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -40,7 +40,7 @@
    """
    class SFCDivergenceDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname, poss_div_types = None):
    """
    Constructor
    @@ -51,14 +51,14 @@
    """
    BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Create a new divergence or convergence'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 7, None, 2, 1)
    -
    +
    # Create label for divergence type
    type_label = wx.StaticText(self, label=_('Type:'))
    self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW)
    -
    +
    # Create radio buttons for selecting divergence type
    divergence_buttons = [
    (SELECTION_DIVERGENCE, _('Selection Divergence')),
    @@ -75,7 +75,7 @@
    first = True
    focusbtn = None
    for type, label in poss_div_btns:
    - radio_button = wx.RadioButton(self, label=label,
    + radio_button = wx.RadioButton(self, label=label,
    style=(wx.RB_GROUP if first else 0))
    radio_button.SetValue(first)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button)
    @@ -85,29 +85,29 @@
    first = False
    # Create label for number of divergence sequences
    - sequences_label = wx.StaticText(self,
    + sequences_label = wx.StaticText(self,
    label=_('Number of sequences:'))
    self.LeftGridSizer.AddWindow(sequences_label, flag=wx.GROW)
    -
    +
    # Create spin control for defining number of divergence sequences
    self.Sequences = wx.SpinCtrl(self, min=2, max=20, initial=2)
    self.Bind(wx.EVT_SPINCTRL, self.OnSequencesChanged, self.Sequences)
    self.LeftGridSizer.AddWindow(self.Sequences, flag=wx.GROW)
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    self.Fit()
    # Selection divergence radio button is default control having keyboard
    # focus
    self.TypeRadioButtons[focusbtn].SetFocus()
    -
    +
    def GetMinElementSize(self):
    """
    Get minimal graphic element size
    @@ -115,7 +115,7 @@
    element defined
    """
    return self.Element.GetMinSize(True)
    -
    +
    def GetDivergenceType(self):
    """
    Return type selected for SFC divergence
    @@ -127,7 +127,7 @@
    if control.GetValue():
    return type
    return None
    -
    +
    def GetValues(self):
    """
    Set default SFC divergence parameters
    @@ -151,17 +151,16 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    Override BlockPreviewDialog function
    """
    # Set graphic element displayed, creating a SFC divergence
    - self.Element = SFC_Divergence(self.Preview,
    - self.GetDivergenceType(),
    + self.Element = SFC_Divergence(self.Preview,
    + self.GetDivergenceType(),
    self.Sequences.GetValue())
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    -
    --- a/dialogs/SFCStepDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/SFCStepDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,7 +38,7 @@
    """
    class SFCStepDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname, initial=False):
    """
    Constructor
    @@ -47,25 +47,25 @@
    @param tagname: Tagname of project POU edited
    @param initial: True if step is initial (default: False)
    """
    - BlockPreviewDialog.__init__(self,parent, controller, tagname,
    + BlockPreviewDialog.__init__(self,parent, controller, tagname,
    title=_('Edit Step'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 6, None, 2, 1)
    -
    +
    # Create label for SFC step name
    name_label = wx.StaticText(self, label=_('Name:'))
    self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW)
    -
    +
    # Create text control for defining SFC step name
    self.StepName = wx.TextCtrl(self)
    self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.StepName)
    self.LeftGridSizer.AddWindow(self.StepName, flag=wx.GROW)
    -
    +
    # Create label for SFC step connectors
    connectors_label = wx.StaticText(self, label=_('Connectors:'))
    self.LeftGridSizer.AddWindow(connectors_label, flag=wx.GROW)
    -
    +
    # Create check boxes for defining connectors available on SFC step
    self.ConnectorsCheckBox = {}
    for name, label in [("input", _("Input")),
    @@ -77,27 +77,27 @@
    self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, check_box)
    self.LeftGridSizer.AddWindow(check_box, flag=wx.GROW)
    self.ConnectorsCheckBox[name] = check_box
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    +
    # Save flag that indicates that step is initial
    self.Initial = initial
    -
    +
    # Set default name for step
    self.StepName.ChangeValue(controller.GenerateNewName(
    tagname, None, "Step%d", 0))
    self.Fit()
    -
    +
    # Step name text control is default control having keyboard focus
    self.StepName.SetFocus()
    -
    +
    def SetValues(self, values):
    """
    Set default block parameters
    @@ -105,20 +105,20 @@
    """
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is step name
    if name == "name":
    self.StepName.ChangeValue(value)
    -
    +
    # Set value of other controls
    else:
    control = self.ConnectorsCheckBox.get(name, None)
    if control is not None:
    control.SetValue(value)
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    -
    +
    def GetValues(self):
    """
    Return step parameters defined in dialog
    @@ -130,7 +130,7 @@
    for name, control in self.ConnectorsCheckBox.iteritems()})
    values["width"], values["height"] = self.Element.GetSize()
    return values
    -
    +
    def OnOK(self, event):
    """
    Called when dialog OK button is pressed
    @@ -138,23 +138,23 @@
    @param event: wx.Event from OK button
    """
    message = None
    -
    +
    # Get step name typed by user
    step_name = self.StepName.GetValue()
    -
    +
    # Test that a name have been defined
    if step_name == "":
    message = _("Form isn't complete. Name must be filled!")
    -
    +
    # If an error have been identify, show error message dialog
    if message is not None:
    self.ShowErrorMessage(message)
    -
    +
    # Test step name validity
    elif self.TestElementName(step_name):
    # Call BlockPreviewDialog function
    BlockPreviewDialog.OnOK(self, event)
    -
    +
    def OnConnectorsChanged(self, event):
    """
    Called when a step connector value changed
    @@ -170,23 +170,23 @@
    """
    self.RefreshPreview()
    event.Skip()
    -
    +
    def RefreshPreview(self):
    """
    Refresh preview panel of graphic element
    Override BlockPreviewDialog function
    """
    # Set graphic element displayed, creating a SFC step element
    - self.Element = SFC_Step(self.Preview,
    - self.StepName.GetValue(),
    + self.Element = SFC_Step(self.Preview,
    + self.StepName.GetValue(),
    self.Initial)
    -
    +
    # Update connectors of SFC step element according to check boxes value
    for name, control in self.ConnectorsCheckBox.iteritems():
    if control.IsChecked():
    getattr(self.Element, "Add" + name.capitalize())()
    else:
    getattr(self.Element, "Remove" + name.capitalize())()
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    --- a/dialogs/SFCStepNameDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/SFCStepNameDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,17 +30,17 @@
    class SFCStepNameDialog(wx.TextEntryDialog):
    - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    + def __init__(self, parent, message, caption = "Please enter text", defaultValue = "",
    style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition):
    wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos)
    -
    +
    self.PouNames = []
    self.Variables = []
    self.StepNames = []
    -
    - self.Bind(wx.EVT_BUTTON, self.OnOK,
    +
    + self.Bind(wx.EVT_BUTTON, self.OnOK,
    self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton())
    -
    +
    def OnOK(self, event):
    message = None
    step_name = self.GetSizer().GetItem(1).GetWindow().GetValue()
    --- a/dialogs/SFCTransitionDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/SFCTransitionDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,7 +38,7 @@
    """
    class SFCTransitionDialog(BlockPreviewDialog):
    -
    +
    def __init__(self, parent, controller, tagname, connection=True):
    """
    Constructor
    @@ -50,32 +50,32 @@
    """
    BlockPreviewDialog.__init__(self, parent, controller, tagname,
    title=_('Edit transition'))
    -
    +
    # Init common sizers
    self._init_sizers(2, 0, 8, None, 2, 1)
    -
    +
    # Create label for transition type
    type_label = wx.StaticText(self, label=_('Type:'))
    self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW)
    -
    +
    # Create combo box for selecting reference value
    reference = wx.ComboBox(self, style=wx.CB_READONLY)
    reference.Append("")
    for transition in controller.GetEditedElementTransitions(tagname):
    reference.Append(transition)
    self.Bind(wx.EVT_COMBOBOX, self.OnReferenceChanged, reference)
    -
    +
    # Create Text control for defining inline value
    inline = wx.TextCtrl(self)
    self.Bind(wx.EVT_TEXT, self.OnInlineChanged, inline)
    -
    +
    # Create radio buttons for selecting power rail type
    self.TypeRadioButtons = {}
    first = True
    for type, label, control in [('reference', _('Reference'), reference),
    ('inline', _('Inline'), inline),
    ('connection', _('Connection'), None)]:
    - radio_button = wx.RadioButton(self, label=label,
    + radio_button = wx.RadioButton(self, label=label,
    style=(wx.RB_GROUP if first else 0))
    radio_button.SetValue(first)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button)
    @@ -85,29 +85,29 @@
    self.LeftGridSizer.AddWindow(control, flag=wx.GROW)
    self.TypeRadioButtons[type] = (radio_button, control)
    first = False
    -
    +
    # Create label for transition priority
    priority_label = wx.StaticText(self, label=_('Priority:'))
    self.LeftGridSizer.AddWindow(priority_label, flag=wx.GROW)
    -
    +
    # Create spin control for defining priority value
    self.Priority = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS)
    self.Bind(wx.EVT_TEXT, self.OnPriorityChanged, self.Priority)
    self.LeftGridSizer.AddWindow(self.Priority, flag=wx.GROW)
    -
    +
    # Add preview panel and associated label to sizers
    self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW)
    self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW)
    -
    +
    # Add buttons sizer to sizers
    - self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    + self.MainSizer.AddSizer(self.ButtonSizer, border=20,
    flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
    self.Fit()
    -
    +
    # Reference radio button is default control having keyboard focus
    self.TypeRadioButtons["reference"][0].SetFocus()
    -
    +
    def GetTransitionType(self):
    """
    Return type selected for SFC transition and associated value
    @@ -124,7 +124,7 @@
    else:
    return type, None
    return None, None
    -
    +
    def SetValues(self, values):
    """
    Set default SFC transition parameters
    @@ -132,14 +132,14 @@
    """
    # Extract transition value according to type
    type_value = values.get("value", None)
    -
    +
    # For each parameters defined, set corresponding control value
    for name, value in values.items():
    -
    +
    # Parameter is SFC transition priority
    if name == "priority":
    self.Priority.SetValue(values["priority"])
    -
    +
    # Parameter is SFC transition type
    elif name == "type":
    for type, (radio, control) in self.TypeRadioButtons.iteritems():
    @@ -152,10 +152,10 @@
    control.SetStringSelection(type_value)
    elif isinstance(control, wx.TextCtrl):
    control.ChangeValue(type_value)
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    -
    +
    def GetValues(self):
    """
    Return SFC transition parameters defined in dialog
    @@ -165,7 +165,7 @@
    values["type"], values["value"] = self.GetTransitionType()
    values["width"], values["height"] = self.Element.GetSize()
    return values
    -
    +
    def OnOK(self, event):
    """
    Called when dialog OK button is pressed
    @@ -173,18 +173,18 @@
    @param event: wx.Event from OK button
    """
    message = None
    -
    +
    # Get transition type and value associated
    type, value = self.GetTransitionType()
    -
    +
    # Test that value associated to type is defined
    if type != "connection" and value == "":
    message = _("Form isn't complete. %s must be filled!") % type
    -
    +
    # Show error message if an error is detected
    if message is not None:
    self.ShowErrorMessage(message)
    -
    +
    else:
    # Call BlockPreviewDialog function
    BlockPreviewDialog.OnOK(self, event)
    @@ -198,7 +198,7 @@
    for type, (radio, control) in self.TypeRadioButtons.iteritems():
    if control is not None:
    control.Enable(radio.GetValue())
    -
    +
    # Refresh preview panel
    self.RefreshPreview()
    event.Skip()
    @@ -236,6 +236,6 @@
    self.Element = SFC_Transition(self.Preview)
    self.Element.SetType(*self.GetTransitionType())
    self.Element.SetPriority(self.Priority.GetValue())
    -
    +
    # Call BlockPreviewDialog function
    BlockPreviewDialog.RefreshPreview(self)
    --- a/dialogs/SearchInProjectDialog.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/dialogs/SearchInProjectDialog.py Mon Aug 14 19:13:01 2017 +0300
    @@ -33,62 +33,62 @@
    def GetElementsChoices():
    _ = lambda x: x
    - return [("datatype", _("Data Type")),
    - ("function", _("Function")),
    - ("functionBlock", _("Function Block")),
    - ("program", _("Program")),
    + return [("datatype", _("Data Type")),
    + ("function", _("Function")),
    + ("functionBlock", _("Function Block")),
    + ("program", _("Program")),
    ("configuration", _("Configuration"))]
    class SearchInProjectDialog(wx.Dialog):
    -
    +
    def __init__(self, parent):
    wx.Dialog.__init__(self, parent, title=_('Search in Project'))
    -
    +
    main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    main_sizer.AddGrowableCol(0)
    main_sizer.AddGrowableRow(1)
    -
    +
    pattern_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
    pattern_sizer.AddGrowableCol(0)
    - main_sizer.AddSizer(pattern_sizer, border=20,
    + main_sizer.AddSizer(pattern_sizer, border=20,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    -
    +
    pattern_label = wx.StaticText(self, label=_('Pattern to search:'))
    pattern_sizer.AddWindow(pattern_label, flag=wx.ALIGN_BOTTOM)
    -
    +
    self.CaseSensitive = wx.CheckBox(self, label=_('Case sensitive'))
    pattern_sizer.AddWindow(self.CaseSensitive, flag=wx.GROW)
    -
    +
    self.Pattern = wx.TextCtrl(self, size=wx.Size(250,-1))
    self.Bind(wx.EVT_TEXT, self.FindPatternChanged, self.Pattern)
    pattern_sizer.AddWindow(self.Pattern, flag=wx.GROW)
    self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey)
    self.RegularExpression = wx.CheckBox(self, label=_('Regular expression'))
    pattern_sizer.AddWindow(self.RegularExpression, flag=wx.GROW)
    -
    +
    scope_staticbox = wx.StaticBox(self, label=_('Scope'))
    scope_sizer = wx.StaticBoxSizer(scope_staticbox, wx.HORIZONTAL)
    - main_sizer.AddSizer(scope_sizer, border=20,
    + main_sizer.AddSizer(scope_sizer, border=20,
    flag=wx.GROW|wx.LEFT|wx.RIGHT)
    -
    +
    scope_selection_sizer = wx.BoxSizer(wx.VERTICAL)
    - scope_sizer.AddSizer(scope_selection_sizer, 1, border=5,
    + scope_sizer.AddSizer(scope_selection_sizer, 1, border=5,
    flag=wx.GROW|wx.TOP|wx.LEFT|wx.BOTTOM)
    -
    +
    self.WholeProject = wx.RadioButton(self, label=_('Whole Project'), style=wx.RB_GROUP)
    self.WholeProject.SetValue(True)
    self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.WholeProject)
    - scope_selection_sizer.AddWindow(self.WholeProject, border=5,
    + scope_selection_sizer.AddWindow(self.WholeProject, border=5,
    flag=wx.GROW|wx.BOTTOM)
    -
    +
    self.OnlyElements = wx.RadioButton(self, label=_('Only Elements'))
    self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.OnlyElements)
    self.OnlyElements.SetValue(False)
    scope_selection_sizer.AddWindow(self.OnlyElements, flag=wx.GROW)
    -
    +
    self.ElementsList = wx.CheckListBox(self)
    self.ElementsList.Enable(False)
    - scope_sizer.AddWindow(self.ElementsList, 1, border=5,
    + scope_sizer.AddWindow(self.ElementsList, 1, border=5,
    flag=wx.GROW|wx.TOP|wx.RIGHT|wx.BOTTOM)
    buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
    @@ -103,9 +103,9 @@
    self.CloseButton = wx.Button(self, label=_("Close"))
    self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton)
    buttons_sizer.AddWindow(self.CloseButton)
    -
    +
    self.SetSizer(main_sizer)
    -
    +
    for name, label in GetElementsChoices():
    self.ElementsList.Append(_(label))
    @@ -139,7 +139,7 @@
    self.OnCloseButton(event)
    else:
    event.Skip()
    -
    +
    def OnFindButton(self, event):
    message = None
    infos = {
    @@ -163,7 +163,7 @@
    except:
    self.criteria.clear()
    message = _("Syntax error in regular expression of pattern to search!")
    -
    +
    if message is not None:
    dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR)
    dialog.ShowModal()
    --- a/editors/DebugViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/DebugViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,12 +35,12 @@
    #-------------------------------------------------------------------------------
    """
    -Class that implements common behavior of every viewers able to display debug
    +Class that implements common behavior of every viewers able to display debug
    values
    """
    class DebugViewer:
    -
    +
    def __init__(self, producer, debug, subscribe_tick=True):
    """
    Constructor
    @@ -52,49 +52,49 @@
    """
    self.Debug = debug
    self.SubscribeTick = subscribe_tick
    -
    +
    # Flag indicating that consumer value update inhibited
    # (DebugViewer is refreshing)
    self.Inhibited = False
    -
    +
    # List of data consumers subscribed to DataProducer
    self.DataConsumers = {}
    -
    +
    # Time stamp indicating when last refresh have been initiated
    self.LastRefreshTime = gettime()
    # Flag indicating that DebugViewer has acquire common debug lock
    self.HasAcquiredLock = False
    # Lock for access to the two preceding variable
    self.AccessLock = Lock()
    -
    +
    # Timer to refresh Debug Viewer one last time in the case that a new
    # value have been received during refresh was inhibited and no one
    # after refresh was activated
    self.LastRefreshTimer = None
    # Lock for access to the timer
    self.TimerAccessLock = Lock()
    -
    +
    # Set DataProducer and subscribe tick if needed
    self.SetDataProducer(producer)
    -
    +
    def __del__(self):
    """
    Destructor
    """
    # Unsubscribe all data consumers
    self.UnsubscribeAllDataConsumers()
    -
    +
    # Delete reference to DataProducer
    self.DataProducer = None
    -
    +
    # Stop last refresh timer
    if self.LastRefreshTimer is not None:
    self.LastRefreshTimer.cancel()
    -
    +
    # Release Common debug lock if DebugViewer has acquired it
    if self.HasAcquiredLock:
    DEBUG_REFRESH_LOCK.release()
    -
    +
    def SetDataProducer(self, producer):
    """
    Set Data Producer
    @@ -103,25 +103,25 @@
    # In the case that tick need to be subscribed and DebugViewer is
    # debugging
    if self.SubscribeTick and self.Debug:
    -
    +
    # Subscribe tick to new data producer
    if producer is not None:
    producer.SubscribeDebugIECVariable("__tick__", self, True)
    -
    +
    # Unsubscribe tick from old data producer
    if getattr(self, "DataProducer", None) is not None:
    self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
    -
    +
    # Save new data producer
    self.DataProducer = producer
    -
    +
    def IsDebugging(self):
    """
    Get flag indicating if Debug Viewer is debugging
    @return: Debugging flag
    """
    return self.Debug
    -
    +
    def Inhibit(self, inhibit):
    """
    Set consumer value update inhibit flag
    @@ -130,10 +130,10 @@
    # Inhibit every data consumers in list
    for consumer, iec_path in self.DataConsumers.iteritems():
    consumer.Inhibit(inhibit)
    -
    +
    # Save inhibit flag
    self.Inhibited = inhibit
    -
    +
    def AddDataConsumer(self, iec_path, consumer, buffer_list=False):
    """
    Subscribe data consumer to DataProducer
    @@ -145,19 +145,19 @@
    # Return immediately if no DataProducer defined
    if self.DataProducer is None:
    return None
    -
    +
    # Subscribe data consumer to DataProducer
    result = self.DataProducer.SubscribeDebugIECVariable(
    iec_path, consumer, buffer_list)
    if result is not None and consumer != self:
    -
    +
    # Store data consumer if successfully subscribed and inform
    # consumer of variable data type
    self.DataConsumers[consumer] = iec_path
    consumer.SetDataType(self.GetDataType(iec_path))
    -
    +
    return result
    -
    +
    def RemoveDataConsumer(self, consumer):
    """
    Unsubscribe data consumer from DataProducer
    @@ -165,12 +165,12 @@
    """
    # Remove consumer from data consumer list
    iec_path = self.DataConsumers.pop(consumer, None)
    -
    +
    # Unsubscribe consumer from DataProducer
    if iec_path is not None:
    self.DataProducer.UnsubscribeDebugIECVariable(
    iec_path, consumer)
    -
    +
    def SubscribeAllDataConsumers(self):
    """
    Called to Subscribe all data consumers contained in DebugViewer.
    @@ -179,24 +179,24 @@
    # Subscribe tick if needed
    if self.SubscribeTick and self.Debug and self.DataProducer is not None:
    self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True)
    -
    +
    def UnsubscribeAllDataConsumers(self, tick=True):
    """
    Called to Unsubscribe all data consumers.
    """
    if self.DataProducer is not None:
    -
    +
    # Unscribe tick if needed
    if self.SubscribeTick and tick and self.Debug:
    self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
    -
    +
    # Unsubscribe all data consumers in list
    for consumer, iec_path in self.DataConsumers.iteritems():
    self.DataProducer.UnsubscribeDebugIECVariable(
    iec_path, consumer)
    -
    +
    self.DataConsumers = {}
    -
    +
    def GetDataType(self, iec_path):
    """
    Return variable data type.
    @@ -204,20 +204,20 @@
    @return: variable data type (None if not found)
    """
    if self.DataProducer is not None:
    -
    +
    # Search for variable informations in project compilation files
    data_type = self.DataProducer.GetDebugIECVariableType(
    iec_path.upper())
    if data_type is not None:
    return data_type
    -
    +
    # Search for variable informations in project data
    infos = self.DataProducer.GetInstanceInfos(iec_path)
    if infos is not None:
    return infos.type
    -
    +
    return None
    -
    +
    def IsNumType(self, data_type):
    """
    Indicate if data type given is a numeric data type
    @@ -226,9 +226,9 @@
    """
    if self.DataProducer is not None:
    return self.DataProducer.IsNumType(data_type)
    -
    +
    return False
    -
    +
    def ForceDataValue(self, iec_path, value):
    """
    Force PLC variable value
    @@ -237,7 +237,7 @@
    """
    if self.DataProducer is not None:
    self.DataProducer.ForceDebugIECVariable(iec_path, value)
    -
    +
    def ReleaseDataValue(self, iec_path):
    """
    Release PLC variable value
    @@ -245,12 +245,12 @@
    """
    if self.DataProducer is not None:
    self.DataProducer.ReleaseDebugIECVariable(iec_path)
    -
    +
    def NewDataAvailable(self, ticks):
    """
    Called by DataProducer for each tick captured
    @param tick: PLC tick captured
    - All other parameters are passed to refresh function
    + All other parameters are passed to refresh function
    """
    # Stop last refresh timer
    self.TimerAccessLock.acquire()
    @@ -258,27 +258,27 @@
    self.LastRefreshTimer.cancel()
    self.LastRefreshTimer=None
    self.TimerAccessLock.release()
    -
    +
    # Only try to refresh DebugViewer if it is visible on screen and not
    # already refreshing
    if self.IsShown() and not self.Inhibited:
    -
    +
    # Try to get acquire common refresh lock if minimum period between
    # two refresh has expired
    if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \
    DEBUG_REFRESH_LOCK.acquire(False):
    self.StartRefreshing()
    -
    +
    # If common lock wasn't acquired for any reason, restart last
    # refresh timer
    else:
    self.StartLastRefreshTimer()
    -
    +
    # In the case that DebugViewer isn't visible on screen and has already
    # acquired common refresh lock, reset DebugViewer
    elif not self.IsShown() and self.HasAcquiredLock:
    DebugViewer.RefreshNewData(self)
    -
    +
    def ShouldRefresh(self):
    """
    Callback function called when last refresh timer expired
    @@ -286,15 +286,15 @@
    """
    # Cancel if DebugViewer is not visible on screen
    if self and self.IsShown():
    -
    +
    # Try to acquire common refresh lock
    if DEBUG_REFRESH_LOCK.acquire(False):
    self.StartRefreshing()
    -
    +
    # Restart last refresh timer if common refresh lock acquired failed
    else:
    self.StartLastRefreshTimer()
    -
    +
    def StartRefreshing(self):
    """
    Called to initiate a refresh of DebugViewer
    @@ -306,13 +306,13 @@
    self.HasAcquiredLock = True
    self.LastRefreshTime = gettime()
    self.AccessLock.release()
    -
    +
    # Inhibit data consumer value update
    self.Inhibit(True)
    -
    +
    # Initiate DebugViewer refresh
    wx.CallAfter(self.RefreshNewData)
    -
    +
    def StartLastRefreshTimer(self):
    """
    Called to start last refresh timer for the minimum time between 2
    @@ -324,19 +324,19 @@
    REFRESH_PERIOD, self.ShouldRefresh)
    self.LastRefreshTimer.start()
    self.TimerAccessLock.release()
    -
    +
    def RefreshNewData(self):
    """
    Called to refresh DebugViewer according to values received by data
    consumers
    May be overridden by inherited classes
    Can receive any parameters depending on what is needed by inherited
    - class
    + class
    """
    if self:
    # Activate data consumer value update
    self.Inhibit(False)
    -
    +
    # Release common refresh lock if acquired and update
    # last refresh time
    self.AccessLock.acquire()
    --- a/editors/FileManagementPanel.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/FileManagementPanel.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,37 +35,37 @@
    FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv")
    class FileManagementPanel(EditorPanel):
    -
    +
    def _init_Editor(self, parent):
    self.Editor = wx.Panel(parent)
    -
    +
    main_sizer = wx.BoxSizer(wx.HORIZONTAL)
    -
    +
    left_sizer = wx.BoxSizer(wx.VERTICAL)
    main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
    -
    +
    managed_dir_label = wx.StaticText(self.Editor, label=_(self.TagName) + ":")
    left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
    -
    +
    self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER)
    left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW)
    -
    +
    managed_treectrl = self.ManagedDir.GetTreeCtrl()
    self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl)
    if self.EnableDragNDrop:
    self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl)
    -
    +
    button_sizer = wx.BoxSizer(wx.VERTICAL)
    - main_sizer.AddSizer(button_sizer, border=5,
    + main_sizer.AddSizer(button_sizer, border=5,
    flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL)
    -
    +
    for idx, (name, bitmap, help) in enumerate([
    ("DeleteButton", "remove_element", _("Remove file from left folder")),
    ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")),
    ("RightCopyButton", "RightCopy", _("Copy file from left folder to right")),
    ("EditButton", "edit", _("Edit file"))]):
    - button = wx.lib.buttons.GenBitmapButton(self.Editor,
    - bitmap=GetBitmap(bitmap),
    + 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)
    @@ -75,62 +75,62 @@
    flag = 0
    self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
    button_sizer.AddWindow(button, border=20, flag=flag)
    -
    +
    right_sizer = wx.BoxSizer(wx.VERTICAL)
    main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL)
    -
    +
    if wx.Platform == '__WXMSW__':
    system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:"))
    else:
    system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:"))
    right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM)
    -
    +
    self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False)
    right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW)
    -
    +
    system_treectrl = self.SystemDir.GetTreeCtrl()
    self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl)
    -
    +
    self.Editor.SetSizer(main_sizer)
    -
    +
    def __init__(self, parent, controler, name, folder, enable_dragndrop=False):
    self.Folder = os.path.realpath(folder)
    self.EnableDragNDrop = enable_dragndrop
    -
    +
    if wx.Platform == '__WXMSW__':
    self.HomeDirectory = "/"
    else:
    self.HomeDirectory = os.path.expanduser("~")
    -
    +
    EditorPanel.__init__(self, parent, name, None, None)
    -
    +
    self.Controler = controler
    -
    +
    self.EditableFileExtensions = []
    self.EditButton.Hide()
    -
    +
    self.SetIcon(GetBitmap("FOLDER"))
    -
    +
    def __del__(self):
    self.Controler.OnCloseEditor(self)
    -
    +
    def GetTitle(self):
    return _(self.TagName)
    -
    +
    def SetEditableFileExtensions(self, extensions):
    self.EditableFileExtensions = extensions
    if len(self.EditableFileExtensions) > 0:
    self.EditButton.Show()
    -
    +
    def RefreshView(self):
    self.ManagedDir.RefreshTree()
    self.SystemDir.RefreshTree()
    self.RefreshButtonsState()
    -
    +
    def RefreshButtonsState(self):
    managed_filepath = self.ManagedDir.GetPath()
    system_filepath = self.SystemDir.GetPath()
    -
    +
    self.DeleteButton.Enable(os.path.isfile(managed_filepath))
    self.LeftCopyButton.Enable(os.path.isfile(system_filepath))
    self.RightCopyButton.Enable(os.path.isfile(managed_filepath))
    @@ -138,22 +138,22 @@
    self.EditButton.Enable(
    os.path.isfile(managed_filepath) and
    os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions)
    -
    +
    def OnTreeItemChanged(self, event):
    self.RefreshButtonsState()
    event.Skip()
    -
    +
    def OnDeleteButton(self, event):
    filepath = self.ManagedDir.GetPath()
    if os.path.isfile(filepath):
    folder, filename = os.path.split(filepath)
    -
    - dialog = wx.MessageDialog(self,
    - _("Do you really want to delete the file '%s'?") % filename,
    +
    + dialog = wx.MessageDialog(self,
    + _("Do you really want to delete the file '%s'?") % filename,
    _("Delete File"), wx.YES_NO|wx.ICON_QUESTION)
    remove = dialog.ShowModal() == wx.ID_YES
    dialog.Destroy()
    -
    +
    if remove:
    os.remove(filepath)
    self.ManagedDir.RefreshTree()
    @@ -161,11 +161,11 @@
    def OnEditButton(self, event):
    filepath = self.ManagedDir.GetPath()
    - if (os.path.isfile(filepath) and
    + if (os.path.isfile(filepath) and
    os.path.splitext(filepath)[1] in self.EditableFileExtensions):
    self.Controler._OpenView(filepath + "::")
    event.Skip()
    -
    +
    def CopyFile(self, src, dst):
    if os.path.isfile(src):
    src_folder, src_filename = os.path.split(src)
    @@ -173,17 +173,17 @@
    dst_folder, dst_filename = os.path.split(dst)
    else:
    dst_folder = dst
    -
    +
    dst_filepath = os.path.join(dst_folder, src_filename)
    if os.path.isfile(dst_filepath):
    - dialog = wx.MessageDialog(self,
    - _("The file '%s' already exist.\nDo you want to replace it?") % src_filename,
    + dialog = wx.MessageDialog(self,
    + _("The file '%s' already exist.\nDo you want to replace it?") % src_filename,
    _("Replace File"), wx.YES_NO|wx.ICON_QUESTION)
    copy = dialog.ShowModal() == wx.ID_YES
    dialog.Destroy()
    else:
    copy = True
    -
    +
    if copy:
    shutil.copyfile(src, dst_filepath)
    return dst_filepath
    @@ -202,7 +202,7 @@
    self.SystemDir.RefreshTree()
    self.SystemDir.SetPath(filepath)
    event.Skip()
    -
    +
    def OnTreeBeginDrag(self, event):
    filepath = self.ManagedDir.GetPath()
    if os.path.isfile(filepath):
    @@ -211,4 +211,3 @@
    dragSource = wx.DropSource(self)
    dragSource.SetData(data)
    dragSource.DoDragDrop()
    -
    --- a/editors/LDViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/LDViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -49,7 +49,7 @@
    block_list.append(next)
    next_list.append(next)
    current_list = next_list
    -
    +
    def CalcBranchSize(elements, stops):
    branch_size = 0
    stop_list = stops
    @@ -194,7 +194,7 @@
    self.RungComments.insert(i, None)
    else:
    self.RungComments.insert(i, None)
    -
    +
    def loadInstance(self, instance, ids, selection):
    Viewer.loadInstance(self, instance, ids, selection)
    if self.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -230,7 +230,7 @@
    if len(rungs) > 1:
    raise ValueError, _("Ladder element with id %d is on more than one rung.")%instance["id"]
    element = self.FindElementById(instance["id"])
    - element_connectors = element.GetConnectors()
    + element_connectors = element.GetConnectors()
    self.Rungs[rungs[0]].SelectElement(element)
    for wire, num in element_connectors["inputs"][0].GetWires():
    self.Rungs[rungs[0]].SelectElement(wire)
    @@ -240,7 +240,7 @@
    pos = element.GetPosition()
    i = 0
    inserted = False
    - while i < len(self.RungComments) and not inserted:
    + while i < len(self.RungComments) and not inserted:
    ipos = self.RungComments[i].GetPosition()
    if pos[1] < ipos[1]:
    self.RungComments.insert(i, element)
    @@ -248,7 +248,7 @@
    i += 1
    if not inserted:
    self.RungComments.append(element)
    -
    +
    #-------------------------------------------------------------------------------
    # Search Element functions
    #-------------------------------------------------------------------------------
    @@ -262,7 +262,7 @@
    def FindElement(self, event, exclude_group = False, connectors = True):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    return Viewer.FindElement(self, event, exclude_group, connectors)
    -
    +
    dc = self.GetLogicalDC()
    pos = event.GetLogicalPosition(dc)
    if self.SelectedElement and not isinstance(self.SelectedElement, (Graphic_Group, Wire)):
    @@ -286,7 +286,7 @@
    def SearchElements(self, bbox):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    return Viewer.SearchElements(self, bbox)
    -
    +
    elements = []
    for element in self.Blocks.values() + self.Comments.values():
    if element.IsInSelection(bbox):
    @@ -345,7 +345,7 @@
    self.SelectedElement.SetElements(elements)
    self.SelectedElement.SetSelected(True)
    elif self.Mode == MODE_SELECTION and self.SelectedElement:
    - dc = self.GetLogicalDC()
    + dc = self.GetLogicalDC()
    if not isinstance(self.SelectedElement, Graphic_Group):
    if self.IsWire(self.SelectedElement):
    result = self.SelectedElement.TestSegment(event.GetLogicalPosition(dc), True)
    @@ -500,7 +500,7 @@
    starty = bbox.y + bbox.height
    starty += LD_OFFSET[1]
    rung = Graphic_Group(self)
    -
    +
    # Create comment
    id = self.GetNewId()
    comment = Comment(self, _("Comment"), id)
    @@ -511,7 +511,7 @@
    self.Controler.AddEditedElementComment(self.TagName, id)
    self.RefreshCommentModel(comment)
    starty += LD_COMMENT_DEFAULTSIZE[1] + LD_OFFSET[1]
    -
    +
    # Create LeftPowerRail
    id = self.GetNewId()
    leftpowerrail = LD_PowerRail(self, LEFTRAIL, id)
    @@ -521,7 +521,7 @@
    rung.SelectElement(leftpowerrail)
    self.Controler.AddEditedElementPowerRail(self.TagName, id, LEFTRAIL)
    self.RefreshPowerRailModel(leftpowerrail)
    -
    +
    # Create Coil
    id = self.GetNewId()
    coil = LD_Coil(self, values["type"], values["name"], id)
    @@ -530,7 +530,7 @@
    self.AddBlock(coil)
    rung.SelectElement(coil)
    self.Controler.AddEditedElementCoil(self.TagName, id)
    -
    +
    # Create Wire between LeftPowerRail and Coil
    wire = Wire(self)
    start_connector = coil_connectors["inputs"][0]
    @@ -541,7 +541,7 @@
    wire.ConnectEndPoint(None, end_connector)
    self.AddWire(wire)
    rung.SelectElement(wire)
    -
    +
    # Create RightPowerRail
    id = self.GetNewId()
    rightpowerrail = LD_PowerRail(self, RIGHTRAIL, id)
    @@ -550,7 +550,7 @@
    self.AddBlock(rightpowerrail)
    rung.SelectElement(rightpowerrail)
    self.Controler.AddEditedElementPowerRail(self.TagName, id, RIGHTRAIL)
    -
    +
    # Create Wire between LeftPowerRail and Coil
    wire = Wire(self)
    start_connector = rightpowerrail_connectors["inputs"][0]
    @@ -778,7 +778,7 @@
    if left_powerrail:
    powerrail = left_elements[0].GetParentBlock()
    index = 0
    - for left_element in left_elements:
    + for left_element in left_elements:
    index = max(index, powerrail.GetConnectorIndex(left_element))
    powerrail.InsertConnector(index + 1)
    powerrail.RefreshModel()
    @@ -810,12 +810,12 @@
    values = dialog.GetValues()
    powerrail = right_elements[0].GetParentBlock()
    index = 0
    - for right_element in right_elements:
    + for right_element in right_elements:
    index = max(index, powerrail.GetConnectorIndex(right_element))
    powerrail.InsertConnector(index + 1)
    powerrail.RefreshModel()
    connectors = powerrail.GetConnectors()
    -
    +
    # Create Coil
    id = self.GetNewId()
    coil = LD_Coil(self, values["type"], values["name"], id)
    @@ -825,7 +825,7 @@
    rung.SelectElement(coil)
    self.Controler.AddEditedElementCoil(self.TagName, id)
    coil_connectors = coil.GetConnectors()
    -
    +
    # Create Wire between LeftPowerRail and Coil
    wire = Wire(self)
    connectors["inputs"][index + 1].Connect((wire, 0), False)
    @@ -835,7 +835,7 @@
    self.AddWire(wire)
    rung.SelectElement(wire)
    left_elements.reverse()
    -
    +
    for i, left_element in enumerate(left_elements):
    # Create Wire between LeftPowerRail and Coil
    new_wire = Wire(self)
    @@ -844,7 +844,7 @@
    left_element.InsertConnect(left_index[i] + 1, (new_wire, -1), False)
    new_wire.ConnectStartPoint(None, coil_connectors["inputs"][0])
    new_wire.ConnectEndPoint(None, left_element)
    -
    +
    self.RefreshPosition(coil)
    else:
    left_elements.reverse()
    @@ -1089,7 +1089,7 @@
    if isinstance(element, LD_PowerRail) and element.GetType() == LEFTRAIL:
    element.RefreshModel()
    return
    -
    +
    # Extract max position of the elements connected to input
    connectors = element.GetConnectors()
    position = element.GetPosition()
    @@ -1103,7 +1103,7 @@
    pos = leftblock.GetPosition()
    size = leftblock.GetSize()
    maxx = max(maxx, pos[0] + size[0])
    -
    +
    # Refresh position of element
    if isinstance(element, LD_Coil):
    interval = LD_WIRECOIL_SIZE
    @@ -1114,13 +1114,13 @@
    movex = maxx + interval - position[0]
    element.Move(movex, 0)
    position = element.GetPosition()
    -
    +
    # Extract blocks connected to inputs
    blocks = []
    for i, connector in enumerate(connectors["inputs"]):
    for j, (wire, handle) in enumerate(connector.GetWires()):
    blocks.append(wire.EndConnected.GetParentBlock())
    -
    +
    for i, connector in enumerate(connectors["inputs"]):
    startpoint = connector.GetPosition(False)
    previous_blocks = []
    @@ -1168,13 +1168,13 @@
    previous_blocks.append(block)
    blocks.remove(block)
    ExtractNextBlocks(block, block_list)
    -
    +
    element.RefreshModel(False)
    if recursive:
    for connector in connectors["outputs"]:
    for wire, handle in connector.GetWires():
    self.RefreshPosition(wire.StartConnected.GetParentBlock())
    -
    +
    def RefreshRungs(self, movey, fromidx):
    if movey != 0:
    for i in xrange(fromidx, len(self.Rungs)):
    @@ -1192,4 +1192,3 @@
    def EditPowerRailContent(self, powerrail):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.EditPowerRailContent(self, powerrail)
    -
    --- a/editors/SFCViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/SFCViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,7 +35,7 @@
    class SFC_Viewer(Viewer):
    -
    +
    SFC_StandardRules = {
    # The key of this dict is a block that user try to connect,
    # and the value is a list of blocks, that can be connected with the current block
    @@ -85,7 +85,7 @@
    def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""):
    Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath)
    self.CurrentLanguage = "SFC"
    -
    +
    def ConnectConnectors(self, start, end):
    startpoint = [start.GetPosition(False), start.GetDirection()]
    endpoint = [end.GetPosition(False), end.GetDirection()]
    @@ -96,7 +96,7 @@
    wire.ConnectStartPoint(None, start)
    wire.ConnectEndPoint(None, end)
    return wire
    -
    +
    def CreateTransition(self, connector, next = None):
    previous = connector.GetParentBlock()
    id = self.GetNewId()
    @@ -124,7 +124,7 @@
    next_block.RefreshPosition()
    transition.RefreshOutputModel(True)
    return transition
    -
    +
    def RemoveTransition(self, transition):
    connectors = transition.GetConnectors()
    input_wires = connectors["input"].GetWires()
    @@ -146,7 +146,7 @@
    self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId())
    wire = self.ConnectConnectors(next, previous)
    return wire
    -
    +
    def CreateStep(self, name, connector, next = None):
    previous = connector.GetParentBlock()
    id = self.GetNewId()
    @@ -289,7 +289,7 @@
    self.SelectedElement.RefreshModel()
    self.SelectedElement.SetSelected(True)
    event.Skip()
    -
    +
    def OnViewerRightUp(self, event):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.OnViewerRightUp(self, event)
    @@ -307,7 +307,7 @@
    self.SelectedElement.Refresh()
    wx.CallAfter(self.SetCurrentCursor, 0)
    event.Skip()
    -
    +
    def OnViewerLeftDClick(self, event):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.OnViewerLeftDClick(self, event)
    @@ -315,7 +315,7 @@
    self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling)
    self.Refresh(False)
    event.Skip()
    -
    +
    def OnViewerMotion(self, event):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.OnViewerMotion(self, event)
    @@ -424,7 +424,7 @@
    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, scaling[1])), False)
    else:
    event.Skip()
    -
    +
    #-------------------------------------------------------------------------------
    # Adding element functions
    #-------------------------------------------------------------------------------
    @@ -503,7 +503,7 @@
    self.RefreshScrollBars()
    self.Refresh(False)
    dialog.Destroy()
    -
    +
    def AddStepAction(self):
    if isinstance(self.SelectedElement, SFC_Step):
    connectors = self.SelectedElement.GetConnectors()
    @@ -532,9 +532,9 @@
    self.RefreshScrollBars()
    self.Refresh(False)
    dialog.Destroy()
    -
    +
    def AddDivergence(self):
    - if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step):
    + if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step):
    dialog = SFCDivergenceDialog(self.ParentWindow)
    dialog.SetPreviewFont(self.GetFont())
    if dialog.ShowModal() == wx.ID_OK:
    @@ -729,7 +729,7 @@
    self.RefreshScrollBars()
    self.Refresh(False)
    dialog.Destroy()
    -
    +
    def AddDivergenceBranch(self, divergence):
    if isinstance(divergence, SFC_Divergence):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    @@ -750,7 +750,7 @@
    self.RefreshBuffer()
    self.RefreshScrollBars()
    self.Refresh(False)
    -
    +
    def RemoveDivergenceBranch(self, divergence):
    if isinstance(divergence, SFC_Divergence):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    @@ -759,15 +759,15 @@
    self.RefreshBuffer()
    self.RefreshScrollBars()
    self.Refresh(False)
    -
    +
    def AddJump(self):
    if isinstance(self.SelectedElement, SFC_Step) and not self.SelectedElement.Output:
    choices = []
    for block in self.Blocks:
    if isinstance(block, SFC_Step):
    choices.append(block.GetName())
    - dialog = wx.SingleChoiceDialog(self.ParentWindow,
    - _("Add a new jump"), _("Please choose a target"),
    + dialog = wx.SingleChoiceDialog(self.ParentWindow,
    + _("Add a new jump"), _("Please choose a target"),
    choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
    if dialog.ShowModal() == wx.ID_OK:
    value = dialog.GetStringSelection()
    @@ -884,7 +884,7 @@
    self.DeleteDivergence(previous_block)
    else:
    self.RefreshDivergenceModel(previous_block)
    -
    +
    def DeleteTransition(self, transition):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.DeleteTransition(self, transition)
    @@ -919,7 +919,7 @@
    self.DeleteDivergence(next_block)
    else:
    self.RefreshDivergenceModel(next_block)
    -
    +
    def DeleteDivergence(self, divergence):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.DeleteDivergence(self, divergence)
    @@ -979,7 +979,7 @@
    next_pos = next.GetPosition(False)
    wire_size = GetWireSize(previous_block)
    previous_block.RefreshOutputPosition((0, previous_pos.y + wire_size - next_pos.y))
    - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size),
    + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size),
    wx.Point(previous_pos.x, previous_pos.y)])
    if isinstance(next_block, SFC_Divergence):
    next_block.RefreshPosition()
    @@ -1009,12 +1009,12 @@
    next_pos = next.GetPosition(False)
    wire_size = GetWireSize(previous_block)
    previous_block.RefreshOutputPosition((previous_pos.x - next_pos.x, previous_pos.y + wire_size - next_pos.y))
    - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size),
    + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size),
    wx.Point(previous_pos.x, previous_pos.y)])
    if isinstance(next_block, SFC_Divergence):
    next_block.RefreshPosition()
    previous_block.RefreshOutputModel(True)
    -
    +
    def DeleteJump(self, jump):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.DeleteJump(self, jump)
    @@ -1049,7 +1049,7 @@
    self.DeleteDivergence(previous_block)
    else:
    previous_block.RefreshModel()
    -
    +
    def DeleteActionBlock(self, actionblock):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.DeleteActionBlock(self, actionblock)
    @@ -1069,7 +1069,7 @@
    self.RefreshStepModel(step)
    step.RefreshOutputPosition()
    step.RefreshOutputModel(True)
    -
    +
    def DeleteWire(self, wire):
    if self.GetDrawingMode() == FREEDRAWING_MODE:
    Viewer.DeleteWire(self, wire)
    @@ -1087,4 +1087,3 @@
    infos["width"], infos["height"] = block.GetSize()
    infos["connectors"] = block.GetConnectors()
    self.Controler.SetEditedElementBlockInfos(self.TagName, blockid, infos)
    -
    --- a/editors/TextViewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/TextViewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -263,7 +263,7 @@
    if dialog.ShowModal() == wx.ID_OK:
    blockname = dialog.GetValue()
    else:
    - event.SetDragText("")
    + event.SetDragText("")
    return
    dialog.Destroy()
    if blockname.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
    @@ -632,7 +632,7 @@
    if len(self.CallStack) > 0:
    current_call = self.CallStack.pop()
    else:
    - current_call = None
    + current_call = None
    elif state == PRAGMA:
    if line.endswith("}"):
    self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY)
    @@ -961,4 +961,3 @@
    self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
    self.StartStyling(highlight_start_pos, 0x00)
    self.SetStyling(len(self.Editor.GetText()) - highlight_end_pos, wx.stc.STC_STYLE_DEFAULT)
    -
    --- a/editors/Viewer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/editors/Viewer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -429,7 +429,7 @@
    class DebugInstanceName(DebugDataConsumer):
    VALUE_TRANSLATION = {True: _("Active"), False: _("Inactive")}
    -
    +
    def __init__(self, parent):
    DebugDataConsumer.__init__(self)
    self.Parent = parent
    @@ -437,7 +437,7 @@
    self.ActionState = None
    self.x_offset = 2
    self.y_offset = 2
    -
    +
    def SetValue(self, value):
    self.ActionState = value
    if self.ActionState != self.ActionLastState:
    @@ -448,7 +448,7 @@
    return _("Debug: %s") % self.Parent.InstancePath
    def GetRedrawRect(self):
    - x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset)
    + x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset)
    dc = self.Parent.GetLogicalDC()
    ipw, iph = dc.GetTextExtent(self.GetInstanceName())
    vw, vh = 0, 0
    @@ -462,13 +462,13 @@
    scalex, scaley = dc.GetUserScale()
    dc.SetUserScale(1, 1)
    x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset)
    -
    +
    text = self.GetInstanceName()
    if self.ActionState is not None:
    text += " ("
    dc.DrawText(text, x, y)
    - tw, th = dc.GetTextExtent(text)
    + tw, th = dc.GetTextExtent(text)
    if self.ActionState is not None:
    text = self.VALUE_TRANSLATION[self.ActionState]
    @@ -478,8 +478,8 @@
    if self.ActionState:
    dc.SetTextForeground(wx.BLACK)
    tw = tw + dc.GetTextExtent(text)[0]
    -
    - text = ")"
    +
    + text = ")"
    dc.DrawText(text, x + tw, y)
    dc.SetUserScale(scalex, scaley)
    @@ -955,7 +955,7 @@
    block.GetName() == name:
    blocks.append(block)
    return blocks
    -
    +
    def GetConnectorByName(self, name):
    for block in self.Blocks.itervalues():
    if isinstance(block, FBD_Connector) and\
    @@ -1222,7 +1222,7 @@
    if self.TagName.split("::")[0] == "A" and self.Debug:
    self.AddDataConsumer("%s.Q" % self.InstancePath.upper(), self.InstanceName)
    -
    +
    for wire in self.Wires:
    if not wire.IsConnectedCompatible():
    wire.SetValid(False)
    @@ -1526,7 +1526,7 @@
    return result
    def FindBlockConnectorWithError(self, pos, direction = None, exclude = None):
    - error = False
    + error = False
    startblock = None
    for block in self.Blocks.itervalues():
    connector = block.TestConnector(pos, direction, exclude)
    @@ -1539,7 +1539,7 @@
    error = True
    return connector, error
    return None, error
    -
    +
    def FindElementById(self, id):
    block = self.Blocks.get(id, None)
    if block is not None:
    @@ -2585,7 +2585,7 @@
    break
    self.AddNewElement(block, bbox, wire, connector)
    self.RefreshVariablePanel()
    - self.ParentWindow.RefreshPouInstanceVariablesPanel()
    + self.ParentWindow.RefreshPouInstanceVariablesPanel()
    dialog.Destroy()
    def AddNewVariable(self, bbox, exclude_input=False, wire=None):
    @@ -2966,7 +2966,7 @@
    rect = rect.Union(block.GetRedrawRect())
    block.Refresh(rect)
    step.SetName(new_name)
    -
    +
    if values["input"]:
    step.AddInput()
    else:
    @@ -3711,5 +3711,3 @@
    if self.Debug:
    DebugViewer.RefreshNewData(self)
    event.Skip()
    -
    -
    --- a/graphics/DebugDataConsumer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/DebugDataConsumer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -46,7 +46,7 @@
    return float(value.days * DAY + \
    value.seconds * SECOND + \
    value.microseconds)
    - return
    + return
    def generate_time(value):
    """
    @@ -56,38 +56,38 @@
    @return: IEC 61131 TIME literal
    """
    microseconds = get_microseconds(value)
    -
    +
    # Get absolute microseconds value and save if it was negative
    negative = microseconds < 0
    microseconds = abs(microseconds)
    -
    +
    # TIME literal prefix
    data = "T#"
    if negative:
    data += "-"
    -
    +
    # In TIME literal format, it isn't mandatory to indicate null values
    # if no greater non-null values are available. This variable is used to
    # inhibit formatting until a non-null value is found
    not_null = False
    -
    +
    for val, format in [
    (int(microseconds) / DAY, "%dd"), # Days
    ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours
    ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes
    ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds
    -
    - # Add value to TIME literal if value is non-null or another non-null
    +
    + # Add value to TIME literal if value is non-null or another non-null
    # value have already be found
    if val > 0 or not_null:
    data += format % val
    -
    +
    # Update non-null variable
    not_null = True
    -
    - # In any case microseconds have to be added to TIME literal
    +
    + # In any case microseconds have to be added to TIME literal
    data += "%gms" % (microseconds % SECOND / 1000.)
    -
    +
    return data
    def generate_date(value):
    @@ -116,22 +116,22 @@
    @return: IEC 61131 TIME_OF_DAY literal
    """
    microseconds = get_microseconds(value)
    -
    +
    # TIME_OF_DAY literal prefix
    data = "TOD#"
    -
    +
    for val, format in [
    (int(microseconds) / HOUR, "%2.2d:"), # Hours
    ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes
    ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds
    (microseconds % SECOND, "%6.6d")]: # Microseconds
    -
    +
    # Add value to TIME_OF_DAY literal
    data += format % val
    -
    +
    return data
    -# Dictionary of translation functions from value send by debugger to IEC
    +# Dictionary of translation functions from value send by debugger to IEC
    # literal stored by type
    TYPE_TRANSLATOR = {
    "TIME": generate_time,
    @@ -154,7 +154,7 @@
    """
    class DebugDataConsumer:
    -
    +
    def __init__(self):
    """
    Constructor
    @@ -162,17 +162,17 @@
    # Debug value and forced flag
    self.Value = None
    self.Forced = False
    -
    +
    # Store debug value and forced flag when value update is inhibited
    self.LastValue = None
    self.LastForced = False
    -
    +
    # Value IEC data type
    self.DataType = None
    -
    +
    # Flag that value update is inhibited
    self.Inhibited = False
    -
    +
    def Inhibit(self, inhibit):
    """
    Set flag to inhibit or activate value update
    @@ -180,23 +180,23 @@
    """
    # Save inhibit flag
    self.Inhibited = inhibit
    -
    +
    # When reactivated update value and forced flag with stored values
    if not inhibit and self.LastValue is not None:
    self.SetForced(self.LastForced)
    self.SetValue(self.LastValue)
    -
    +
    # Reset stored values
    self.LastValue = None
    self.LastForced = False
    -
    +
    def SetDataType(self, data_type):
    """
    Set value IEC data type
    @param data_type: Value IEC data type
    """
    self.DataType = data_type
    -
    +
    def NewValues(self, tick, values, raw="BOOL"):
    """
    Function called by debug thread when a new debug value is available
    @@ -206,21 +206,21 @@
    @param raw: Data type of values not translated (default: 'BOOL')
    """
    value, forced = values
    -
    +
    # Translate value to IEC literal
    if self.DataType != raw:
    value = TYPE_TRANSLATOR.get(self.DataType, str)(value)
    -
    +
    # Store value and forced flag when value update is inhibited
    if self.Inhibited:
    self.LastValue = value
    self.LastForced = forced
    -
    +
    # Update value and forced flag in any other case
    else:
    self.SetForced(forced)
    self.SetValue(value)
    -
    +
    def SetValue(self, value):
    """
    Update value.
    @@ -228,14 +228,14 @@
    @param value: New value
    """
    self.Value = value
    -
    +
    def GetValue(self):
    """
    Return current value
    @return: Current value
    """
    return self.Value
    -
    +
    def SetForced(self, forced):
    """
    Update Forced flag.
    @@ -243,7 +243,7 @@
    @param forced: New forced flag
    """
    self.Forced = forced
    -
    +
    def IsForced(self):
    """
    Indicate if current value is forced
    --- a/graphics/FBD_Objects.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/FBD_Objects.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,7 +39,7 @@
    return name in ["OUT", "MN", "MX"] or name.startswith("IN") and (block_type, name) != ("EXPT", "IN2")
    class FBD_Block(Graphic_Element):
    -
    +
    # Create a new block
    def __init__(self, parent, type, name, id = None, extension = 0, inputs = None, connectors = {}, executionControl = False, executionOrder = 0):
    Graphic_Element.__init__(self, parent)
    @@ -56,7 +56,7 @@
    self.Pen = MiterPen(wx.BLACK)
    self.SetType(type, extension, inputs, connectors, executionControl)
    self.Highlights = {}
    -
    +
    # Make a clone of this FBD_Block
    def Clone(self, parent, id = None, name = "", pos = None):
    if self.Name != "" and name == "":
    @@ -70,10 +70,10 @@
    block.Inputs = [input.Clone(block) for input in self.Inputs]
    block.Outputs = [output.Clone(block) for output in self.Outputs]
    return block
    -
    +
    def GetConnectorTranslation(self, element):
    return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))
    -
    +
    def Flush(self):
    for input in self.Inputs:
    input.Flush()
    @@ -81,7 +81,7 @@
    for output in self.Outputs:
    output.Flush()
    self.Outputs = []
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -93,26 +93,26 @@
    if output.IsConnected():
    rect = rect.Union(output.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Delete this block by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteBlock(self)
    -
    +
    # Unconnect all inputs and outputs
    def Clean(self):
    for input in self.Inputs:
    input.UnConnect(delete = True)
    for output in self.Outputs:
    output.UnConnect(delete = True)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    -
    +
    # Refresh the size of text for execution order
    def RefreshExecutionOrderSize(self):
    self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder))
    -
    +
    # Returns if the point given is in the bounding box
    def HitTest(self, pt, connectors=True):
    if self.Name != "":
    @@ -121,7 +121,7 @@
    test_text = False
    test_block = self.GetBlockBoundingBox(connectors).InsideXY(pt.x, pt.y)
    return test_text or test_block
    -
    +
    # Returns the bounding box of the name outside the block
    def GetTextBoundingBox(self):
    # Calculate the size of the name outside the block
    @@ -130,7 +130,7 @@
    self.Pos.y - (text_height + 2),
    text_width,
    text_height)
    -
    +
    # Returns the bounding box of function block without name outside
    def GetBlockBoundingBox(self, connectors=True):
    bbx_x, bbx_y = self.Pos.x, self.Pos.y
    @@ -143,13 +143,13 @@
    bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0])
    bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2)
    return wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
    -
    +
    # Refresh the block bounding box
    def RefreshBoundingBox(self):
    self.BoundingBox = self.GetBlockBoundingBox()
    if self.Name != "":
    self.BoundingBox.Union(self.GetTextBoundingBox())
    -
    +
    # Refresh the positions of the block connectors
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -170,15 +170,15 @@
    self.Outputs[i].SetPosition(wx.Point(self.Size[0], ypos))
    position += linesize
    self.RefreshConnected()
    -
    +
    # Refresh the positions of wires connected to inputs and outputs
    def RefreshConnected(self, exclude = []):
    for input in self.Inputs:
    input.MoveConnected(exclude)
    for output in self.Outputs:
    output.MoveConnected(exclude)
    -
    - # Returns the block connector that starts with the point given if it exists
    +
    + # Returns the block connector that starts with the point given if it exists
    def GetConnector(self, position, output_name = None, input_name = None):
    if input_name is not None:
    # Test each input connector
    @@ -193,14 +193,14 @@
    if input_name is None and output_name is None:
    return self.FindNearestConnector(position, self.Inputs + self.Outputs)
    return None
    -
    +
    def GetInputTypes(self):
    return tuple([input.GetType(True) for input in self.Inputs if input.GetName() != "EN"])
    -
    +
    def SetOutputValues(self, values):
    for output in self.Outputs:
    output.SetValue(values.get(ouput.getName(), None))
    -
    +
    def GetConnectionResultType(self, connector, connectortype):
    if not TestConnectorName(connector.GetName(), self.Type):
    return connectortype
    @@ -216,11 +216,11 @@
    if resulttype is None or outputtype is not None and self.IsOfType(outputtype, resulttype):
    resulttype = outputtype
    return resulttype
    -
    +
    # Returns all the block connectors
    def GetConnectors(self):
    return {"inputs" : self.Inputs, "outputs" : self.Outputs}
    -
    +
    # Test if point given is on one of the block connectors
    def TestConnector(self, pt, direction = None, exclude = True):
    # Test each input connector
    @@ -232,10 +232,10 @@
    if output.TestPoint(pt, direction, exclude):
    return output
    return None
    -
    +
    # Changes the block type
    def SetType(self, type, extension, inputs = None, connectors = {}, executionControl = False):
    - if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl:
    + if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl:
    if type != self.Type:
    self.Type = type
    self.TypeSize = self.Parent.GetTextExtent(self.Type)
    @@ -264,7 +264,7 @@
    inputs.insert(0, ("EN","BOOL","none"))
    outputs.insert(0, ("ENO","BOOL","none"))
    self.Pen = MiterPen(self.Colour)
    -
    +
    # Extract the inputs properties and create or modify the corresponding connector
    input_connectors = []
    for input_name, input_type, input_modifier in inputs:
    @@ -284,7 +284,7 @@
    for input in self.Inputs:
    input.UnConnect(delete = True)
    self.Inputs = input_connectors
    -
    +
    # Extract the outputs properties and create or modify the corresponding connector
    output_connectors = []
    for output_name, output_type, output_modifier in outputs:
    @@ -304,47 +304,47 @@
    for output in self.Outputs:
    output.UnConnect(delete = True)
    self.Outputs = output_connectors
    -
    +
    self.RefreshMinSize()
    self.RefreshConnectors()
    for output in self.Outputs:
    output.RefreshWires()
    self.RefreshBoundingBox()
    -
    +
    # Returns the block type
    def GetType(self):
    return self.Type
    -
    +
    # Changes the block name
    def SetName(self, name):
    self.Name = name
    self.RefreshNameSize()
    -
    +
    # Returs the block name
    def GetName(self):
    return self.Name
    -
    +
    # Changes the extension name
    def SetExtension(self, extension):
    self.Extension = extension
    -
    +
    # Returs the extension name
    def GetExtension(self):
    return self.Extension
    -
    +
    # Changes the execution order
    def SetExecutionOrder(self, executionOrder):
    self.ExecutionOrder = executionOrder
    self.RefreshExecutionOrderSize()
    -
    +
    # Returs the execution order
    def GetExecutionOrder(self):
    return self.ExecutionOrder
    -
    +
    # Returs the execution order
    def GetExecutionControl(self):
    return self.ExecutionControl
    -
    +
    # Refresh the block minimum size
    def RefreshMinSize(self):
    # Calculate the inputs maximum width
    @@ -360,25 +360,25 @@
    width = max(self.TypeSize[0] + 10, max_input + max_output + 15)
    height = (max(len(self.Inputs), len(self.Outputs)) + 1) * BLOCK_LINE_SIZE
    self.MinSize = width, height
    -
    +
    # Returns the block minimum size
    def GetMinSize(self):
    return self.MinSize
    -
    +
    # Changes the negated property of the connector handled
    def SetConnectorNegated(self, negated):
    handle_type, handle = self.Handle
    if handle_type == HANDLE_CONNECTOR:
    handle.SetNegated(negated)
    self.RefreshModel(False)
    -
    +
    # Changes the edge property of the connector handled
    def SetConnectorEdge(self, edge):
    handle_type, handle = self.Handle
    if handle_type == HANDLE_CONNECTOR:
    handle.SetEdge(edge)
    self.RefreshModel(False)
    -
    +
    ## # Method called when a Motion event have been generated
    ## def OnMotion(self, event, dc, scaling):
    ## if not event.Dragging():
    @@ -390,12 +390,12 @@
    ## tip = wx.TipWindow(self.Parent, "Test")
    ## tip.SetBoundingRect(rect)
    ## return Graphic_Element.OnMotion(self, event, dc, scaling)
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the block properties
    self.Parent.EditBlockContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -406,7 +406,7 @@
    self.Parent.PopupBlockMenu(connector)
    else:
    self.Parent.PopupBlockMenu()
    -
    +
    # Refreshes the block model
    def RefreshModel(self, move=True):
    self.Parent.RefreshBlockModel(self)
    @@ -414,10 +414,10 @@
    if move:
    for output in self.Outputs:
    output.RefreshWires()
    -
    +
    def GetToolTipValue(self):
    return self.Description
    -
    +
    # Adds an highlight to the block
    def AddHighlight(self, infos, start, end ,highlight_type):
    if infos[0] in ["type", "name"] and start[0] == 0 and end[0] == 0:
    @@ -427,7 +427,7 @@
    self.Inputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type)
    elif infos[0] == "output" and infos[1] < len(self.Outputs):
    self.Outputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type)
    -
    +
    # Removes an highlight from the block
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] in ["type", "name"]:
    @@ -438,7 +438,7 @@
    self.Inputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type)
    elif infos[0] == "output" and infos[1] < len(self.Outputs):
    self.Outputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type)
    -
    +
    # Removes all the highlights of one particular type from the block
    def ClearHighlight(self, highlight_type=None):
    if highlight_type is None:
    @@ -453,14 +453,14 @@
    input.ClearHighlights(highlight_type)
    for output in self.Outputs:
    output.ClearHighlights(highlight_type)
    -
    +
    # Draws block
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    dc.SetPen(self.Pen)
    dc.SetBrush(wx.WHITE_BRUSH)
    dc.SetTextForeground(self.Colour)
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    type_size = dc.GetTextExtent(self.Type)
    @@ -469,7 +469,7 @@
    name_size = self.NameSize
    type_size = self.TypeSize
    executionorder_size = self.ExecutionOrderSize
    -
    +
    # Draw a rectangle with the block size
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    # Draw block name and block type
    @@ -488,11 +488,11 @@
    # Draw block execution order
    dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0],
    self.Pos.y + self.Size[1] + 2)
    -
    +
    if not getattr(dc, "printing", False):
    DrawHighlightedText(dc, self.Name, self.Highlights.get("name", []), name_pos[0], name_pos[1])
    DrawHighlightedText(dc, self.Type, self.Highlights.get("type", []), type_pos[0], type_pos[1])
    -
    +
    #-------------------------------------------------------------------------------
    # Function Block Diagram Variable
    @@ -516,7 +516,7 @@
    self.Output = None
    self.SetType(type, value_type)
    self.Highlights = []
    -
    +
    # Make a clone of this FBD_Variable
    def Clone(self, parent, id = None, pos = None):
    variable = FBD_Variable(parent, self.Type, self.Name, self.ValueType, id)
    @@ -530,7 +530,7 @@
    if self.Output:
    variable.Output = self.Output.Clone(variable)
    return variable
    -
    +
    def GetConnectorTranslation(self, element):
    connectors = {}
    if self.Input is not None:
    @@ -538,7 +538,7 @@
    if self.Output is not None:
    connectors[self.Output] = element.Output
    return connectors
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    @@ -546,7 +546,7 @@
    if self.Output is not None:
    self.Output.Flush()
    self.Output = None
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -556,26 +556,26 @@
    if self.Output and self.Output.IsConnected():
    rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Unconnect connector
    def Clean(self):
    if self.Input:
    self.Input.UnConnect(delete = True)
    if self.Output:
    self.Output.UnConnect(delete = True)
    -
    +
    # Delete this variable by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteVariable(self)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    -
    +
    # Refresh the size of text for execution order
    def RefreshExecutionOrderSize(self):
    self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder))
    -
    +
    # Refresh the variable bounding box
    def RefreshBoundingBox(self):
    if self.Type in (OUTPUT, INOUT):
    @@ -594,7 +594,7 @@
    bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0])
    bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2)
    self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width + 1, bbx_height + 1)
    -
    +
    # Refresh the position of the variable connector
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -607,14 +607,14 @@
    if self.Output:
    self.Output.SetPosition(wx.Point(self.Size[0], position))
    self.RefreshConnected()
    -
    +
    # Refresh the position of wires connected to connector
    def RefreshConnected(self, exclude = []):
    if self.Input:
    self.Input.MoveConnected(exclude)
    if self.Output:
    self.Output.MoveConnected(exclude)
    -
    +
    # Test if point given is on the variable connector
    def TestConnector(self, pt, direction = None, exclude=True):
    if self.Input and self.Input.TestPoint(pt, direction, exclude):
    @@ -622,8 +622,8 @@
    if self.Output and self.Output.TestPoint(pt, direction, exclude):
    return self.Output
    return None
    -
    - # Returns the block connector that starts with the point given if it exists
    +
    + # Returns the block connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -640,8 +640,8 @@
    if self.Output:
    connectors.append(self.Output)
    return self.FindNearestConnector(position, connectors)
    -
    - # Returns all the block connectors
    +
    + # Returns all the block connectors
    def GetConnectors(self):
    connectors = {"inputs": [], "outputs": []}
    if self.Input:
    @@ -649,14 +649,14 @@
    if self.Output:
    connectors["outputs"].append(self.Output)
    return connectors
    -
    +
    # Changes the negated property of the variable connector if handled
    def SetConnectorNegated(self, negated):
    handle_type, handle = self.Handle
    if handle_type == HANDLE_CONNECTOR:
    handle.SetNegated(negated)
    self.RefreshModel(False)
    -
    +
    # Changes the variable type
    def SetType(self, type, value_type):
    if type != self.Type:
    @@ -680,38 +680,38 @@
    if self.Input:
    self.Input.SetType(value_type)
    if self.Output:
    - self.Output.SetType(value_type)
    -
    + self.Output.SetType(value_type)
    +
    # Returns the variable type
    def GetType(self):
    return self.Type
    -
    +
    # Returns the variable value type
    def GetValueType(self):
    return self.ValueType
    -
    +
    # Changes the variable name
    def SetName(self, name):
    self.Name = name
    self.RefreshNameSize()
    -
    +
    # Returns the variable name
    def GetName(self):
    return self.Name
    -
    +
    # Changes the execution order
    def SetExecutionOrder(self, executionOrder):
    self.ExecutionOrder = executionOrder
    self.RefreshExecutionOrderSize()
    -
    +
    # Returs the execution order
    def GetExecutionOrder(self):
    return self.ExecutionOrder
    -
    +
    # Returns the variable minimum size
    def GetMinSize(self):
    return self.NameSize[0] + 10, self.NameSize[1] + 10
    -
    +
    # Set size of the variable to the minimum size
    def SetBestSize(self, scaling):
    if self.Type == INPUT:
    @@ -720,22 +720,22 @@
    return Graphic_Element.SetBestSize(self, scaling, x_factor=0.)
    else:
    return Graphic_Element.SetBestSize(self, scaling)
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    if event.ControlDown():
    - # Change variable type
    + # Change variable type
    types = [INPUT, OUTPUT, INOUT]
    self.Parent.ChangeVariableType(self,
    types[(types.index(self.Type) + 1) % len(types)])
    else:
    # Edit the variable properties
    self.Parent.EditVariableContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    self.Parent.PopupVariableMenu()
    -
    +
    # Refreshes the variable model
    def RefreshModel(self, move=True):
    self.Parent.RefreshVariableModel(self)
    @@ -744,35 +744,35 @@
    if move and self.Type != OUTPUT:
    if self.Output:
    self.Output.RefreshWires()
    -
    +
    # Adds an highlight to the variable
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "expression" and start[0] == 0 and end[0] == 0:
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the variable
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "expression":
    RemoveHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes all the highlights of one particular type from the variable
    def ClearHighlight(self, highlight_type=None):
    ClearHighlights(self.Highlights, highlight_type)
    -
    +
    # Draws variable
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.WHITE_BRUSH)
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    executionorder_size = dc.GetTextExtent(str(self.ExecutionOrder))
    else:
    name_size = self.NameSize
    executionorder_size = self.ExecutionOrderSize
    -
    - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
    +
    + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
    self.Pos.y + (self.Size[1] - name_size[1]) / 2)
    # Draw a rectangle with the variable size
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    @@ -781,7 +781,7 @@
    # Draw connectors
    if self.Input:
    self.Input.Draw(dc)
    - if self.Output:
    + if self.Output:
    self.Output.Draw(dc)
    if self.ExecutionOrder != 0:
    # Draw variable execution order
    @@ -789,7 +789,7 @@
    self.Pos.y + self.Size[1] + 2)
    if not getattr(dc, "printing", False):
    DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
    -
    +
    #-------------------------------------------------------------------------------
    # Function Block Diagram Connector
    #-------------------------------------------------------------------------------
    @@ -816,12 +816,12 @@
    self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST)
    self.RefreshConnectors()
    self.RefreshNameSize()
    -
    +
    def Flush(self):
    if self.Connector:
    self.Connector.Flush()
    self.Connector = None
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -829,7 +829,7 @@
    if self.Connector and self.Connector.IsConnected():
    rect = rect.Union(self.Connector.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Make a clone of this FBD_Connector
    def Clone(self, parent, id = None, pos = None):
    connection = FBD_Connector(parent, self.Type, self.Name, id)
    @@ -840,7 +840,7 @@
    connection.SetPosition(self.Pos.x, self.Pos.y)
    connection.Connector = self.Connector.Clone(connection)
    return connection
    -
    +
    def GetConnectorTranslation(self, element):
    return {self.Connector : element.Connector}
    @@ -848,15 +848,15 @@
    def Clean(self):
    if self.Connector:
    self.Connector.UnConnect(delete = True)
    -
    +
    # Delete this connection by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteConnection(self)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    -
    +
    # Refresh the connection bounding box
    def RefreshBoundingBox(self):
    if self.Type == CONNECTOR:
    @@ -865,7 +865,7 @@
    bbx_x = self.Pos.x
    bbx_width = self.Size[0] + CONNECTOR_SIZE
    self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width, self.Size[1])
    -
    +
    # Refresh the position of the connection connector
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -878,23 +878,23 @@
    else:
    self.Connector.SetPosition(wx.Point(self.Size[0], position))
    self.RefreshConnected()
    -
    +
    # Refresh the position of wires connected to connector
    def RefreshConnected(self, exclude = []):
    if self.Connector:
    self.Connector.MoveConnected(exclude)
    -
    +
    # Test if point given is on the connection connector
    def TestConnector(self, pt, direction = None, exclude=True):
    if self.Connector and self.Connector.TestPoint(pt, direction, exclude):
    return self.Connector
    return None
    -
    +
    # Returns the connection connector
    def GetConnector(self, position = None, name = None):
    return self.Connector
    -
    - # Returns all the block connectors
    +
    + # Returns all the block connectors
    def GetConnectors(self):
    connectors = {"inputs": [], "outputs": []}
    if self.Type == CONNECTOR:
    @@ -923,45 +923,45 @@
    self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST)
    self.RefreshConnectors()
    self.RefreshBoundingBox()
    -
    +
    # Returns the connection type
    def GetType(self):
    return self.Type
    -
    +
    def GetConnectionResultType(self, connector, connectortype):
    if self.Type == CONTINUATION:
    connector = self.Parent.GetConnectorByName(self.Name)
    if connector is not None:
    return connector.Connector.GetConnectedType()
    return connectortype
    -
    +
    # Changes the connection name
    def SetName(self, name):
    self.Name = name
    self.RefreshNameSize()
    -
    +
    # Returns the connection name
    def GetName(self):
    return self.Name
    -
    +
    # Set size of the variable to the minimum size
    def SetBestSize(self, scaling):
    if self.Type == CONTINUATION:
    return Graphic_Element.SetBestSize(self, scaling, x_factor=1.)
    else:
    return Graphic_Element.SetBestSize(self, scaling, x_factor=0.)
    -
    +
    # Returns the connection minimum size
    def GetMinSize(self):
    text_width, text_height = self.NameSize
    if text_height % 2 == 1:
    text_height += 1
    return text_width + text_height + 20, text_height + 10
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    if event.ControlDown():
    - # Change connection type
    + # Change connection type
    if self.Type == CONNECTOR:
    self.Parent.ChangeConnectionType(self, CONTINUATION)
    else:
    @@ -969,12 +969,12 @@
    else:
    # Edit the connection properties
    self.Parent.EditConnectionContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupConnectionMenu()
    -
    +
    # Refreshes the connection model
    def RefreshModel(self, move=True):
    self.Parent.RefreshConnectionModel(self)
    @@ -983,51 +983,50 @@
    if move and self.Type == CONTINUATION:
    if self.Connector:
    self.Connector.RefreshWires()
    -
    +
    # Adds an highlight to the connection
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "name" and start[0] == 0 and end[0] == 0:
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the connection
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "name":
    RemoveHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes all the highlights of one particular type from the connection
    def ClearHighlight(self, highlight_type=None):
    ClearHighlights(self.Highlights, highlight_type)
    -
    +
    # Draws connection
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.WHITE_BRUSH)
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    else:
    name_size = self.NameSize
    -
    +
    # Draw a rectangle with the connection size with arrows inside
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    arrowsize = min(self.Size[1] / 2, (self.Size[0] - name_size[0] - 10) / 2)
    - dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize,
    + dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize,
    self.Pos.y + self.Size[1] / 2)
    - dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2,
    + dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2,
    self.Pos.x, self.Pos.y + self.Size[1])
    - dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y,
    + dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y,
    self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2)
    - dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2,
    + dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2,
    self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1])
    # Draw connection name
    - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
    + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
    self.Pos.y + (self.Size[1] - name_size[1]) / 2)
    dc.DrawText(self.Name, text_pos[0], text_pos[1])
    # Draw connector
    if self.Connector:
    self.Connector.Draw(dc)
    -
    +
    if not getattr(dc, "printing", False):
    DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
    -
    --- a/graphics/GraphicCommons.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/GraphicCommons.py Mon Aug 14 19:13:01 2017 +0300
    @@ -83,12 +83,12 @@
    # Contants for defining the direction of a connector
    [EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)]
    -# Contants for defining which mode is selected for each view
    -[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT,
    - MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP,
    +# Contants for defining which mode is selected for each view
    +[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT,
    + MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP,
    MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15)
    -# Contants for defining alignment types for graphic group
    +# Contants for defining alignment types for graphic group
    [ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6)
    # Contants for defining which drawing mode is selected for app
    @@ -122,7 +122,7 @@
    else:
    xround = round(fraction)
    if constraint == 1 and xround < fraction:
    - xround += 1
    + xround += 1
    return int(xround * n)
    """
    @@ -224,7 +224,7 @@
    dc.DrawText(part, x + offset_width, y)
    dc.SetPen(current_pen)
    dc.SetTextForeground(wx.BLACK)
    -
    +
    #-------------------------------------------------------------------------------
    # Graphic element base class
    #-------------------------------------------------------------------------------
    @@ -234,7 +234,7 @@
    """
    class Graphic_Element(ToolTipProducer):
    -
    +
    # Create a new graphic element
    def __init__(self, parent, id = None):
    ToolTipProducer.__init__(self, parent)
    @@ -251,22 +251,22 @@
    self.Size = wx.Size(0, 0)
    self.BoundingBox = wx.Rect(0, 0, 0, 0)
    self.Visible = False
    -
    +
    def GetDefinition(self):
    return [self.Id], []
    -
    +
    def TestVisible(self, screen):
    self.Visible = self.Selected or self.GetRedrawRect().Intersects(screen)
    -
    +
    def IsVisible(self):
    return self.Visible
    -
    +
    def SpreadCurrent(self):
    pass
    -
    +
    def GetConnectorTranslation(self, element):
    return {}
    -
    +
    def FindNearestConnector(self, position, connectors):
    distances = []
    for connector in connectors:
    @@ -278,20 +278,20 @@
    if len(distances) > 0:
    return distances[0][1]
    return None
    -
    +
    def IsOfType(self, type, reference):
    return self.Parent.IsOfType(type, reference)
    -
    +
    def IsEndType(self, type):
    return self.Parent.IsEndType(type)
    -
    +
    def GetDragging(self):
    return self.Dragging
    -
    +
    # Make a clone of this element
    def Clone(self, parent):
    return Graphic_Element(parent, self.Id)
    -
    +
    # Changes the block position
    def SetPosition(self, x, y):
    self.Pos.x = x
    @@ -302,7 +302,7 @@
    # Returns the block position
    def GetPosition(self):
    return self.Pos.x, self.Pos.y
    -
    +
    # Changes the element size
    def SetSize(self, width, height):
    self.Size.SetWidth(width)
    @@ -313,11 +313,11 @@
    # Returns the element size
    def GetSize(self):
    return self.Size.GetWidth(), self.Size.GetHeight()
    -
    +
    # Returns the minimum element size
    def GetMinSize(self):
    return 0, 0
    -
    +
    # Set size of the element to the minimum size
    def SetBestSize(self, scaling, x_factor=0.5, y_factor=0.5):
    width, height = self.GetSize()
    @@ -336,31 +336,31 @@
    height = round_scaling(height, scaling[1], 1)
    self.SetSize(width, height)
    return self.Pos.x - posx, self.Pos.y - posy
    -
    +
    # Refresh the element Bounding Box
    def RefreshBoundingBox(self):
    self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
    -
    +
    # Refresh the element connectors position
    def RefreshConnectors(self):
    pass
    -
    +
    # Refresh the position of wires connected to element inputs and outputs
    def RefreshConnected(self):
    pass
    -
    +
    # Change the parent
    def SetParent(self, parent):
    self.Parent = parent
    -
    +
    # Override this method for defining the method to call for deleting this element
    def Delete(self):
    pass
    -
    +
    # Returns the Id
    def GetId(self):
    return self.Id
    -
    +
    # Returns if the point given is in the bounding box
    def HitTest(self, pt, connectors=True):
    if connectors:
    @@ -368,19 +368,19 @@
    else:
    rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
    return rect.InsideXY(pt.x, pt.y)
    -
    +
    # Returns if the point given is in the bounding box
    def IsInSelection(self, rect):
    return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height)
    -
    +
    # Override this method for refreshing the bounding box
    def RefreshBoundingBox(self):
    pass
    -
    +
    # Returns the bounding box
    def GetBoundingBox(self):
    return self.BoundingBox
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    scalex, scaley = self.Parent.GetViewScale()
    @@ -390,42 +390,42 @@
    rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4
    rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4
    return rect
    -
    +
    def Refresh(self, rect = None):
    if self.Visible:
    if rect is not None:
    self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
    else:
    self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False)
    -
    +
    # Change the variable that indicates if this element is selected
    def SetSelected(self, selected):
    self.Selected = selected
    self.Refresh()
    -
    +
    # Change the variable that indicates if this element is highlighted
    def SetHighlighted(self, highlighted):
    self.Highlighted = highlighted
    self.Refresh()
    -
    +
    # Test if the point is on a handle of this element
    def TestHandle(self, event):
    dc = self.Parent.GetLogicalDC()
    scalex, scaley = dc.GetUserScale()
    pos = event.GetPosition()
    pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y))
    -
    +
    left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
    center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
    right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
    -
    +
    top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
    middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
    bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
    -
    +
    extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top)
    intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE)
    -
    +
    # Verify that this element is selected
    if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y):
    # Find if point is on a handle horizontally
    @@ -450,7 +450,7 @@
    if (handle_x, handle_y) in VALID_HANDLES:
    return handle_x, handle_y
    return 0, 0
    -
    +
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
    pos = event.GetLogicalPosition(dc)
    @@ -469,7 +469,7 @@
    self.oldPos = GetScaledEventPosition(event, dc, scaling)
    self.StartPos = wx.Point(self.Pos.x, self.Pos.y)
    self.CurrentDrag = wx.Point(0, 0)
    -
    +
    # Method called when a LeftUp event have been generated
    def OnLeftUp(self, event, dc, scaling):
    # If a dragging have been initiated
    @@ -498,7 +498,7 @@
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    pass
    -
    +
    # Method called when a Motion event have been generated
    def OnMotion(self, event, dc, scaling):
    # If the cursor is dragging and the element have been clicked
    @@ -537,12 +537,12 @@
    self.Pos.y += max(-self.BoundingBox.y, dy)
    self.RefreshConnected(exclude)
    self.RefreshBoundingBox()
    -
    +
    # Resizes the element from position and size given
    def Resize(self, x, y, width, height):
    self.Move(x, y)
    self.SetSize(width, height)
    -
    +
    # Refreshes the element state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1):
    handle_type, handle = self.Handle
    @@ -618,27 +618,27 @@
    movey = self.StartPos.y - self.Pos.y
    else:
    movex = self.StartPos.x - self.Pos.x
    - movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y
    + movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y
    self.Move(movex, movey)
    return movex, movey
    return 0, 0
    -
    +
    # Override this method for defining the method to call for adding an highlight to this element
    def AddHighlight(self, infos, start, end, highlight_type):
    pass
    -
    +
    # Override this method for defining the method to call for removing an highlight from this element
    def RemoveHighlight(self, infos, start, end, highlight_type):
    pass
    -
    +
    # Override this method for defining the method to call for removing all the highlights of one particular type from this element
    def ClearHighlight(self, highlight_type=None):
    pass
    -
    +
    # Override this method for defining the method to call for refreshing the model of this element
    def RefreshModel(self, move=True):
    pass
    -
    +
    # Draws the highlightment of this element if it is highlighted (can be overwritten)
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -646,13 +646,13 @@
    dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
    dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
    dc.SetLogicalFunction(wx.AND)
    - dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2,
    - int(round((self.Pos.y - 1) * scaley)) - 2,
    - int(round((self.Size.width + 3) * scalex)) + 5,
    + dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2,
    + int(round((self.Pos.y - 1) * scaley)) - 2,
    + int(round((self.Size.width + 3) * scalex)) + 5,
    int(round((self.Size.height + 3) * scaley)) + 5)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Draws the handles of this element if it is selected
    def Draw(self, dc):
    if not getattr(dc, "printing", False):
    @@ -663,20 +663,20 @@
    dc.SetUserScale(1, 1)
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.BLACK_BRUSH)
    -
    +
    left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
    center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
    right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
    -
    +
    top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
    middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
    bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
    -
    +
    for x, y in [(left, top), (center, top), (right, top),
    (left, middle), (right, middle),
    (left, bottom), (center, bottom), (right, bottom)]:
    dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE)
    -
    +
    dc.SetUserScale(scalex, scaley)
    @@ -689,27 +689,27 @@
    """
    class Graphic_Group(Graphic_Element):
    -
    +
    # Create a new group of graphic elements
    def __init__(self, parent):
    Graphic_Element.__init__(self, parent)
    self.Elements = []
    self.RefreshWireExclusion()
    self.RefreshBoundingBox()
    -
    +
    # Destructor
    def __del__(self):
    self.Elements = []
    -
    +
    def GetDefinition(self):
    - blocks = []
    + blocks = []
    wires = []
    for element in self.Elements:
    block, wire = element.GetDefinition()
    blocks.extend(block)
    wires.extend(wire)
    return blocks, wires
    -
    +
    # Make a clone of this element
    def Clone(self, parent, pos = None):
    group = Graphic_Group(parent)
    @@ -751,20 +751,20 @@
    if not isinstance(element, Wire):
    parent.AddBlockInModel(element)
    return group
    -
    +
    def CanAddBlocks(self, parent):
    valid = True
    for element in self.Elements:
    if not isinstance(element, Wire):
    valid &= parent.CanAddElement(element)
    return valid
    -
    +
    def IsVisible(self):
    for element in self.Elements:
    if element.IsVisible():
    return True
    return False
    -
    +
    # Refresh the list of wire excluded
    def RefreshWireExclusion(self):
    self.WireExcluded = []
    @@ -774,7 +774,7 @@
    endblock = element.EndConnected.GetParentBlock()
    if startblock in self.Elements and endblock in self.Elements:
    self.WireExcluded.append(element)
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = None
    @@ -784,41 +784,41 @@
    else:
    rect = rect.Union(element.GetRedrawRect(movex, movey))
    return rect
    -
    +
    # Clean this group of elements
    def Clean(self):
    # Clean all the elements of the group
    for element in self.Elements:
    element.Clean()
    -
    +
    # Delete this group of elements
    def Delete(self):
    # Delete all the elements of the group
    for element in self.Elements:
    element.Delete()
    self.WireExcluded = []
    -
    +
    # Returns if the point given is in the bounding box of one of the elements of this group
    def HitTest(self, pt, connectors=True):
    result = False
    for element in self.Elements:
    result |= element.HitTest(pt, connectors)
    return result
    -
    +
    # Returns if the element given is in this group
    def IsElementIn(self, element):
    return element in self.Elements
    -
    +
    # Change the elements of the group
    def SetElements(self, elements):
    self.Elements = elements
    self.RefreshWireExclusion()
    self.RefreshBoundingBox()
    -
    +
    # Returns the elements of the group
    def GetElements(self):
    return self.Elements
    -
    +
    # Align the group elements
    def AlignElements(self, horizontally, vertically):
    minx = self.BoundingBox.x + self.BoundingBox.width
    @@ -854,11 +854,11 @@
    element.Move(movex, movey)
    element.RefreshModel()
    self.RefreshBoundingBox()
    -
    +
    # Add the given element to the group of elements
    def AddElement(self, element):
    self.Elements.append(element)
    -
    +
    # Remove or select the given element if it is or not in the group
    def SelectElement(self, element):
    if element in self.Elements:
    @@ -867,7 +867,7 @@
    self.Elements.append(element)
    self.RefreshWireExclusion()
    self.RefreshBoundingBox()
    -
    +
    # Move this group of elements
    def Move(self, movex, movey):
    movex = max(-self.BoundingBox.x, movex)
    @@ -879,7 +879,7 @@
    elif element in self.WireExcluded:
    element.Move(movex, movey, True)
    self.RefreshBoundingBox()
    -
    +
    # Refreshes the bounding box of this group of elements
    def RefreshBoundingBox(self):
    if len(self.Elements) > 0:
    @@ -902,7 +902,7 @@
    # Forbids to change the group position
    def SetPosition(x, y):
    pass
    -
    +
    # Returns the position of this group
    def GetPosition(self, exclude_wires=False):
    if exclude_wires:
    @@ -919,15 +919,15 @@
    return 0, 0
    return posx, posy
    return self.BoundingBox.x, self.BoundingBox.y
    -
    +
    # Forbids to change the group size
    def SetSize(width, height):
    pass
    -
    +
    # Returns the size of this group
    def GetSize(self):
    return self.BoundingBox.width, self.BoundingBox.height
    -
    +
    # Set size of the group elements to their minimum size
    def SetBestSize(self, scaling):
    max_movex = max_movey = 0
    @@ -936,7 +936,7 @@
    max_movex = max(max_movex, movex)
    max_movey = max(max_movey, movey)
    return max_movex, max_movey
    -
    +
    # Refreshes the group elements to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    handle_type, handle = self.Handle
    @@ -960,17 +960,17 @@
    self.Move(movex, movey)
    return movex, movey
    return 0, 0
    -
    +
    # Change the variable that indicates if this element is highlighted
    def SetHighlighted(self, highlighted):
    for element in self.Elements:
    element.SetHighlighted(highlighted)
    -
    +
    def HighlightPoint(self, pos):
    for element in self.Elements:
    if isinstance(element, Wire):
    element.HighlightPoint(pos)
    -
    +
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
    Graphic_Element.OnLeftDown(self, event, dc, scaling)
    @@ -1007,7 +1007,7 @@
    """
    class Connector(DebugDataConsumer, ToolTipProducer):
    -
    +
    # Create a new connector
    def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False):
    DebugDataConsumer.__init__(self)
    @@ -1033,13 +1033,13 @@
    self.Selected = False
    self.Highlights = []
    self.RefreshNameSize()
    -
    +
    def Flush(self):
    self.ParentBlock = None
    for wire, handle in self.Wires:
    wire.Flush()
    self.Wires = []
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    parent_pos = self.ParentBlock.GetPosition()
    @@ -1072,22 +1072,22 @@
    height * (self.Direction[1] - 1),
    width, height))
    return rect
    -
    +
    # Change the connector selection
    def SetSelected(self, selected):
    self.Selected = selected
    -
    +
    # Make a clone of the connector
    def Clone(self, parent = None):
    if parent is None:
    parent = self.ParentBlock
    return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]),
    self.Direction, self.Negated)
    -
    +
    # Returns the connector parent block
    def GetParentBlock(self):
    return self.ParentBlock
    -
    +
    # Returns the connector type
    def GetType(self, raw = False):
    if self.ParentBlock.IsEndType(self.Type) or raw:
    @@ -1096,7 +1096,7 @@
    return "BOOL"
    else:
    return self.ParentBlock.GetConnectionResultType(self, self.Type)
    -
    +
    # Returns the connector type
    def GetConnectedType(self):
    if self.ParentBlock.IsEndType(self.Type):
    @@ -1104,7 +1104,7 @@
    elif len(self.Wires) == 1:
    return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1])
    return self.Type
    -
    +
    # Returns the connector type
    def GetConnectedRedrawRect(self, movex, movey):
    rect = None
    @@ -1114,22 +1114,22 @@
    else:
    rect = rect.Union(wire.GetRedrawRect())
    return rect
    -
    +
    # Returns if connector type is compatible with type given
    def IsCompatible(self, type):
    reference = self.GetType()
    return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type)
    -
    +
    # Changes the connector name
    def SetType(self, type):
    self.Type = type
    for wire, handle in self.Wires:
    wire.SetValid(wire.IsConnectedCompatible())
    -
    +
    # Returns the connector name
    def GetName(self):
    return self.Name
    -
    +
    # Changes the connector name
    def SetName(self, name):
    self.Name = name
    @@ -1140,15 +1140,15 @@
    self.Forced = forced
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    def GetComputedValue(self):
    if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
    return self.Value
    return None
    -
    +
    def GetToolTipValue(self):
    return self.GetComputedValue()
    -
    +
    def SetValue(self, value):
    if self.Value != value:
    self.Value = value
    @@ -1161,7 +1161,7 @@
    self.ValueSize = None
    if self.ParentBlock.Visible:
    self.ParentBlock.Parent.ElementNeedRefresh(self)
    -
    +
    def RefreshForced(self):
    self.Forced = False
    for wire, handle in self.Wires:
    @@ -1169,12 +1169,12 @@
    def RefreshValue(self):
    self.Value = self.ReceivingCurrent()
    -
    +
    def RefreshValid(self):
    self.Valid = True
    for wire, handle in self.Wires:
    self.Valid &= wire.GetValid()
    -
    +
    def ReceivingCurrent(self):
    current = False
    for wire, handle in self.Wires:
    @@ -1184,34 +1184,34 @@
    elif value == "undefined":
    current = "undefined"
    return current
    -
    +
    def SpreadCurrent(self, spreading):
    for wire, handle in self.Wires:
    wire.SetValue(spreading)
    -
    +
    # Changes the connector name size
    def RefreshNameSize(self):
    if self.Name != "":
    self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name)
    else:
    self.NameSize = 0, 0
    -
    +
    # Returns the connector name size
    def GetNameSize(self):
    return self.NameSize
    -
    +
    # Returns the wires connected to the connector
    def GetWires(self):
    return self.Wires
    -
    +
    # Returns the parent block Id
    def GetBlockId(self):
    return self.ParentBlock.GetId()
    -
    +
    # Returns the connector relative position
    def GetRelPosition(self):
    return self.Pos
    -
    +
    # Returns the connector absolute position
    def GetPosition(self, size = True):
    parent_pos = self.ParentBlock.GetPosition()
    @@ -1223,23 +1223,23 @@
    x = parent_pos[0] + self.Pos.x
    y = parent_pos[1] + self.Pos.y
    return wx.Point(x, y)
    -
    +
    # Change the connector relative position
    def SetPosition(self, pos):
    self.Pos = pos
    -
    +
    # Returns the connector direction
    def GetDirection(self):
    return self.Direction
    -
    +
    # Change the connector direction
    def SetDirection(self, direction):
    self.Direction = direction
    -
    +
    # Connect a wire to this connector at the last place
    def Connect(self, wire, refresh = True):
    self.InsertConnect(len(self.Wires), wire, refresh)
    -
    +
    # Connect a wire to this connector at the place given
    def InsertConnect(self, idx, wire, refresh = True):
    if wire not in self.Wires:
    @@ -1250,14 +1250,14 @@
    wire[0].ConnectEndPoint(None, self)
    if refresh:
    self.ParentBlock.RefreshModel(False)
    -
    +
    # Returns the index of the wire given in the list of connected
    def GetWireIndex(self, wire):
    for i, (tmp_wire, handle) in enumerate(self.Wires):
    if tmp_wire == wire:
    return i
    return None
    -
    +
    # Unconnect a wire or all wires connected to the connector
    def UnConnect(self, wire = None, unconnect = True, delete = False):
    i = 0
    @@ -1281,11 +1281,11 @@
    if not delete:
    self.RefreshValid()
    self.ParentBlock.RefreshModel(False)
    -
    +
    # Returns if connector has one or more wire connected
    def IsConnected(self):
    return len(self.Wires) > 0
    -
    +
    # Move the wires connected
    def MoveConnected(self, exclude = []):
    if len(self.Wires) > 0:
    @@ -1300,21 +1300,21 @@
    wire.MoveStartPoint(wx.Point(x, y))
    else:
    wire.MoveEndPoint(wx.Point(x, y))
    -
    +
    # Refreshes the model of all the wires connected
    def RefreshWires(self):
    for wire in self.Wires:
    wire[0].RefreshModel()
    -
    +
    # Refreshes the parent block model
    def RefreshParentBlock(self):
    self.ParentBlock.RefreshModel(False)
    -
    +
    # Highlight the parent block
    def HighlightParentBlock(self, highlight):
    self.ParentBlock.SetHighlighted(highlight)
    self.ParentBlock.Refresh()
    -
    +
    # Returns all the blocks connected to this connector
    def GetConnectedBlocks(self):
    blocks = []
    @@ -1330,31 +1330,31 @@
    if block not in blocks:
    blocks.append(block)
    return blocks
    -
    +
    # Returns the connector negated property
    def IsNegated(self):
    return self.Negated
    -
    +
    # Changes the connector negated property
    def SetNegated(self, negated):
    if self.ParentBlock.IsOfType("BOOL", self.Type):
    self.Negated = negated
    self.Edge = "none"
    -
    +
    # Returns the connector edge property
    def GetEdge(self):
    return self.Edge
    -
    +
    # Changes the connector edge property
    def SetEdge(self, edge):
    if self.ParentBlock.IsOfType("BOOL", self.Type):
    - self.Edge = edge
    + self.Edge = edge
    self.Negated = False
    -
    +
    # assume that pointer is already inside of this connector
    def ConnectionAvailable(self, direction=None, exclude=True):
    wire_nums = len(self.Wires)
    -
    +
    connector_free = (wire_nums<= 0)
    connector_max_used = ((wire_nums > 0) and self.OneConnected)
    if (self.Parent.CurrentLanguage in ["SFC", "LD"]) and (self.Type == "BOOL"):
    @@ -1363,7 +1363,7 @@
    # connector is available for new connection
    connect = connector_free or not connector_max_used
    return connect, connector_max_used
    -
    +
    # Tests if the point given is near from the end point of this connector
    def TestPoint(self, pt, direction=None, exclude=True):
    inside = False;
    @@ -1378,9 +1378,9 @@
    height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE
    rect = wx.Rect(x, y, width, height)
    inside = rect.InsideXY(pt.x, pt.y);
    -
    +
    return inside
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -1393,7 +1393,7 @@
    parent_pos = self.ParentBlock.GetPosition()
    posx = parent_pos[0] + self.Pos.x
    posy = parent_pos[1] + self.Pos.y
    - xstart = parent_pos[0] + self.Pos.x
    + xstart = parent_pos[0] + self.Pos.x
    ystart = parent_pos[1] + self.Pos.y
    if self.Direction[0] < 0:
    xstart += 1
    @@ -1401,18 +1401,18 @@
    ystart += 1
    xend = xstart + CONNECTOR_SIZE * self.Direction[0]
    yend = ystart + CONNECTOR_SIZE * self.Direction[1]
    - dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley),
    + dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley),
    round(xend * scalex), round(yend * scaley))
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Adds an highlight to the connector
    def AddHighlight(self, infos, start, end, highlight_type):
    if highlight_type == ERROR_HIGHLIGHT:
    for wire, handle in self.Wires:
    wire.SetValid(False)
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the connector
    def RemoveHighlight(self, infos, start, end, highlight_type):
    error = False
    @@ -1425,7 +1425,7 @@
    if not error:
    for wire, handle in self.Wires:
    wire.SetValid(wire.IsConnectedCompatible())
    -
    +
    # Removes all the highlights of one particular type from the connector
    def ClearHighlight(self, highlight_type=None):
    error = False
    @@ -1441,7 +1441,7 @@
    if not error:
    for wire, handle in self.Wires:
    wire.SetValid(wire.IsConnectedCompatible())
    -
    +
    # Draws the connector
    def Draw(self, dc):
    if self.Selected:
    @@ -1466,19 +1466,19 @@
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.WHITE_BRUSH)
    parent_pos = self.ParentBlock.GetPosition()
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    else:
    name_size = self.NameSize
    -
    +
    if self.Negated:
    # If connector is negated, draw a circle
    xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2
    ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2
    dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2)
    else:
    - xstart = parent_pos[0] + self.Pos.x
    + xstart = parent_pos[0] + self.Pos.x
    ystart = parent_pos[1] + self.Pos.y
    if self.Edge == "rising":
    # If connector has a rising edge, draw a right arrow
    @@ -1524,7 +1524,7 @@
    self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue)
    if self.ValueSize is not None:
    width, height = self.ValueSize
    - dc.DrawText(self.ComputedValue,
    + dc.DrawText(self.ComputedValue,
    parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \
    width * (self.Direction[0] - 1) / 2,
    parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \
    @@ -1541,7 +1541,7 @@
    """
    class Wire(Graphic_Element, DebugDataConsumer):
    -
    +
    # Create a new wire
    def __init__(self, parent, start = None, end = None):
    Graphic_Element.__init__(self, parent)
    @@ -1568,18 +1568,18 @@
    self.ComputingType = False
    self.Font = parent.GetMiniFont()
    self.ErrHighlight = False
    -
    +
    def GetDefinition(self):
    if self.StartConnected is not None and self.EndConnected is not None:
    startblock = self.StartConnected.GetParentBlock()
    endblock = self.EndConnected.GetParentBlock()
    return [], [(startblock.GetId(), endblock.GetId())]
    return [], []
    -
    +
    def Flush(self):
    self.StartConnected = None
    self.EndConnected = None
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -1607,7 +1607,7 @@
    y = self.Points[middle].y - height
    rect = rect.Union(wx.Rect(x, y, width, height))
    return rect
    -
    +
    def Clone(self, parent, connectors = {}, dx = 0, dy = 0):
    start_connector = connectors.get(self.StartConnected, None)
    end_connector = connectors.get(self.EndConnected, None)
    @@ -1620,18 +1620,18 @@
    wire.ConnectEndPoint(end_connector.GetPosition(), end_connector)
    return wire
    return None
    -
    +
    # Forbids to change the wire position
    def SetPosition(x, y):
    pass
    -
    +
    # Forbids to change the wire size
    def SetSize(width, height):
    pass
    -
    +
    # Forbids to et size of the group elements to their minimum size
    pass
    -
    +
    # Moves and Resizes the element for fitting scaling
    def SetBestSize(self, scaling):
    if scaling is not None:
    @@ -1664,11 +1664,11 @@
    point.y += movey
    return movex_max, movey_max
    return 0, 0
    -
    +
    # Returns connector to which start point is connected
    def GetStartConnected(self):
    return self.StartConnected
    -
    +
    # Returns connector to which start point is connected
    def GetStartConnectedType(self):
    if self.StartConnected and not self.ComputingType:
    @@ -1677,11 +1677,11 @@
    self.ComputingType = False
    return computed_type
    return None
    -
    +
    # Returns connector to which end point is connected
    def GetEndConnected(self):
    return self.EndConnected
    -
    +
    # Returns connector to which end point is connected
    def GetEndConnectedType(self):
    if self.EndConnected and not self.ComputingType:
    @@ -1690,7 +1690,7 @@
    self.ComputingType = False
    return computed_type
    return None
    -
    +
    def GetConnectionDirection(self):
    if self.StartConnected is None and self.EndConnected is None:
    return None
    @@ -1707,26 +1707,26 @@
    else:
    return (-self.StartPoint[1][0], -self.StartPoint[1][1])
    return None
    -
    +
    def GetOtherConnected(self, connector):
    if self.StartConnected == connector:
    return self.EndConnected
    else:
    return self.StartConnected
    -
    +
    def GetOtherConnectedType(self, handle):
    if handle == 0:
    return self.GetEndConnectedType()
    else:
    return self.GetStartConnectedType()
    -
    +
    def IsConnectedCompatible(self):
    if self.StartConnected:
    return self.StartConnected.IsCompatible(self.GetEndConnectedType())
    elif self.EndConnected:
    return True
    return False
    -
    +
    def SetForced(self, forced):
    if self.Forced != forced:
    self.Forced = forced
    @@ -1741,7 +1741,7 @@
    if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
    return self.Value
    return None
    -
    +
    def GetToolTipValue(self):
    return self.GetComputedValue()
    @@ -1773,18 +1773,18 @@
    if isinstance(value, BooleanType) and self.StartConnected is not None:
    block = self.StartConnected.GetParentBlock()
    block.SpreadCurrent()
    -
    +
    # Unconnect the start and end points
    def Clean(self):
    if self.StartConnected:
    self.UnConnectStartPoint()
    if self.EndConnected:
    self.UnConnectEndPoint()
    -
    +
    # Delete this wire by calling the corresponding method
    def Delete(self):
    self.Parent.DeleteWire(self)
    -
    +
    # Select a segment and not the whole wire. It's useful for Ladder Diagram
    def SetSelectedSegment(self, segment):
    # The last segment is indicated
    @@ -1814,17 +1814,17 @@
    self.EndConnected.SetSelected(True)
    self.SelectedSegment = segment
    self.Refresh()
    -
    +
    def SetValid(self, valid):
    self.Valid = valid
    if self.StartConnected:
    self.StartConnected.RefreshValid()
    if self.EndConnected:
    self.EndConnected.RefreshValid()
    -
    +
    def GetValid(self):
    return self.Valid
    -
    +
    # Reinitialize the wire points
    def ResetPoints(self):
    if self.StartPoint and self.EndPoint:
    @@ -1833,7 +1833,7 @@
    else:
    self.Points = []
    self.Segments = []
    -
    +
    # Refresh the wire bounding box
    def RefreshBoundingBox(self):
    if len(self.Points) > 0:
    @@ -1862,7 +1862,7 @@
    self.Pos.x, self.Pos.y = minx, miny
    self.Size = wx.Size(maxx - minx, maxy - miny)
    self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1)
    -
    +
    # Refresh the realpoints that permits to keep the proportionality in wire during resizing
    def RefreshRealPoints(self):
    if len(self.Points) > 0:
    @@ -1870,8 +1870,8 @@
    # Calculate float relative position of each point with the minimum point
    for point in self.Points:
    self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)])
    -
    - # Returns the wire minimum size
    +
    + # Returns the wire minimum size
    def GetMinSize(self):
    width = 1
    height = 1
    @@ -1893,7 +1893,7 @@
    width = MIN_SEGMENT_SIZE
    height = MIN_SEGMENT_SIZE
    return width + 1, height + 1
    -
    +
    # Returns if the point given is on one of the wire segments
    def HitTest(self, pt, connectors=True):
    test = False
    @@ -1903,7 +1903,7 @@
    x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE
    y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE
    else:
    - x1, y1 = self.Points[i].x, self.Points[i].y
    + x1, y1 = self.Points[i].x, self.Points[i].y
    if i == len(self.Points) - 2 and self.EndConnected is not None:
    x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE
    y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE
    @@ -1912,10 +1912,10 @@
    # Calculate a rectangle around the segment
    rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
    abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
    - test |= rect.InsideXY(pt.x, pt.y)
    + test |= rect.InsideXY(pt.x, pt.y)
    return test
    -
    - # Returns the wire start or end point if the point given is on one of them
    +
    + # Returns the wire start or end point if the point given is on one of them
    def TestPoint(self, pt):
    # Test the wire start point
    rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE,
    @@ -1929,7 +1929,7 @@
    if rect.InsideXY(pt.x, pt.y):
    return -1
    return None
    -
    +
    # Returns the wire segment if the point given is on it
    def TestSegment(self, pt, all=False):
    for i in xrange(len(self.Segments)):
    @@ -1943,7 +1943,7 @@
    if rect.InsideXY(pt.x, pt.y):
    return i, self.Segments[i]
    return None
    -
    +
    # Define the wire points
    def SetPoints(self, points, verify=True):
    if len(points) > 1:
    @@ -1952,9 +1952,9 @@
    self.StartPoint = [None, vector(self.Points[0], self.Points[1])]
    self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])]
    # Calculate the start and end points
    - self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0],
    + self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0],
    self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1])
    - self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0],
    + self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0],
    self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1])
    self.Points[0] = self.StartPoint[0]
    self.Points[-1] = self.EndPoint[0]
    @@ -1979,37 +1979,37 @@
    i += 1
    self.RefreshBoundingBox()
    self.RefreshRealPoints()
    -
    +
    # Returns the position of the point indicated
    def GetPoint(self, index):
    if index < len(self.Points):
    return self.Points[index].x, self.Points[index].y
    return None
    -
    +
    # Returns a list of the position of all wire points
    def GetPoints(self, invert = False):
    points = self.VerifyPoints()
    - points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0],
    + points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0],
    points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1])
    - points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0],
    + points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0],
    points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1])
    # An inversion of the list is asked
    if invert:
    points.reverse()
    return points
    -
    +
    # Returns the position of the two selected segment points
    def GetSelectedSegmentPoints(self):
    if self.SelectedSegment != None and len(self.Points) > 1:
    return self.Points[self.SelectedSegment:self.SelectedSegment + 2]
    return []
    -
    +
    # Returns if the selected segment is the first and/or the last of the wire
    def GetSelectedSegmentConnections(self):
    if self.SelectedSegment != None and len(self.Points) > 1:
    return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1
    return (True, True)
    -
    +
    # Returns the connectors on which the wire is connected
    def GetConnected(self):
    connected = []
    @@ -2018,7 +2018,7 @@
    if self.EndConnected and self.EndPoint[1] == WEST:
    connected.append(self.EndConnected)
    return connected
    -
    +
    # Returns the id of the block connected to the first or the last wire point
    def GetConnectedInfos(self, index):
    if index == 0 and self.StartConnected:
    @@ -2026,14 +2026,14 @@
    elif index == -1 and self.EndConnected:
    return self.EndConnected.GetBlockId(), self.EndConnected.GetName()
    return None
    -
    +
    # Update the wire points position by keeping at most possible the current positions
    def GeneratePoints(self, realpoints = True):
    i = 0
    # Calculate the start enad end points with the minimum segment size in the right direction
    end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE,
    self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE)
    - start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE,
    + start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE,
    self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE)
    # Evaluate the point till it's the last
    while i < len(self.Points) - 1:
    @@ -2046,14 +2046,14 @@
    # If the end point is not in the start direction, a point is added
    if v_end != self.Segments[0] or v_end == self.EndPoint[1]:
    self.Points.insert(1, wx.Point(start.x, start.y))
    - self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
    + self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
    self.Segments[0][0]), v_end, self.EndPoint[1]))
    # The current point is the second
    elif i == 1:
    # The previous direction and the target direction are mainly opposed, a point is added
    if product(v_end, self.Segments[0]) < 0:
    self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
    - self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    + self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    self.Segments[1][0]), v_end, self.EndPoint[1]))
    # The previous direction and the end direction are the same or they are
    # perpendiculars and the end direction points towards current segment
    @@ -2066,7 +2066,7 @@
    # If the previous direction and the end direction are the same, a point is added
    if product(self.Segments[0], self.EndPoint[1]) > 0:
    self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
    - self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    + self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    self.Segments[1][0]), v_end, self.EndPoint[1]))
    else:
    # Current point is positioned in the middle of start point
    @@ -2076,7 +2076,7 @@
    if self.Segments[0][1] != 0:
    self.Points[1].y = (end.y + start.y) / 2
    self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
    - self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    + self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
    self.Segments[1][0]), v_end, self.EndPoint[1]))
    else:
    # The previous direction and the end direction are perpendiculars
    @@ -2093,7 +2093,7 @@
    self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
    else:
    test = True
    - # If the current point is the third, test if the second
    + # If the current point is the third, test if the second
    # point can be aligned with the end point
    if i == 2:
    test_point = wx.Point(self.Points[1].x, self.Points[1].y)
    @@ -2119,7 +2119,7 @@
    if self.Segments[1][1] != 0:
    self.Points[2].y = (self.Points[1].y + end.y) / 2
    self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y))
    - self.Segments.insert(3, DirectionChoice((self.Segments[2][1],
    + self.Segments.insert(3, DirectionChoice((self.Segments[2][1],
    self.Segments[2][0]), v_end, self.EndPoint[1]))
    else:
    # Current point is aligned with end point
    @@ -2140,15 +2140,15 @@
    self.Points[i].y = (end.y + self.Points[i - 1].y) / 2
    # A point is added
    self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y))
    - self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1],
    + self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1],
    self.Segments[i][0]), v_end, self.EndPoint[1]))
    else:
    # Current point is the first, and second is not mainly in the first direction
    if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0:
    - # If first and second directions aren't perpendiculars, a point is added
    + # If first and second directions aren't perpendiculars, a point is added
    if product(self.Segments[0], self.Segments[1]) != 0:
    self.Points.insert(1, wx.Point(start.x, start.y))
    - self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
    + self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
    self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1]))
    else:
    self.Points[1].x, self.Points[1].y = start.x, start.y
    @@ -2165,7 +2165,7 @@
    self.RefreshBoundingBox()
    if realpoints:
    self.RefreshRealPoints()
    -
    +
    # Verify that two consecutive points haven't the same position
    def VerifyPoints(self):
    points = [point for point in self.Points]
    @@ -2185,7 +2185,7 @@
    self.RefreshBoundingBox()
    self.RefreshRealPoints()
    return points
    -
    +
    # Moves all the wire points except the first and the last if they are connected
    def Move(self, dx, dy, endpoints = False):
    for i, point in enumerate(self.Points):
    @@ -2195,7 +2195,7 @@
    self.StartPoint[0] = self.Points[0]
    self.EndPoint[0] = self.Points[-1]
    self.GeneratePoints()
    -
    +
    # Resize the wire from position and size given
    def Resize(self, x, y, width, height):
    if len(self.Points) > 1:
    @@ -2254,25 +2254,25 @@
    self.StartPoint[0] = self.Points[0]
    self.EndPoint[0] = self.Points[-1]
    self.GeneratePoints(False)
    -
    +
    # Moves the wire start point and update the wire points
    def MoveStartPoint(self, point):
    if len(self.Points) > 1:
    self.StartPoint[0] = point
    self.Points[0] = point
    self.GeneratePoints()
    -
    +
    # Changes the wire start direction and update the wire points
    def SetStartPointDirection(self, dir):
    if len(self.Points) > 1:
    self.StartPoint[1] = dir
    self.Segments[0] = dir
    self.GeneratePoints()
    -
    +
    # Rotates the wire start direction by an angle of 90 degrees anticlockwise
    def RotateStartPoint(self):
    self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0]))
    -
    +
    # Connects wire start point to the connector given and moves wire start point
    # to given point
    def ConnectStartPoint(self, point, connector):
    @@ -2280,7 +2280,7 @@
    self.MoveStartPoint(point)
    self.StartConnected = connector
    self.RefreshBoundingBox()
    -
    +
    # Unconnects wire start point
    def UnConnectStartPoint(self, delete = False):
    if delete:
    @@ -2290,7 +2290,7 @@
    self.StartConnected.UnConnect(self, unconnect = False)
    self.StartConnected = None
    self.RefreshBoundingBox()
    -
    +
    # Moves the wire end point and update the wire points
    def MoveEndPoint(self, point):
    if len(self.Points) > 1:
    @@ -2303,7 +2303,7 @@
    if len(self.Points) > 1:
    self.EndPoint[1] = dir
    self.GeneratePoints()
    -
    +
    # Rotates the wire end direction by an angle of 90 degrees anticlockwise
    def RotateEndPoint(self):
    self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0]))
    @@ -2315,7 +2315,7 @@
    self.MoveEndPoint(point)
    self.EndConnected = connector
    self.RefreshBoundingBox()
    -
    +
    # Unconnects wire end point
    def UnConnectEndPoint(self, delete = False):
    if delete:
    @@ -2325,7 +2325,7 @@
    self.EndConnected.UnConnect(self, unconnect = False)
    self.EndConnected = None
    self.RefreshBoundingBox()
    -
    +
    # Moves the wire segment given by its index
    def MoveSegment(self, idx, movex, movey, scaling):
    if 0 < idx < len(self.Segments) - 1:
    @@ -2356,7 +2356,7 @@
    if start_y != self.Points[idx].y:
    return 0, self.Points[idx].y - start_y
    return 0, 0
    -
    +
    # Adds two points in the middle of the handled segment
    def AddSegment(self):
    handle_type, handle = self.Handle
    @@ -2391,7 +2391,7 @@
    self.Points.insert(segment + 4, wx.Point(p2x, p2y))
    self.Segments.insert(segment + 4, dir)
    self.GeneratePoints()
    -
    +
    # Delete the handled segment by removing the two segment points
    def DeleteSegment(self):
    handle_type, handle = self.Handle
    @@ -2402,7 +2402,7 @@
    self.Segments.pop(segment)
    self.GeneratePoints()
    self.RefreshModel()
    -
    +
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -2424,7 +2424,7 @@
    else:
    Graphic_Element.OnLeftDown(self, event, dc, scaling)
    self.oldPos = pos
    -
    +
    # Method called when a RightUp event has been generated
    def OnRightUp(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -2437,7 +2437,7 @@
    else:
    # Execute the default method for a graphic element
    Graphic_Element.OnRightUp(self, event, dc, scaling)
    -
    +
    # Method called when a LeftDClick event has been generated
    def OnLeftDClick(self, event, dc, scaling):
    rect = self.GetRedrawRect()
    @@ -2492,7 +2492,7 @@
    self.Parent.RefreshBuffer()
    rect.Union(self.GetRedrawRect())
    self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
    -
    +
    # Method called when a Motion event has been generated
    def OnMotion(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -2511,7 +2511,7 @@
    else:
    # Execute the default method for a graphic element
    return Graphic_Element.OnMotion(self, event, dc, scaling)
    -
    +
    # Refreshes the wire state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    handle_type, handle = self.Handle
    @@ -2572,14 +2572,14 @@
    # Execute the default method for a graphic element
    else:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    -
    +
    # Refreshes the wire model
    def RefreshModel(self, move=True):
    if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]:
    self.StartConnected.RefreshParentBlock()
    if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]:
    self.EndConnected.RefreshParentBlock()
    -
    +
    # Change the variable that indicates if this element is highlighted
    def SetHighlighted(self, highlighted):
    self.Highlighted = highlighted
    @@ -2587,7 +2587,7 @@
    self.OverStart = False
    self.OverEnd = False
    self.Refresh()
    -
    +
    def HighlightPoint(self, pos):
    refresh = False
    start, end = self.OverStart, self.OverEnd
    @@ -2602,7 +2602,7 @@
    self.OverEnd = True
    if start != self.OverStart or end != self.OverEnd:
    self.Refresh()
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -2617,31 +2617,31 @@
    dc.SetLogicalFunction(wx.AND)
    # Draw the start and end points if they are not connected or the mouse is over them
    if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
    - dc.DrawCircle(round(self.Points[0].x * scalex),
    - round(self.Points[0].y * scaley),
    + dc.DrawCircle(round(self.Points[0].x * scalex),
    + round(self.Points[0].y * scaley),
    (POINT_RADIUS + 1) * scalex + 2)
    if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
    dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2)
    # Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
    if len(self.Points) > 1:
    - points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex),
    + points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex),
    round((self.Points[0].y - self.Segments[0][1]) * scaley))]
    points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]])
    - points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex),
    + points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex),
    round((self.Points[-1].y + self.Segments[-1][1]) * scaley)))
    else:
    points = []
    dc.DrawLines(points)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    if self.StartConnected is not None:
    self.StartConnected.DrawHighlightment(dc)
    self.StartConnected.Draw(dc)
    if self.EndConnected is not None:
    self.EndConnected.DrawHighlightment(dc)
    self.EndConnected.Draw(dc)
    -
    +
    # Draws the wire lines and points
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -2750,7 +2750,7 @@
    self.Pos = wx.Point(0, 0)
    self.Size = wx.Size(0, 0)
    self.Highlights = []
    -
    +
    # Make a clone of this comment
    def Clone(self, parent, id = None, pos = None):
    comment = Comment(parent, self.Content, id)
    @@ -2758,19 +2758,19 @@
    comment.SetPosition(pos.x, pos.y)
    comment.SetSize(self.Size[0], self.Size[1])
    return comment
    -
    +
    # Method for keeping compatibility with others
    def Clean(self):
    pass
    -
    +
    # Delete this comment by calling the corresponding method
    def Delete(self):
    self.Parent.DeleteComment(self)
    -
    +
    # Refresh the comment bounding box
    def RefreshBoundingBox(self):
    self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    -
    +
    # Changes the comment size
    def SetSize(self, width, height):
    self.Size.SetWidth(width)
    @@ -2780,7 +2780,7 @@
    # Returns the comment size
    def GetSize(self):
    return self.Size.GetWidth(), self.Size.GetHeight()
    -
    +
    # Returns the comment minimum size
    def GetMinSize(self):
    dc = wx.ClientDC(self.Parent)
    @@ -2793,7 +2793,7 @@
    min_width = max(min_width, wordwidth)
    min_height = max(min_height, wordheight)
    return min_width + 20, min_height + 20
    -
    +
    # Changes the comment position
    def SetPosition(self, x, y):
    self.Pos.x = x
    @@ -2815,51 +2815,51 @@
    # Returns the comment position
    def GetPosition(self):
    return self.Pos.x, self.Pos.y
    -
    +
    # Moves the comment
    def Move(self, dx, dy, connected = True):
    self.Pos.x += dx
    self.Pos.y += dy
    self.RefreshBoundingBox()
    -
    +
    # Resizes the comment with the position and the size given
    def Resize(self, x, y, width, height):
    self.Move(x, y)
    self.SetSize(width, height)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the wire state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD":
    movex = movey = 0
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    -
    +
    # Refreshes the comment model
    def RefreshModel(self, move=True):
    self.Parent.RefreshCommentModel(self)
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the comment content
    self.Parent.EditCommentContent(self)
    -
    +
    # Adds an highlight to the comment
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "content":
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the comment
    def RemoveHighlight(self, infos, start, end, highlight_type):
    RemoveHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes all the highlights of one particular type from the comment
    def ClearHighlight(self, highlight_type=None):
    self.Highlights = ClearHighlights(self.Highlights, highlight_type)
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -2867,29 +2867,29 @@
    dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
    dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
    dc.SetLogicalFunction(wx.AND)
    -
    +
    left = (self.Pos.x - 1) * scalex - 2
    right = (self.Pos.x + self.Size[0] + 1) * scalex + 2
    top = (self.Pos.y - 1) * scaley - 2
    bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2
    angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2
    angle_right = (self.Pos.y + 9) * scaley - 2
    -
    +
    polygon = [wx.Point(left, top), wx.Point(angle_top, top),
    wx.Point(right, angle_right), wx.Point(right, bottom),
    wx.Point(left, bottom)]
    dc.DrawPolygon(polygon)
    -
    +
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Draws the comment and its content
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.WHITE_BRUSH)
    # Draws the comment shape
    - polygon = [wx.Point(self.Pos.x, self.Pos.y),
    + polygon = [wx.Point(self.Pos.x, self.Pos.y),
    wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
    wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10),
    wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]),
    @@ -2951,4 +2951,3 @@
    y += wordheight + 5
    if y + wordheight > self.Pos.y + self.Size[1] - 10:
    break
    -
    --- a/graphics/LD_Objects.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/LD_Objects.py Mon Aug 14 19:13:01 2017 +0300
    @@ -37,7 +37,7 @@
    """
    class LD_PowerRail(Graphic_Element):
    -
    +
    # Create a new power rail
    def __init__(self, parent, type, id=None, connectors=1):
    Graphic_Element.__init__(self, parent)
    @@ -47,12 +47,12 @@
    self.Id = id
    self.Extensions = [LD_LINE_SIZE / 2, LD_LINE_SIZE / 2]
    self.SetType(type, connectors)
    -
    +
    def Flush(self):
    for connector in self.Connectors:
    connector.Flush()
    self.Connectors = []
    -
    +
    # Make a clone of this LD_PowerRail
    def Clone(self, parent, id = None, pos = None):
    powerrail = LD_PowerRail(parent, self.Type, id)
    @@ -65,11 +65,11 @@
    for connector in self.Connectors:
    powerrail.Connectors.append(connector.Clone(powerrail))
    return powerrail
    -
    +
    def GetConnectorTranslation(self, element):
    return dict(zip([connector for connector in self.Connectors],
    [connector for connector in element.Connectors]))
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -80,7 +80,7 @@
    if connector.IsConnected():
    rect = rect.Union(connector.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Forbids to change the power rail size
    def SetSize(self, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    @@ -88,47 +88,47 @@
    else:
    Graphic_Element.SetSize(self, LD_POWERRAIL_WIDTH, height)
    self.RefreshConnectors()
    -
    +
    # Forbids to select a power rail
    def HitTest(self, pt, connectors=True):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) != None
    return False
    -
    +
    # Forbids to select a power rail
    def IsInSelection(self, rect):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    return Graphic_Element.IsInSelection(self, rect)
    return False
    -
    +
    # Deletes this power rail by calling the appropriate method
    def Delete(self):
    self.Parent.DeletePowerRail(self)
    -
    +
    # Unconnect all connectors
    def Clean(self):
    for connector in self.Connectors:
    connector.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the power rail bounding box
    def RefreshBoundingBox(self):
    self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    -
    +
    # Refresh the power rail size
    def RefreshSize(self):
    self.Size = wx.Size(LD_POWERRAIL_WIDTH, max(LD_LINE_SIZE * len(self.Connectors), self.Size[1]))
    self.RefreshBoundingBox()
    -
    +
    # Returns the block minimum size
    def GetMinSize(self, default=False):
    height = (LD_LINE_SIZE * (len(self.Connectors) - 1)
    if default else 0)
    return LD_POWERRAIL_WIDTH, height + self.Extensions[0] + self.Extensions[1]
    -
    +
    # Add a connector or a blank to this power rail at the last place
    def AddConnector(self):
    self.InsertConnector(len(self.Connectors))
    -
    +
    # Add a connector or a blank to this power rail at the place given
    def InsertConnector(self, idx):
    if self.Type == LEFTRAIL:
    @@ -138,7 +138,7 @@
    self.Connectors.insert(idx, connector)
    self.RefreshSize()
    self.RefreshConnectors()
    -
    +
    # Moves the divergence connector given
    def MoveConnector(self, connector, movey):
    position = connector.GetRelPosition()
    @@ -163,19 +163,19 @@
    self.Size[1] = max(maxy + self.Extensions[1], self.Size[1])
    connector.MoveConnected()
    self.RefreshBoundingBox()
    -
    +
    # Returns the index in connectors list for the connector given
    def GetConnectorIndex(self, connector):
    if connector in self.Connectors:
    return self.Connectors.index(connector)
    return None
    -
    +
    # Delete the connector or blank from connectors list at the index given
    def DeleteConnector(self, idx):
    self.Connectors.pop(idx)
    self.RefreshConnectors()
    self.RefreshSize()
    -
    +
    # Refresh the positions of the power rail connectors
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -193,13 +193,13 @@
    elif self.Type == RIGHTRAIL:
    connector.SetPosition(wx.Point(0, position))
    self.RefreshConnected()
    -
    +
    # Refresh the position of wires connected to power rail
    def RefreshConnected(self, exclude = []):
    for connector in self.Connectors:
    connector.MoveConnected(exclude)
    -
    - # Returns the power rail connector that starts with the point given if it exists
    +
    + # Returns the power rail connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -208,22 +208,22 @@
    if name == connector.GetName():
    return connector
    return self.FindNearestConnector(position, [connector for connector in self.Connectors if connector is not None])
    -
    - # Returns all the power rail connectors
    +
    + # Returns all the power rail connectors
    def GetConnectors(self):
    connectors = [connector for connector in self.Connectors if connector]
    if self.Type == LEFTRAIL:
    return {"inputs": [], "outputs": connectors}
    else:
    return {"inputs": connectors, "outputs": []}
    -
    +
    # Test if point given is on one of the power rail connectors
    def TestConnector(self, pt, direction = None, exclude = True):
    for connector in self.Connectors:
    if connector.TestPoint(pt, direction, exclude):
    return connector
    return None
    -
    +
    # Returns the power rail type
    def SetType(self, type, connectors):
    if type != self.Type or len(self.Connectors) != connectors:
    @@ -235,11 +235,11 @@
    for connector in xrange(connectors):
    self.AddConnector()
    self.RefreshSize()
    -
    +
    # Returns the power rail type
    def GetType(self):
    return self.Type
    -
    +
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
    self.RealConnectors = []
    @@ -253,12 +253,12 @@
    else:
    self.RealConnectors = [0.5]
    Graphic_Element.OnLeftDown(self, event, dc, scaling)
    -
    +
    # Method called when a LeftUp event have been generated
    def OnLeftUp(self, event, dc, scaling):
    Graphic_Element.OnLeftUp(self, event, dc, scaling)
    self.RealConnectors = None
    -
    +
    # Method called when a LeftDown event have been generated
    def OnRightDown(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -272,12 +272,12 @@
    self.oldPos = GetScaledEventPosition(event, dc, scaling)
    else:
    Graphic_Element.OnRightDown(self, event, dc, scaling)
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the powerrail properties
    self.Parent.EditPowerRailContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    handle_type, handle = self.Handle
    @@ -292,7 +292,7 @@
    Graphic_Element.OnRightUp(self, event, dc, scaling)
    else:
    self.Parent.PopupDefaultMenu()
    -
    +
    def Resize(self, x, y, width, height):
    self.Move(x, y)
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    @@ -314,16 +314,16 @@
    elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    return 0, 0
    -
    +
    # Refreshes the power rail model
    def RefreshModel(self, move=True):
    self.Parent.RefreshPowerRailModel(self)
    - # If power rail has moved and power rail is of type LEFT, refresh the model
    + # If power rail has moved and power rail is of type LEFT, refresh the model
    # of wires connected to connectors
    if move and self.Type == LEFTRAIL:
    for connector in self.Connectors:
    connector.RefreshWires()
    -
    +
    # Draws power rail
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -337,7 +337,7 @@
    # Draw connectors
    for connector in self.Connectors:
    connector.Draw(dc)
    -
    +
    #-------------------------------------------------------------------------------
    # Ladder Diagram Contact
    @@ -348,7 +348,7 @@
    """
    class LD_Contact(Graphic_Element, DebugDataConsumer):
    -
    +
    # Create a new contact
    def __init__(self, parent, type, name, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -365,7 +365,7 @@
    self.PreviousSpreading = False
    self.RefreshNameSize()
    self.RefreshTypeSize()
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    @@ -373,13 +373,13 @@
    if self.Output is not None:
    self.Output.Flush()
    self.Output = None
    -
    +
    def SetForced(self, forced):
    if self.Forced != forced:
    self.Forced = forced
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    def SetValue(self, value):
    if self.Type == CONTACT_RISING:
    refresh = self.Value and not self.PreviousValue
    @@ -393,7 +393,7 @@
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    self.SpreadCurrent()
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    if self.Value is None:
    @@ -414,7 +414,7 @@
    elif not spreading and self.PreviousSpreading:
    self.Output.SpreadCurrent(False)
    self.PreviousSpreading = spreading
    -
    +
    # Make a clone of this LD_Contact
    def Clone(self, parent, id = None, pos = None):
    contact = LD_Contact(parent, self.Type, self.Name, id)
    @@ -426,10 +426,10 @@
    contact.Input = self.Input.Clone(contact)
    contact.Output = self.Output.Clone(contact)
    return contact
    -
    +
    def GetConnectorTranslation(self, element):
    return {self.Input : element.Input, self.Output : element.Output}
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -446,29 +446,29 @@
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    movex = movey = 0
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2)
    -
    +
    # Forbids to change the contact size
    def SetSize(self, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.SetSize(self, width, height)
    self.RefreshConnectors()
    -
    +
    # Delete this contact by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteContact(self)
    -
    +
    # Unconnect input and output
    def Clean(self):
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    if self.Name != "":
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    else:
    self.NameSize = 0, 0
    -
    +
    # Refresh the size of text for type
    def RefreshTypeSize(self):
    typetext = ""
    @@ -482,7 +482,7 @@
    self.TypeSize = self.Parent.GetTextExtent(typetext)
    else:
    self.TypeSize = 0, 0
    -
    +
    # Refresh the contact bounding box
    def RefreshBoundingBox(self):
    # Calculate the size of the name outside the contact
    @@ -499,17 +499,17 @@
    bbx_y = self.Pos.y
    bbx_height = self.Size[1]
    self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
    -
    +
    # Returns the block minimum size
    def GetMinSize(self):
    return LD_ELEMENT_SIZE
    -
    +
    # Refresh the position of wire connected to contact
    def RefreshConnected(self, exclude = []):
    self.Input.MoveConnected(exclude)
    self.Output.MoveConnected(exclude)
    -
    - # Returns the contact connector that starts with the point given if it exists
    +
    + # Returns the contact connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -519,11 +519,11 @@
    if name == self.Output.GetName():
    return self.Output
    return self.FindNearestConnector(position, [self.Input, self.Output])
    -
    - # Returns input and output contact connectors
    +
    + # Returns input and output contact connectors
    def GetConnectors(self):
    return {"inputs": [self.Input], "outputs": [self.Output]}
    -
    +
    # Test if point given is on contact input or output connector
    def TestConnector(self, pt, direction = None, exclude=True):
    # Test input connector
    @@ -561,24 +561,24 @@
    # Returns the contact type
    def GetType(self):
    return self.Type
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the contact properties
    self.Parent.EditContactContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the contact model
    def RefreshModel(self, move=True):
    self.Parent.RefreshContactModel(self)
    # If contact has moved, refresh the model of wires connected to output
    if move:
    self.Output.RefreshWires()
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -592,12 +592,12 @@
    top = (self.Pos.y - 1) * scaley - 2
    width = 4 * scalex + 5
    height = (self.Size[1] + 3) * scaley + 5
    -
    +
    dc.DrawRectangle(left_left, top, width, height)
    dc.DrawRectangle(right_left, top, width, height)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Adds an highlight to the connection
    def AddHighlight(self, infos, start, end, highlight_type):
    highlights = self.Highlights.setdefault(infos[0], [])
    @@ -606,13 +606,13 @@
    AddHighlight(highlights, (start, end, highlight_type))
    else:
    AddHighlight(highlights, ((0, 0), (0, 1), highlight_type))
    -
    +
    # Removes an highlight from the connection
    def RemoveHighlight(self, infos, start, end, highlight_type):
    highlights = self.Highlights.get(infos[0], [])
    if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
    self.Highlights.pop(infos[0])
    -
    +
    # Removes all the highlights of one particular type from the connection
    def ClearHighlight(self, highlight_type=None):
    if highlight_type is None:
    @@ -623,11 +623,11 @@
    highlights = ClearHighlights(highlight, highlight_type)
    if len(highlights) == 0:
    self.Highlights.pop(name)
    -
    +
    # Draws contact
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    - if self.Value is not None:
    + if self.Value is not None:
    if self.Type == CONTACT_NORMAL and self.Value or \
    self.Type == CONTACT_REVERSE and not self.Value or \
    self.Type == CONTACT_RISING and self.Value and not self.PreviousValue or \
    @@ -643,7 +643,7 @@
    else:
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.BLACK_BRUSH)
    -
    +
    # Compiling contact type modifier symbol
    typetext = ""
    if self.Type == CONTACT_REVERSE:
    @@ -652,7 +652,7 @@
    typetext = "P"
    elif self.Type == CONTACT_FALLING:
    typetext = "N"
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    if typetext != "":
    @@ -661,7 +661,7 @@
    name_size = self.NameSize
    if typetext != "":
    type_size = self.TypeSize
    -
    +
    # Draw two rectangles for representing the contact
    dc.DrawRectangle(self.Pos.x, self.Pos.y, 2, self.Size[1] + 1)
    dc.DrawRectangle(self.Pos.x + self.Size[0] - 1, self.Pos.y, 2, self.Size[1] + 1)
    @@ -677,7 +677,7 @@
    # Draw input and output connectors
    self.Input.Draw(dc)
    self.Output.Draw(dc)
    -
    +
    if not getattr(dc, "printing", False):
    for name, highlights in self.Highlights.iteritems():
    if name == "reference":
    @@ -694,7 +694,7 @@
    """
    class LD_Coil(Graphic_Element):
    -
    +
    # Create a new coil
    def __init__(self, parent, type, name, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -710,7 +710,7 @@
    self.PreviousValue = False
    self.RefreshNameSize()
    self.RefreshTypeSize()
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    @@ -718,7 +718,7 @@
    if self.Output is not None:
    self.Output.Flush()
    self.Output = None
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    self.PreviousValue = self.Value
    @@ -729,7 +729,7 @@
    self.Output.SpreadCurrent(False)
    if self.Value != self.PreviousValue and self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    # Make a clone of this LD_Coil
    def Clone(self, parent, id = None, pos = None):
    coil = LD_Coil(parent, self.Type, self.Name, id)
    @@ -741,10 +741,10 @@
    coil.Input = self.Input.Clone(coil)
    coil.Output = self.Output.Clone(coil)
    return coil
    -
    +
    def GetConnectorTranslation(self, element):
    return {self.Input : element.Input, self.Output : element.Output}
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -756,34 +756,34 @@
    if self.Output.IsConnected():
    rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    def ProcessDragging(self, movex, movey, event, scaling):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    movex = movey = 0
    - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2)
    -
    + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2)
    +
    # Forbids to change the Coil size
    def SetSize(self, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.SetSize(self, width, height)
    self.RefreshConnectors()
    -
    +
    # Delete this coil by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteCoil(self)
    -
    +
    # Unconnect input and output
    def Clean(self):
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    if self.Name != "":
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    else:
    self.NameSize = 0, 0
    -
    +
    # Refresh the size of text for type
    def RefreshTypeSize(self):
    typetext = ""
    @@ -801,7 +801,7 @@
    self.TypeSize = self.Parent.GetTextExtent(typetext)
    else:
    self.TypeSize = 0, 0
    -
    +
    # Refresh the coil bounding box
    def RefreshBoundingBox(self):
    # Calculate the size of the name outside the coil
    @@ -818,17 +818,17 @@
    bbx_y = self.Pos.y
    bbx_height = self.Size[1]
    self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
    -
    +
    # Returns the block minimum size
    def GetMinSize(self):
    return LD_ELEMENT_SIZE
    -
    +
    # Refresh the position of wire connected to coil
    def RefreshConnected(self, exclude = []):
    self.Input.MoveConnected(exclude)
    self.Output.MoveConnected(exclude)
    -
    - # Returns the coil connector that starts with the point given if it exists
    +
    + # Returns the coil connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -838,11 +838,11 @@
    if self.Output and name == self.Output.GetName():
    return self.Output
    return self.FindNearestConnector(position, [self.Input, self.Output])
    -
    - # Returns input and output coil connectors
    +
    + # Returns input and output coil connectors
    def GetConnectors(self):
    return {"inputs": [self.Input], "outputs": [self.Output]}
    -
    +
    # Test if point given is on coil input or output connector
    def TestConnector(self, pt, direction = None, exclude=True):
    # Test input connector
    @@ -852,7 +852,7 @@
    if self.Output.TestPoint(pt, direction, exclude):
    return self.Output
    return None
    -
    +
    # Refresh the positions of the block connectors
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -862,7 +862,7 @@
    self.Input.SetPosition(wx.Point(0, position))
    self.Output.SetPosition(wx.Point(self.Size[0], position))
    self.RefreshConnected()
    -
    +
    # Changes the coil name
    def SetName(self, name):
    self.Name = name
    @@ -871,33 +871,33 @@
    # Returns the coil name
    def GetName(self):
    return self.Name
    -
    +
    # Changes the coil type
    def SetType(self, type):
    self.Type = type
    self.RefreshTypeSize()
    -
    +
    # Returns the coil type
    def GetType(self):
    return self.Type
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the coil properties
    self.Parent.EditCoilContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the coil model
    def RefreshModel(self, move=True):
    self.Parent.RefreshCoilModel(self)
    # If coil has moved, refresh the model of wires connected to output
    if move:
    self.Output.RefreshWires()
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -906,19 +906,19 @@
    dc.SetBrush(wx.TRANSPARENT_BRUSH)
    dc.SetLogicalFunction(wx.AND)
    # Draw a two circle arcs for representing the coil
    - dc.DrawEllipticArc(round(self.Pos.x * scalex),
    - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
    - round(self.Size[0] * scalex),
    + dc.DrawEllipticArc(round(self.Pos.x * scalex),
    + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
    + round(self.Size[0] * scalex),
    round((int(self.Size[1] * sqrt(2)) - 1) * scaley),
    135, 225)
    - dc.DrawEllipticArc(round(self.Pos.x * scalex),
    - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
    - round(self.Size[0] * scalex),
    + dc.DrawEllipticArc(round(self.Pos.x * scalex),
    + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
    + round(self.Size[0] * scalex),
    round((int(self.Size[1] * sqrt(2)) - 1) * scaley),
    -45, 45)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Adds an highlight to the connection
    def AddHighlight(self, infos, start, end, highlight_type):
    highlights = self.Highlights.setdefault(infos[0], [])
    @@ -927,13 +927,13 @@
    AddHighlight(highlights, (start, end, highlight_type))
    else:
    AddHighlight(highlights, ((0, 0), (0, 1), highlight_type))
    -
    +
    # Removes an highlight from the connection
    def RemoveHighlight(self, infos, start, end, highlight_type):
    highlights = self.Highlights.get(infos[0], [])
    if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
    self.Highlights.pop(infos[0])
    -
    +
    # Removes all the highlights of one particular type from the connection
    def ClearHighlight(self, highlight_type=None):
    if highlight_type is None:
    @@ -944,7 +944,7 @@
    highlights = ClearHighlights(highlight, highlight_type)
    if len(highlights) == 0:
    self.Highlights.pop(name)
    -
    +
    # Draws coil
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -953,8 +953,8 @@
    else:
    dc.SetPen(MiterPen(wx.BLACK, 2, wx.SOLID))
    dc.SetBrush(wx.TRANSPARENT_BRUSH)
    -
    - # Compiling coil type modifier symbol
    +
    + # Compiling coil type modifier symbol
    typetext = ""
    if self.Type == COIL_REVERSE:
    typetext = "/"
    @@ -966,7 +966,7 @@
    typetext = "P"
    elif self.Type == COIL_FALLING:
    typetext = "N"
    -
    +
    if getattr(dc, "printing", False) and not isinstance(dc, wx.PostScriptDC):
    # Draw an clipped ellipse for representing the coil
    clipping_box = dc.GetClippingBox()
    @@ -992,7 +992,7 @@
    name_size = self.NameSize
    if typetext != "":
    type_size = self.TypeSize
    -
    +
    # Draw coil name
    name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
    self.Pos.y - (name_size[1] + 2))
    @@ -1012,5 +1012,3 @@
    DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1])
    elif typetext != "":
    DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1])
    -
    -
    --- a/graphics/RubberBand.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/RubberBand.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,19 +35,19 @@
    """
    class RubberBand:
    -
    +
    def __init__(self, viewer):
    """
    Constructor
    @param viewer: Viewer on which rubberband must be drawn
    """
    self.Viewer = viewer
    -
    +
    # wx.Panel on which rubberband will be drawn
    self.DrawingSurface = viewer.Editor
    -
    +
    self.Reset()
    -
    +
    def Reset(self):
    """
    Initialize internal attributes of rubberband
    @@ -55,14 +55,14 @@
    self.StartPoint = None
    self.CurrentBBox = None
    self.LastBBox = None
    -
    +
    def IsShown(self):
    """
    Indicate if rubberband is drawn on viewer
    @return: True if rubberband is drawn
    """
    return self.CurrentBBox != None
    -
    +
    def GetCurrentExtent(self):
    """
    Return the rubberband bounding box
    @@ -73,7 +73,7 @@
    if self.IsShown():
    return self.CurrentBBox
    return self.LastBBox
    -
    +
    def OnLeftDown(self, event, dc, scaling):
    """
    Called when left mouse is pressed on Viewer. Starts to edit a new
    @@ -85,16 +85,16 @@
    # Save the point where mouse was pressed in Viewer unit, position may
    # be modified by scroll and zoom applied on viewer
    self.StartPoint = GetScaledEventPosition(event, dc, scaling)
    -
    +
    # Initialize rubberband bounding box
    self.CurrentBBox = wx.Rect(self.StartPoint.x, self.StartPoint.y, 0, 0)
    -
    +
    # Change viewer mouse cursor to reflect a rubberband bounding box is
    # edited
    self.DrawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
    -
    +
    self.Redraw()
    -
    +
    def OnMotion(self, event, dc, scaling):
    """
    Called when mouse is dragging over Viewer. Update the current edited
    @@ -106,19 +106,19 @@
    # Get mouse position in Viewer unit, position may be modified by scroll
    # and zoom applied on viewer
    pos = GetScaledEventPosition(event, dc, scaling)
    -
    +
    # Save the last bounding box drawn for erasing it later
    self.LastBBox = wx.Rect(0, 0, 0, 0)
    self.LastBBox.Union(self.CurrentBBox)
    -
    - # Calculate new position and size of the box
    +
    + # Calculate new position and size of the box
    self.CurrentBBox.x = min(pos.x, self.StartPoint.x)
    self.CurrentBBox.y = min(pos.y, self.StartPoint.y)
    self.CurrentBBox.width = abs(pos.x - self.StartPoint.x) + 1
    self.CurrentBBox.height = abs(pos.y - self.StartPoint.y) + 1
    -
    +
    self.Redraw()
    -
    +
    def OnLeftUp(self, event, dc, scaling):
    """
    Called when mouse is release from Viewer. Erase the current edited
    @@ -129,16 +129,16 @@
    """
    # Change viewer mouse cursor to default
    self.DrawingSurface.SetCursor(wx.NullCursor)
    -
    +
    # Save the last edited bounding box
    self.LastBBox = self.CurrentBBox
    self.CurrentBBox = None
    -
    +
    self.Redraw()
    -
    +
    def DrawBoundingBoxes(self, bboxes, dc=None):
    """
    - Draw a list of bounding box on Viewer in the order given using XOR
    + Draw a list of bounding box on Viewer in the order given using XOR
    logical function
    @param bboxes: List of bounding boxes to draw on viewer
    @param dc: Device Context of Viewer (default None)
    @@ -146,29 +146,29 @@
    # Get viewer Device Context if not given
    if dc is None:
    dc = self.Viewer.GetLogicalDC()
    -
    +
    # Save current viewer scale factors before resetting them in order to
    # avoid rubberband pen to be scaled
    scalex, scaley = dc.GetUserScale()
    dc.SetUserScale(1, 1)
    -
    +
    # Set DC drawing style
    dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT))
    dc.SetBrush(wx.TRANSPARENT_BRUSH)
    dc.SetLogicalFunction(wx.XOR)
    -
    +
    # Draw the bounding boxes using viewer scale factor
    for bbox in bboxes:
    if bbox is not None:
    dc.DrawRectangle(
    - bbox.x * scalex, bbox.y * scaley,
    + bbox.x * scalex, bbox.y * scaley,
    bbox.width * scalex, bbox.height * scaley)
    -
    +
    dc.SetLogicalFunction(wx.COPY)
    -
    +
    # Restore Viewer scale factor
    dc.SetUserScale(scalex, scaley)
    -
    +
    def Redraw(self, dc = None):
    """
    Redraw rubberband on Viewer
    @@ -176,7 +176,7 @@
    """
    # Erase last bbox and draw current bbox
    self.DrawBoundingBoxes([self.LastBBox, self.CurrentBBox], dc)
    -
    +
    def Erase(self, dc = None):
    """
    Erase rubberband from Viewer
    --- a/graphics/SFC_Objects.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/SFC_Objects.py Mon Aug 14 19:13:01 2017 +0300
    @@ -43,7 +43,7 @@
    """
    class SFC_Step(Graphic_Element, DebugDataConsumer):
    -
    +
    # Create a new step
    def __init__(self, parent, name, initial = False, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -62,7 +62,7 @@
    self.Action = None
    self.PreviousValue = None
    self.PreviousSpreading = False
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    @@ -73,13 +73,13 @@
    if self.Action is not None:
    self.Action.Flush()
    self.Action = None
    -
    +
    def SetForced(self, forced):
    if self.Forced != forced:
    self.Forced = forced
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    def SetValue(self, value):
    self.PreviousValue = self.Value
    self.Value = value
    @@ -87,7 +87,7 @@
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    self.SpreadCurrent()
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    spreading = self.Value
    @@ -102,7 +102,7 @@
    if self.Action is not None:
    self.Action.SpreadCurrent(False)
    self.PreviousSpreading = spreading
    -
    +
    # Make a clone of this SFC_Step
    def Clone(self, parent, id = None, name = "Step", pos = None):
    step = SFC_Step(parent, name, self.Initial, id)
    @@ -118,7 +118,7 @@
    if self.Action:
    step.Action = self.Action.Clone(step)
    return step
    -
    +
    def GetConnectorTranslation(self, element):
    connectors = {}
    if self.Input is not None:
    @@ -128,7 +128,7 @@
    if self.Action is not None:
    connectors[self.Action] = element.Action
    return connectors
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -146,11 +146,11 @@
    if self.Action and self.Action.IsConnected():
    rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Delete this step by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteStep(self)
    -
    +
    # Unconnect input and output
    def Clean(self):
    if self.Input:
    @@ -159,50 +159,50 @@
    self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    if self.Action:
    self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the size of text for name
    def RefreshNameSize(self):
    self.NameSize = self.Parent.GetTextExtent(self.Name)
    -
    +
    # Add output connector to step
    def AddInput(self):
    if not self.Input:
    self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH)
    self.RefreshBoundingBox()
    -
    +
    # Remove output connector from step
    def RemoveInput(self):
    if self.Input:
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Input = None
    self.RefreshBoundingBox()
    -
    +
    # Add output connector to step
    def AddOutput(self):
    if not self.Output:
    self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)
    self.RefreshBoundingBox()
    -
    +
    # Remove output connector from step
    def RemoveOutput(self):
    if self.Output:
    self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Output = None
    self.RefreshBoundingBox()
    -
    +
    # Add action connector to step
    def AddAction(self):
    if not self.Action:
    self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone = True)
    self.RefreshBoundingBox()
    -
    +
    # Remove action connector from step
    def RemoveAction(self):
    if self.Action:
    self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Action = None
    self.RefreshBoundingBox()
    -
    +
    # Refresh the step bounding box
    def RefreshBoundingBox(self):
    # Calculate the bounding box size
    @@ -222,7 +222,7 @@
    bbx_height += CONNECTOR_SIZE
    #self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1)
    self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    -
    +
    # Refresh the positions of the step connectors
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -241,7 +241,7 @@
    if self.Action:
    self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos))
    self.RefreshConnected()
    -
    +
    # Refresh the position of wires connected to step
    def RefreshConnected(self, exclude = []):
    if self.Input:
    @@ -250,8 +250,8 @@
    self.Output.MoveConnected(exclude)
    if self.Action:
    self.Action.MoveConnected(exclude)
    -
    - # Returns the step connector that starts with the point given if it exists
    +
    + # Returns the step connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -273,12 +273,12 @@
    if self.Action:
    connectors.append(self.Action)
    return self.FindNearestConnector(position, connectors)
    -
    - # Returns action step connector
    +
    + # Returns action step connector
    def GetActionConnector(self):
    return self.Action
    -
    - # Returns input and output step connectors
    +
    + # Returns input and output step connectors
    def GetConnectors(self):
    connectors = {"inputs": [], "outputs": []}
    if self.Input:
    @@ -286,7 +286,7 @@
    if self.Output:
    connectors["outputs"].append(self.Output)
    return connectors
    -
    +
    # Test if point given is on step input or output connector
    def TestConnector(self, pt, direction = None, exclude=True):
    # Test input connector if it exists
    @@ -312,7 +312,7 @@
    # Returns the step initial property
    def GetInitial(self):
    return self.Initial
    -
    +
    # Returns the connector connected to input
    def GetPreviousConnector(self):
    if self.Input:
    @@ -320,7 +320,7 @@
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Input)
    return None
    -
    +
    # Returns the connector connected to output
    def GetNextConnector(self):
    if self.Output:
    @@ -328,7 +328,7 @@
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Output)
    return None
    -
    +
    # Returns the connector connected to action
    def GetActionConnected(self):
    if self.Action:
    @@ -336,7 +336,7 @@
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Action)
    return None
    -
    +
    # Returns the number of action line
    def GetActionExtraLineNumber(self):
    if self.Action:
    @@ -346,7 +346,7 @@
    action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
    return max(0, action_block.GetLineNumber() - 1)
    return 0
    -
    +
    # Returns the step minimum size
    def GetMinSize(self):
    text_width, text_height = self.Parent.GetTextExtent(self.Name)
    @@ -354,7 +354,7 @@
    return text_width + 14, text_height + 14
    else:
    return text_width + 10, text_height + 10
    -
    +
    # Updates the step size
    def UpdateSize(self, width, height):
    diffx = self.Size.GetWidth() / 2 - width / 2
    @@ -365,7 +365,7 @@
    self.RefreshConnected()
    else:
    self.RefreshOutputPosition((0, diffy))
    -
    +
    # Align input element with this step
    def RefreshInputPosition(self):
    if self.Input:
    @@ -382,7 +382,7 @@
    input_block.MoveActionBlock((diffx, 0))
    input_block.Move(diffx, 0)
    input_block.RefreshInputPosition()
    -
    +
    # Align output element with this step
    def RefreshOutputPosition(self, move = None):
    if self.Output:
    @@ -422,7 +422,7 @@
    output_block.MoveActionBlock((diffx, 0))
    output_block.Move(diffx, 0)
    output_block.RefreshOutputPosition()
    -
    +
    # Refresh action element with this step
    def MoveActionBlock(self, move):
    if self.Action:
    @@ -432,24 +432,24 @@
    action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
    action_block.Move(move[0], move[1], self.Parent.Wires)
    wires[0][0].Move(move[0], move[1], True)
    -
    +
    # Resize the divergence from position and size given
    def Resize(self, x, y, width, height):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    self.UpdateSize(width, height)
    else:
    Graphic_Element.Resize(self, x, y, width, height)
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the step properties
    self.Parent.EditStepContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the menu with special items for a step
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the step state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    handle_type, handle = self.Handle
    @@ -477,17 +477,17 @@
    return movex, 0
    else:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    -
    +
    # Refresh input element model
    def RefreshInputModel(self):
    if self.Input:
    input = self.GetPreviousConnector()
    - if input:
    + if input:
    input_block = input.GetParentBlock()
    input_block.RefreshModel(False)
    if not isinstance(input_block, SFC_Divergence):
    input_block.RefreshInputModel()
    -
    +
    # Refresh output element model
    def RefreshOutputModel(self, move=False):
    if self.Output:
    @@ -497,7 +497,7 @@
    output_block.RefreshModel(False)
    if not isinstance(output_block, SFC_Divergence) or move:
    output_block.RefreshOutputModel(move)
    -
    +
    # Refreshes the step model
    def RefreshModel(self, move=True):
    self.Parent.RefreshStepModel(self)
    @@ -513,21 +513,21 @@
    self.RefreshOutputModel(self.Initial)
    elif self.Output:
    self.Output.RefreshWires()
    -
    +
    # Adds an highlight to the connection
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "name" and start[0] == 0 and end[0] == 0:
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the connection
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "name":
    RemoveHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes all the highlights of one particular type from the connection
    def ClearHighlight(self, highlight_type=None):
    ClearHighlights(self.Highlights, highlight_type)
    -
    +
    # Draws step
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -541,12 +541,12 @@
    else:
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.WHITE_BRUSH)
    -
    +
    if getattr(dc, "printing", False):
    name_size = dc.GetTextExtent(self.Name)
    else:
    name_size = self.NameSize
    -
    +
    # Draw two rectangles for representing the step
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    if self.Initial:
    @@ -562,10 +562,10 @@
    self.Output.Draw(dc)
    if self.Action:
    self.Action.Draw(dc)
    -
    +
    if not getattr(dc, "printing", False):
    DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1])
    -
    +
    #-------------------------------------------------------------------------------
    # Sequencial Function Chart Transition
    @@ -576,7 +576,7 @@
    """
    class SFC_Transition(Graphic_Element, DebugDataConsumer):
    -
    +
    # Create a new transition
    def __init__(self, parent, type = "reference", condition = None, priority = 0, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -593,7 +593,7 @@
    self.Highlights = {}
    self.PreviousValue = None
    self.PreviousSpreading = False
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    @@ -604,13 +604,13 @@
    if self.Type == "connection" and self.Condition is not None:
    self.Condition.Flush()
    self.Condition = None
    -
    +
    def SetForced(self, forced):
    if self.Forced != forced:
    self.Forced = forced
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    def SetValue(self, value):
    self.PreviousValue = self.Value
    self.Value = value
    @@ -618,7 +618,7 @@
    if self.Visible:
    self.Parent.ElementNeedRefresh(self)
    self.SpreadCurrent()
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    if self.Value is None:
    @@ -629,7 +629,7 @@
    elif not spreading and self.PreviousSpreading:
    self.Output.SpreadCurrent(False)
    self.PreviousSpreading = spreading
    -
    +
    # Make a clone of this SFC_Transition
    def Clone(self, parent, id = None, pos = None):
    transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id)
    @@ -643,13 +643,13 @@
    if self.Type == "connection":
    transition.Condition = self.Condition.Clone(transition)
    return transition
    -
    +
    def GetConnectorTranslation(self, element):
    connectors = {self.Input : element.Input, self.Output : element.Output}
    if self.Type == "connection" and self.Condition is not None:
    connectors[self.Condition] = element.Condition
    return connectors
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -665,17 +665,17 @@
    if self.Type == "connection" and self.Condition.IsConnected():
    rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Forbids to change the transition size
    def SetSize(self, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.SetSize(self, width, height)
    -
    +
    # Forbids to resize the transition
    def Resize(self, x, y, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.Resize(self, x, y, width, height)
    -
    +
    # Refresh the size of text for name
    def RefreshConditionSize(self):
    if self.Type != "connection":
    @@ -683,7 +683,7 @@
    self.ConditionSize = self.Parent.GetTextExtent(self.Condition)
    else:
    self.ConditionSize = self.Parent.GetTextExtent("Transition")
    -
    +
    # Refresh the size of text for name
    def RefreshPrioritySize(self):
    if self.Priority != "":
    @@ -694,14 +694,14 @@
    # Delete this transition by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteTransition(self)
    -
    +
    # Unconnect input and output
    def Clean(self):
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    if self.Type == "connection":
    self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Returns if the point given is in the bounding box
    def HitTest(self, pt, connectors=True):
    if self.Type != "connection":
    @@ -715,7 +715,7 @@
    else:
    test_text = False
    return test_text or Graphic_Element.HitTest(self, pt, connectors)
    -
    +
    # Refresh the transition bounding box
    def RefreshBoundingBox(self):
    bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]
    @@ -733,21 +733,21 @@
    bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2))
    bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2)
    self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
    -
    +
    # Returns the connector connected to input
    def GetPreviousConnector(self):
    wires = self.Input.GetWires()
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Input)
    return None
    -
    +
    # Returns the connector connected to output
    def GetNextConnector(self):
    wires = self.Output.GetWires()
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Output)
    return None
    -
    +
    # Refresh the positions of the transition connectors
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -763,15 +763,15 @@
    if self.Type == "connection":
    self.Condition.SetPosition(wx.Point(0, vertical_pos))
    self.RefreshConnected()
    -
    +
    # Refresh the position of the wires connected to transition
    def RefreshConnected(self, exclude = []):
    self.Input.MoveConnected(exclude)
    self.Output.MoveConnected(exclude)
    if self.Type == "connection":
    self.Condition.MoveConnected(exclude)
    -
    - # Returns the transition connector that starts with the point given if it exists
    +
    + # Returns the transition connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -786,17 +786,17 @@
    if self.Type == "connection":
    connectors.append(self.Condition)
    return self.FindNearestConnector(position, connectors)
    -
    +
    # Returns the transition condition connector
    def GetConditionConnector(self):
    if self.Type == "connection":
    return self.Condition
    return None
    -
    +
    # Returns input and output transition connectors
    def GetConnectors(self):
    return {"inputs": [self.Input], "outputs": [self.Output]}
    -
    +
    # Test if point given is on transition input or output connector
    def TestConnector(self, pt, direction = None, exclude=True):
    # Test input connector
    @@ -814,7 +814,7 @@
    def SetType(self, type, condition = None):
    if self.Type != type:
    if self.Type == "connection":
    - self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    + self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    self.Type = type
    if type == "connection":
    self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST)
    @@ -829,7 +829,7 @@
    self.Condition = condition
    self.RefreshConditionSize()
    self.RefreshBoundingBox()
    -
    +
    # Returns the transition type
    def GetType(self):
    return self.Type
    @@ -839,7 +839,7 @@
    self.Priority = priority
    self.RefreshPrioritySize()
    self.RefreshBoundingBox()
    -
    +
    # Returns the transition type
    def GetPriority(self):
    return self.Priority
    @@ -849,11 +849,11 @@
    if self.Type != "connection":
    return self.Condition
    return None
    -
    +
    # Returns the transition minimum size
    def GetMinSize(self):
    return SFC_TRANSITION_SIZE
    -
    +
    # Align input element with this step
    def RefreshInputPosition(self):
    wires = self.Input.GetWires()
    @@ -870,7 +870,7 @@
    input_block.MoveActionBlock((diffx, 0))
    input_block.Move(diffx, 0)
    input_block.RefreshInputPosition()
    -
    +
    # Align output element with this step
    def RefreshOutputPosition(self, move = None):
    wires = self.Output.GetWires()
    @@ -902,12 +902,12 @@
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the transition properties
    self.Parent.EditTransitionContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the menu with special items for a step
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the transition state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -920,7 +920,7 @@
    return movex, 0
    else:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2, height_fac = 2)
    -
    +
    # Refresh input element model
    def RefreshInputModel(self):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -930,7 +930,7 @@
    input_block.RefreshModel(False)
    if not isinstance(input_block, SFC_Divergence):
    input_block.RefreshInputModel()
    -
    +
    # Refresh output element model
    def RefreshOutputModel(self, move=False):
    output = self.GetNextConnector()
    @@ -939,7 +939,7 @@
    output_block.RefreshModel(False)
    if not isinstance(output_block, SFC_Divergence) or move:
    output_block.RefreshOutputModel(move)
    -
    +
    # Refreshes the transition model
    def RefreshModel(self, move=True):
    self.Parent.RefreshTransitionModel(self)
    @@ -950,20 +950,20 @@
    self.RefreshOutputModel()
    else:
    self.Output.RefreshWires()
    -
    +
    # Adds an highlight to the block
    def AddHighlight(self, infos, start, end ,highlight_type):
    if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0:
    highlights = self.Highlights.setdefault(infos[0], [])
    AddHighlight(highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the block
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] in ["reference", "inline", "priority"]:
    highlights = self.Highlights.get(infos[0], [])
    if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
    self.Highlights.pop(infos[0])
    -
    +
    # Removes all the highlights of one particular type from the block
    def ClearHighlight(self, highlight_type=None):
    if highlight_type is None:
    @@ -974,7 +974,7 @@
    highlights = ClearHighlights(highlight, highlight_type)
    if len(highlights) == 0:
    self.Highlights.pop(name)
    -
    +
    # Draws transition
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -991,7 +991,7 @@
    else:
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.BLACK_BRUSH)
    -
    +
    if getattr(dc, "printing", False):
    if self.Type != "connection":
    condition_size = dc.GetTextExtent(self.Condition)
    @@ -1002,14 +1002,14 @@
    condition_size = self.ConditionSize
    if self.Priority != 0:
    priority_size = self.PrioritySize
    -
    +
    # Draw plain rectangle for representing the transition
    - dc.DrawRectangle(self.Pos.x,
    - self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2,
    + dc.DrawRectangle(self.Pos.x,
    + self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2,
    self.Size[0] + 1,
    SFC_TRANSITION_SIZE[1] + 1)
    vertical_line_x = self.Input.GetPosition()[0]
    - dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1)
    + dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1)
    # Draw transition condition
    if self.Type != "connection":
    if self.Condition != "":
    @@ -1028,7 +1028,7 @@
    self.Output.Draw(dc)
    if self.Type == "connection":
    self.Condition.Draw(dc)
    -
    +
    if not getattr(dc, "printing", False):
    for name, highlights in self.Highlights.iteritems():
    if name == "priority":
    @@ -1046,7 +1046,7 @@
    """
    class SFC_Divergence(Graphic_Element):
    -
    +
    # Create a new divergence
    def __init__(self, parent, type, number = 2, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -1068,7 +1068,7 @@
    self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)]
    self.Value = None
    self.PreviousValue = None
    -
    +
    def Flush(self):
    for input in self.Inputs:
    input.Flush()
    @@ -1076,7 +1076,7 @@
    for output in self.Outputs:
    output.Flush()
    self.Outputs = []
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    self.PreviousValue = self.Value
    @@ -1102,7 +1102,7 @@
    self.Parent.ElementNeedRefresh(self)
    for output in self.Outputs:
    output.SpreadCurrent(False)
    -
    +
    # Make a clone of this SFC_Divergence
    def Clone(self, parent, id = None, pos = None):
    divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id)
    @@ -1114,10 +1114,10 @@
    divergence.Inputs = [input.Clone(divergence) for input in self.Inputs]
    divergence.Outputs = [output.Clone(divergence) for output in self.Outputs]
    return divergence
    -
    +
    def GetConnectorTranslation(self, element):
    return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -1129,27 +1129,27 @@
    if output.IsConnected():
    rect = rect.Union(output.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Forbids to resize the divergence
    def Resize(self, x, y, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1])
    -
    +
    # Delete this divergence by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteDivergence(self)
    -
    +
    # Returns the divergence type
    def GetType(self):
    return self.Type
    -
    +
    # Unconnect input and output
    def Clean(self):
    for input in self.Inputs:
    input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    for output in self.Outputs:
    output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Add a branch to the divergence
    def AddBranch(self):
    if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
    @@ -1168,7 +1168,7 @@
    connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True)
    self.Inputs.append(connector)
    self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL)
    -
    +
    # Remove a branch from the divergence
    def RemoveBranch(self, connector):
    if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
    @@ -1179,41 +1179,41 @@
    if connector in self.Inputs and len(self.Inputs) > 2:
    self.Inputs.remove(connector)
    self.MoveConnector(self.Inputs[0], 0)
    -
    +
    # Remove the handled branch from the divergence
    def RemoveHandledBranch(self):
    handle_type, handle = self.Handle
    if handle_type == HANDLE_CONNECTOR:
    handle.UnConnect(delete=True)
    self.RemoveBranch(handle)
    -
    +
    # Return the number of branches for the divergence
    def GetBranchNumber(self):
    if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
    return len(self.Outputs)
    elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
    return len(self.Inputs)
    -
    +
    # Returns if the point given is in the bounding box
    def HitTest(self, pt, connectors=True):
    return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None
    -
    +
    # Refresh the divergence bounding box
    def RefreshBoundingBox(self):
    if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
    - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y,
    + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y,
    self.Size[0] + 1, self.Size[1] + 1)
    elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
    - self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
    + self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
    self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1)
    -
    +
    # Refresh the position of wires connected to divergence
    def RefreshConnected(self, exclude = []):
    for input in self.Inputs:
    input.MoveConnected(exclude)
    for output in self.Outputs:
    output.MoveConnected(exclude)
    -
    +
    # Moves the divergence connector given
    def MoveConnector(self, connector, movex):
    position = connector.GetRelPosition()
    @@ -1241,8 +1241,8 @@
    self.Size[0] = maxx - minx
    connector.MoveConnected()
    self.RefreshBoundingBox()
    -
    - # Returns the divergence connector that starts with the point given if it exists
    +
    + # Returns the divergence connector that starts with the point given if it exists
    def GetConnector(self, position, name = None):
    # if a name is given
    if name is not None:
    @@ -1254,11 +1254,11 @@
    if name == output.GetName():
    return output
    return self.FindNearestConnector(position, self.Inputs + self.Outputs)
    -
    - # Returns input and output divergence connectors
    +
    + # Returns input and output divergence connectors
    def GetConnectors(self):
    return {"inputs": self.Inputs, "outputs": self.Outputs}
    -
    +
    # Test if point given is on divergence input or output connector
    def TestConnector(self, pt, direction = None, exclude=True):
    # Test input connector
    @@ -1270,7 +1270,7 @@
    if output.TestPoint(pt, direction, exclude):
    return output
    return None
    -
    +
    # Changes the divergence size
    def SetSize(self, width, height):
    height = self.GetMinSize()[1]
    @@ -1290,7 +1290,7 @@
    output.MoveConnected()
    self.Size = wx.Size(width, height)
    self.RefreshBoundingBox()
    -
    +
    # Returns the divergence minimum size
    def GetMinSize(self, default=False):
    width = 0
    @@ -1304,7 +1304,7 @@
    elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
    return width, 3
    return 0, 0
    -
    +
    # Refresh the position of the block connected to connector
    def RefreshConnectedPosition(self, connector):
    wires = connector.GetWires()
    @@ -1340,7 +1340,7 @@
    self.RefreshOutputPosition((0, diffy))
    for input in self.Inputs:
    input.MoveConnected()
    -
    +
    # Align output element with this divergence
    def RefreshOutputPosition(self, move = None):
    if move:
    @@ -1359,7 +1359,7 @@
    if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
    output_block.Move(move[0], move[1], self.Parent.Wires)
    output_block.RefreshOutputPosition(move)
    -
    +
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
    self.RealConnectors = {"Inputs":[],"Outputs":[]}
    @@ -1370,12 +1370,12 @@
    position = output.GetRelPosition()
    self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0]))
    Graphic_Element.OnLeftDown(self, event, dc, scaling)
    -
    +
    # Method called when a LeftUp event have been generated
    def OnLeftUp(self, event, dc, scaling):
    Graphic_Element.OnLeftUp(self, event, dc, scaling)
    self.RealConnectors = None
    -
    +
    # Method called when a RightDown event have been generated
    def OnRightDown(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -1389,7 +1389,7 @@
    self.oldPos = GetScaledEventPosition(event, dc, scaling)
    else:
    Graphic_Element.OnRightDown(self, event, dc, scaling)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    pos = GetScaledEventPosition(event, dc, scaling)
    @@ -1414,7 +1414,7 @@
    else:
    # Popup the divergence menu without delete branch
    self.Parent.PopupDivergenceMenu(False)
    -
    +
    # Refreshes the divergence state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    handle_type, handle = self.Handle
    @@ -1430,7 +1430,7 @@
    elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    return 0, 0
    -
    +
    # Refresh output element model
    def RefreshOutputModel(self, move=False):
    if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -1442,7 +1442,7 @@
    output_block.RefreshModel(False)
    if not isinstance(output_block, SFC_Divergence) or move:
    output_block.RefreshOutputModel(move)
    -
    +
    # Refreshes the divergence model
    def RefreshModel(self, move=True):
    self.Parent.RefreshDivergenceModel(self)
    @@ -1453,7 +1453,7 @@
    else:
    for output in self.Outputs:
    output.RefreshWires()
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -1467,13 +1467,13 @@
    if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
    posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA
    width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2
    - dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2,
    - int(round((self.Pos.y - 1) * scaley)) - 2,
    - int(round((width + 3) * scalex)) + 5,
    + dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2,
    + int(round((self.Pos.y - 1) * scaley)) - 2,
    + int(round((width + 3) * scalex)) + 5,
    int(round((self.Size.height + 3) * scaley)) + 5)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Draws divergence
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -1487,16 +1487,16 @@
    if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
    - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
    + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
    self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y)
    - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1],
    + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1],
    self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1])
    # Draw inputs and outputs connectors
    for input in self.Inputs:
    input.Draw(dc)
    for output in self.Outputs:
    output.Draw(dc)
    -
    +
    #-------------------------------------------------------------------------------
    # Sequencial Function Chart Jump to Step
    @@ -1507,7 +1507,7 @@
    """
    class SFC_Jump(Graphic_Element):
    -
    +
    # Create a new jump
    def __init__(self, parent, target, id = None):
    Graphic_Element.__init__(self, parent)
    @@ -1519,19 +1519,19 @@
    self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
    self.Value = None
    self.PreviousValue = None
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    self.Input = None
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    self.PreviousValue = self.Value
    self.Value = self.Input.ReceivingCurrent()
    if self.Value != self.PreviousValue and self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    # Make a clone of this SFC_Jump
    def Clone(self, parent, id = None, pos = None):
    jump = SFC_Jump(parent, self.Target, id)
    @@ -1542,10 +1542,10 @@
    jump.SetPosition(self.Pos.x, self.Pos.y)
    jump.Input = self.Input.Clone(jump)
    return jump
    -
    +
    def GetConnectorTranslation(self, element):
    return {self.Input : element.Input}
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -1555,29 +1555,29 @@
    if self.Input.IsConnected():
    rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Forbids to change the jump size
    def SetSize(self, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.SetSize(self, width, height)
    -
    +
    # Forbids to resize jump
    def Resize(self, x, y, width, height):
    if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
    Graphic_Element.Resize(self, x, y, width, height)
    -
    +
    # Delete this jump by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteJump(self)
    -
    +
    # Unconnect input
    def Clean(self):
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the size of text for target
    def RefreshTargetSize(self):
    self.TargetSize = self.Parent.GetTextExtent(self.Target)
    -
    +
    # Returns if the point given is in the bounding box
    def HitTest(self, pt, connectors=True):
    # Calculate the bounding box of the condition outside the transition
    @@ -1587,22 +1587,22 @@
    text_width,
    text_height)
    return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors)
    -
    +
    # Refresh the jump bounding box
    def RefreshBoundingBox(self):
    text_width, text_height = self.Parent.GetTextExtent(self.Target)
    # Calculate the bounding box size
    bbx_width = self.Size[0] + 2 + text_width
    - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE,
    + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE,
    bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1)
    -
    +
    # Returns the connector connected to input
    def GetPreviousConnector(self):
    wires = self.Input.GetWires()
    if len(wires) == 1:
    return wires[0][0].GetOtherConnected(self.Input)
    return None
    -
    +
    # Refresh the element connectors position
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -1611,41 +1611,41 @@
    horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x
    self.Input.SetPosition(wx.Point(horizontal_pos, 0))
    self.RefreshConnected()
    -
    +
    # Refresh the position of wires connected to jump
    def RefreshConnected(self, exclude = []):
    if self.Input:
    self.Input.MoveConnected(exclude)
    -
    - # Returns input jump connector
    +
    + # Returns input jump connector
    def GetConnector(self, position = None, name = None):
    return self.Input
    -
    - # Returns all the jump connectors
    +
    + # Returns all the jump connectors
    def GetConnectors(self):
    return {"inputs": [self.Input], "outputs": []}
    -
    +
    # Test if point given is on jump input connector
    def TestConnector(self, pt, direction = None, exclude = True):
    # Test input connector
    if self.Input and self.Input.TestPoint(pt, direction, exclude):
    return self.Input
    return None
    -
    +
    # Changes the jump target
    def SetTarget(self, target):
    self.Target = target
    self.RefreshTargetSize()
    self.RefreshBoundingBox()
    -
    +
    # Returns the jump target
    def GetTarget(self):
    return self.Target
    -
    +
    # Returns the jump minimum size
    def GetMinSize(self):
    return SFC_JUMP_SIZE
    -
    +
    # Align input element with this jump
    def RefreshInputPosition(self):
    if self.Input:
    @@ -1662,21 +1662,21 @@
    input_block.MoveActionBlock((diffx, 0))
    input_block.Move(diffx, 0)
    input_block.RefreshInputPosition()
    -
    +
    # Can't align output element, because there is no output
    def RefreshOutputPosition(self, move = None):
    pass
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the jump properties
    self.Parent.EditJumpContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the jump state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -1688,7 +1688,7 @@
    return movex, 0
    else:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2)
    -
    +
    # Refresh input element model
    def RefreshInputModel(self):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -1698,32 +1698,32 @@
    input_block.RefreshModel(False)
    if not isinstance(input_block, SFC_Divergence):
    input_block.RefreshInputModel()
    -
    +
    # Refresh output element model
    def RefreshOutputModel(self, move=False):
    pass
    -
    +
    # Refreshes the jump model
    def RefreshModel(self, move=True):
    self.Parent.RefreshJumpModel(self)
    if move:
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    self.RefreshInputModel()
    -
    +
    # Adds an highlight to the variable
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "target" and start[0] == 0 and end[0] == 0:
    AddHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the variable
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "target":
    RemoveHighlight(self.Highlights, (start, end, highlight_type))
    -
    +
    # Removes all the highlights of one particular type from the variable
    def ClearHighlight(self, highlight_type=None):
    ClearHighlights(self.Highlights, highlight_type)
    -
    +
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
    scalex, scaley = dc.GetUserScale()
    @@ -1731,16 +1731,16 @@
    dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
    dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
    dc.SetLogicalFunction(wx.AND)
    - points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3,
    + points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3,
    int(round((self.Pos.y - 2) * scaley)) - 2),
    - wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4,
    + wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4,
    int(round((self.Pos.y - 2) * scaley)) - 2),
    - wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)),
    + wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)),
    int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)]
    dc.DrawPolygon(points)
    dc.SetLogicalFunction(wx.COPY)
    dc.SetUserScale(scalex, scaley)
    -
    +
    # Draws divergence
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -1750,12 +1750,12 @@
    else:
    dc.SetPen(MiterPen(wx.BLACK))
    dc.SetBrush(wx.BLACK_BRUSH)
    -
    +
    if getattr(dc, "printing", False):
    target_size = dc.GetTextExtent(self.Target)
    else:
    target_size = self.TargetSize
    -
    +
    # Draw plain rectangle for representing the divergence
    dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1])
    points = [wx.Point(self.Pos.x, self.Pos.y),
    @@ -1769,10 +1769,10 @@
    # Draw input connector
    if self.Input:
    self.Input.Draw(dc)
    -
    +
    if not getattr(dc, "printing", False):
    DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1])
    -
    +
    #-------------------------------------------------------------------------------
    # Sequencial Function Chart Action Block
    @@ -1783,7 +1783,7 @@
    """
    class SFC_ActionBlock(Graphic_Element):
    -
    +
    # Create a new action block
    def __init__(self, parent, actions = [], id = None):
    Graphic_Element.__init__(self, parent)
    @@ -1796,19 +1796,19 @@
    self.SetActions(actions)
    self.Value = None
    self.PreviousValue = None
    -
    +
    def Flush(self):
    if self.Input is not None:
    self.Input.Flush()
    self.Input = None
    -
    +
    def SpreadCurrent(self):
    if self.Parent.Debug:
    self.PreviousValue = self.Value
    self.Value = self.Input.ReceivingCurrent()
    if self.Value != self.PreviousValue and self.Visible:
    self.Parent.ElementNeedRefresh(self)
    -
    +
    # Make a clone of this SFC_ActionBlock
    def Clone(self, parent, id = None, pos = None):
    actions = [action.copy() for action in self.Actions]
    @@ -1820,10 +1820,10 @@
    action_block.SetPosition(self.Pos.x, self.Pos.y)
    action_block.Input = self.Input.Clone(action_block)
    return action_block
    -
    +
    def GetConnectorTranslation(self, element):
    return {self.Input : element.Input}
    -
    +
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
    rect = Graphic_Element.GetRedrawRect(self, movex, movey)
    @@ -1833,17 +1833,17 @@
    if self.Input.IsConnected():
    rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
    return rect
    -
    +
    # Returns the number of action lines
    def GetLineNumber(self):
    return len(self.Actions)
    -
    +
    def GetLineSize(self):
    if len(self.Actions) > 0:
    return self.Size[1] / len(self.Actions)
    else:
    return SFC_ACTION_MIN_SIZE[1]
    -
    +
    # Forbids to resize the action block
    def Resize(self, x, y, width, height):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -1851,38 +1851,38 @@
    self.SetSize(width, self.Size[1])
    else:
    Graphic_Element.Resize(self, x, y, width, height)
    -
    +
    # Delete this action block by calling the appropriate method
    def Delete(self):
    self.Parent.DeleteActionBlock(self)
    -
    +
    # Unconnect input and output
    def Clean(self):
    self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
    -
    +
    # Refresh the action block bounding box
    def RefreshBoundingBox(self):
    self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    -
    +
    # Refresh the position of wires connected to action block
    def RefreshConnected(self, exclude = []):
    self.Input.MoveConnected(exclude)
    -
    - # Returns input action block connector
    +
    + # Returns input action block connector
    def GetConnector(self, position = None, name = None):
    return self.Input
    -
    - # Returns all the action block connectors
    +
    + # Returns all the action block connectors
    def GetConnectors(self):
    return {"inputs": [self.Input], "outputs": []}
    -
    +
    # Test if point given is on action block input connector
    def TestConnector(self, pt, direction = None, exclude = True):
    # Test input connector
    if self.Input.TestPoint(pt, direction, exclude):
    return self.Input
    return None
    -
    +
    # Refresh the element connectors position
    def RefreshConnectors(self):
    scaling = self.Parent.GetScaling()
    @@ -1891,7 +1891,7 @@
    vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y
    self.Input.SetPosition(wx.Point(0, vertical_pos))
    self.RefreshConnected()
    -
    +
    # Changes the action block actions
    def SetActions(self, actions):
    self.Actions = actions
    @@ -1931,25 +1931,25 @@
    input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock()
    input_block.RefreshOutputPosition()
    input_block.RefreshOutputModel(True)
    -
    +
    # Returns the action block actions
    def GetActions(self):
    return self.Actions
    -
    +
    # Returns the action block minimum size
    def GetMinSize(self):
    return self.MinSize
    -
    +
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
    # Edit the action block properties
    self.Parent.EditActionBlockContent(self)
    -
    +
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
    # Popup the default menu
    self.Parent.PopupDefaultMenu()
    -
    +
    # Refreshes the action block state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
    if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
    @@ -1970,18 +1970,18 @@
    else:
    return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    -
    +
    # Refreshes the action block model
    def RefreshModel(self, move=True):
    self.Parent.RefreshActionBlockModel(self)
    -
    +
    # Adds an highlight to the variable
    def AddHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "action" and infos[1] < len(self.Actions):
    action_highlights = self.Highlights.setdefault(infos[1], {})
    attribute_highlights = action_highlights.setdefault(infos[2], [])
    AddHighlight(attribute_highlights, (start, end, highlight_type))
    -
    +
    # Removes an highlight from the block
    def RemoveHighlight(self, infos, start, end, highlight_type):
    if infos[0] == "action" and infos[1] < len(self.Actions):
    @@ -1991,7 +1991,7 @@
    action_highlights.pop(infos[2])
    if len(action_highlights) == 0:
    self.Highlights.pop(infos[1])
    -
    +
    # Removes all the highlights of one particular type from the block
    def ClearHighlight(self, highlight_type=None):
    if highlight_type is None:
    @@ -2006,7 +2006,7 @@
    action_highlights.pop(name)
    if len(action_highlights) == 0:
    self.Highlights.pop(number)
    -
    +
    # Draws divergence
    def Draw(self, dc):
    Graphic_Element.Draw(self, dc)
    @@ -2018,14 +2018,14 @@
    colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]]
    # Draw plain rectangle for representing the action block
    dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    - dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y,
    + dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y,
    self.Pos.x + colsize[0], self.Pos.y + self.Size[1])
    - dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y,
    + dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y,
    self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1])
    line_size = self.GetLineSize()
    for i, action in enumerate(self.Actions):
    if i != 0:
    - dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size,
    + dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size,
    self.Pos.x + self.Size[0], self.Pos.y + i * line_size)
    qualifier_size = dc.GetTextExtent(action.qualifier)
    if action.duration != "":
    @@ -2048,7 +2048,7 @@
    indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2,
    self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2)
    dc.DrawText(action.indicator, indicator_pos[0], indicator_pos[1])
    -
    +
    if not getattr(dc, "printing", False):
    action_highlights = self.Highlights.get(i, {})
    for name, attribute_highlights in action_highlights.iteritems():
    @@ -2060,7 +2060,6 @@
    DrawHighlightedText(dc, action.value, attribute_highlights, content_pos[0], content_pos[1])
    elif name == "indicator":
    DrawHighlightedText(dc, action.indicator, attribute_highlights, indicator_pos[0], indicator_pos[1])
    -
    +
    # Draw input connector
    self.Input.Draw(dc)
    -
    --- a/graphics/ToolTipProducer.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/graphics/ToolTipProducer.py Mon Aug 14 19:13:01 2017 +0300
    @@ -25,7 +25,7 @@
    import wx
    from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
    -
    +
    #-------------------------------------------------------------------------------
    # Tool Tip Producer class
    #-------------------------------------------------------------------------------
    @@ -35,29 +35,29 @@
    """
    class ToolTipProducer:
    -
    +
    def __init__(self, parent):
    """
    Constructor
    @param parent: Parent Viewer
    """
    self.Parent = parent
    -
    +
    self.ToolTip = None
    self.ToolTipPos = None
    -
    +
    # Timer for firing Tool tip display
    self.ToolTipTimer = wx.Timer(self.Parent, -1)
    - self.Parent.Bind(wx.EVT_TIMER,
    - self.OnToolTipTimer,
    + self.Parent.Bind(wx.EVT_TIMER,
    + self.OnToolTipTimer,
    self.ToolTipTimer)
    -
    +
    def __del__(self):
    """
    Destructor
    """
    self.DestroyToolTip()
    -
    +
    def OnToolTipTimer(self, event):
    """
    Callback for Tool Tip firing timer Event
    @@ -65,21 +65,21 @@
    """
    # Get Tool Tip text
    value = self.GetToolTipValue()
    -
    +
    if value is not None and self.ToolTipPos is not None:
    # Create Tool Tip
    self.ToolTip = CustomToolTip(self.Parent, value)
    self.ToolTip.SetToolTipPosition(self.ToolTipPos)
    self.ToolTip.Show()
    -
    +
    def GetToolTipValue(self):
    """
    Return tool tip text
    - Have to be overridden by inherited classes
    - @return: Tool tip text (None if not overridden)
    + Have to be overridden by inherited classes
    + @return: Tool tip text (None if not overridden)
    """
    return None
    -
    +
    def DisplayToolTip(self, pos):
    """
    Display Tool tip
    @@ -87,14 +87,14 @@
    """
    # Destroy current displayed Tool tip
    self.DestroyToolTip()
    -
    +
    # Save Tool Tip position
    self.ToolTipPos = pos
    # Start Tool tip firing timer
    self.ToolTipTimer.Start(
    - int(TOOLTIP_WAIT_PERIOD * 1000),
    + int(TOOLTIP_WAIT_PERIOD * 1000),
    oneShot=True)
    -
    +
    def SetToolTipText(self, text):
    """
    Set current Tool tip text
    @@ -102,7 +102,7 @@
    """
    if self.ToolTip is not None:
    self.ToolTip.SetTip(text)
    -
    +
    def DestroyToolTip(self):
    """
    Destroy current displayed Tool Tip
    @@ -110,7 +110,7 @@
    # Stop Tool tip firing timer
    self.ToolTipTimer.Stop()
    self.ToolTipPos = None
    -
    +
    # Destroy Tool Tip
    if self.ToolTip is not None:
    self.ToolTip.Destroy()
    --- a/i18n/mki18n.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/i18n/mki18n.py Mon Aug 14 19:13:01 2017 +0300
    @@ -1,21 +1,21 @@
    #! /usr/bin/env python
    # -*- coding: iso-8859-1 -*-
    -#
    +#
    # PYTHON MODULE: MKI18N.PY
    # =========
    -#
    +#
    # Abstract: Make Internationalization (i18n) files for an application.
    -#
    +#
    # Copyright Pierre Rouleau. 2003. Released to public domain.
    -#
    +#
    # Last update: Saturday, November 8, 2003. @ 15:55:18.
    -#
    +#
    # File: ROUP2003N01::C:/dev/python/mki18n.py
    -#
    +#
    # RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $
    -#
    +#
    # Update history:
    -#
    +#
    # - File created: Saturday, June 7, 2003. by Pierre Rouleau
    # - 10/06/03 rcs : RCS Revision 1.1 2003/06/10 10:06:12 PRouleau
    # - 10/06/03 rcs : RCS Initial revision
    @@ -27,22 +27,22 @@
    # - 05/11/03 rcs : RCS Revision 1.4 2003/10/22 06:39:31 PRouleau
    # - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file.
    # - 08/11/03 rcs : RCS Revision 1.5 2003/11/05 19:40:04 PRouleau
    -#
    +#
    # RCS $Log: $
    -#
    -#
    +#
    +#
    # -----------------------------------------------------------------------------
    -"""
    -mki18n allows you to internationalize your software. You can use it to
    +"""
    +mki18n allows you to internationalize your software. You can use it to
    create the GNU .po files (Portable Object) and the compiled .mo files
    (Machine Object).
    -mki18n module can be used from the command line or from within a script (see
    +mki18n module can be used from the command line or from within a script (see
    the Usage at the end of this page).
    Table of Contents
    -----------------
    -
    +
    makePO() -- Build the Portable Object file for the application --
    catPO() -- Concatenate one or several PO files with the application domain files. --
    makeMO() -- Compile the Portable Object files into the Machine Object stored in the right location. --
    @@ -60,24 +60,24 @@
    libiconv ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP
    files and install the packages inside c:/gnu. All binaries will be stored
    inside c:/gnu/bin. Just put c:/gnu/bin inside your PATH. You will need
    - the following files:
    + the following files:
    - - `gettext-runtime-0.12.1.bin.woe32.zip`_
    + - `gettext-runtime-0.12.1.bin.woe32.zip`_
    - `gettext-tools-0.12.1.bin.woe32.zip`_
    - - `libiconv-1.9.1.bin.woe32.zip`_
    + - `libiconv-1.9.1.bin.woe32.zip`_
    .. _GNU libiconv: http://www.gnu.org/software/libiconv/
    .. _GNU libiconv ftp site: http://www.ibiblio.org/pub/gnu/libiconv/
    -.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip
    -.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip
    +.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip
    +.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip
    .. _libiconv-1.9.1.bin.woe32.zip: http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip
    """
    # -----------------------------------------------------------------------------
    # Module Import
    # -------------
    -#
    +#
    import os
    import sys
    import wx
    @@ -143,24 +143,24 @@
    def makePO(applicationDirectoryPath, applicationDomain=None, verbose=0) :
    """Build the Portable Object Template file for the application.
    - makePO builds the .pot file for the application stored inside
    - a specified directory by running xgettext for all application source
    - files. It finds the name of all files by looking for a file called 'app.fil'.
    + makePO builds the .pot file for the application stored inside
    + a specified directory by running xgettext for all application source
    + files. It finds the name of all files by looking for a file called 'app.fil'.
    If this file does not exists, makePo raises an IOError exception.
    By default the application domain (the application
    name) is the same as the directory name but it can be overridden by the
    'applicationDomain' argument.
    - makePO always creates a new file called messages.pot. If it finds files
    - of the form app_xx.po where 'app' is the application name and 'xx' is one
    - of the ISO 639 two-letter language codes, makePO resynchronizes those
    - files with the latest extracted strings (now contained in messages.pot).
    + makePO always creates a new file called messages.pot. If it finds files
    + of the form app_xx.po where 'app' is the application name and 'xx' is one
    + of the ISO 639 two-letter language codes, makePO resynchronizes those
    + files with the latest extracted strings (now contained in messages.pot).
    This process updates all line location number in the language-specific
    - .po files and may also create new entries for translation (or comment out
    - some). The .po file is not changed, instead a new file is created with
    + .po files and may also create new entries for translation (or comment out
    + some). The .po file is not changed, instead a new file is created with
    the .new extension appended to the name of the .po file.
    - By default the function does not display what it is doing. Set the
    + By default the function does not display what it is doing. Set the
    verbose argument to 1 to force it to print its commands.
    """
    @@ -175,10 +175,10 @@
    raise IOError(2,'No module file: ' % filelist)
    fileout = 'messages.pot'
    - # Steps:
    + # Steps:
    # Use xgettext to parse all application modules
    # The following switches are used:
    - #
    + #
    # -s : sort output by string content (easier to use when we need to merge several .po files)
    # --files-from=app.fil : The list of files is taken from the file: app.fil
    # --output= : specifies the name of the output file (using a .pot extension)
    @@ -190,12 +190,12 @@
    processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings')
    XML_TC6_STRING_MODEL = re.compile("<documentation>\s*<xhtml\:p><!\[CDATA\[([^\]]*)\]\]></xhtml\:p>\s*</documentation>", re.MULTILINE | re.DOTALL)
    - processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')
    + processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings')
    # generate messages.po
    cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout)
    if verbose: print cmd
    - os.system(cmd)
    + os.system(cmd)
    languageDict = getlanguageDict()
    @@ -251,18 +251,18 @@
    # -----------------------------------------------------------------------------
    # m a k e M O ( ) -- Compile the Portable Object files into the Machine Object stored in the right location. --
    # ^^^^^^^^^^^^^^^
    -#
    +#
    def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) :
    """Compile the Portable Object files into the Machine Object stored in the right location.
    - makeMO converts all translated language-specific PO files located inside
    - the application directory into the binary .MO files stored inside the
    + makeMO converts all translated language-specific PO files located inside
    + the application directory into the binary .MO files stored inside the
    LC_MESSAGES sub-directory for the found locale files.
    - makeMO searches for all files that have a name of the form 'app_xx.po'
    - inside the application directory specified by the first argument. The
    - 'app' is the application domain name (that can be specified by the
    - applicationDomain argument or is taken from the directory name). The 'xx'
    + makeMO searches for all files that have a name of the form 'app_xx.po'
    + inside the application directory specified by the first argument. The
    + 'app' is the application domain name (that can be specified by the
    + applicationDomain argument or is taken from the directory name). The 'xx'
    corresponds to one of the ISO 639 two-letter language codes.
    makeMo stores the resulting files inside a sub-directory of `targetDir`
    @@ -296,7 +296,7 @@
    if verbose: print cmd
    os.system(cmd)
    os.chdir(currentDir)
    -
    +
    # -----------------------------------------------------------------------------
    # p r i n t U s a g e -- Displays how to use this script from the command line --
    # ^^^^^^^^^^^^^^^^^^^
    @@ -331,7 +331,7 @@
    # #
    # You must specify one of the -p or -m option to perform the work. You can #
    # specify the path of the target application. If you leave it out mki18n #
    - # will use the current directory as the application main directory. #
    + # will use the current directory as the application main directory. #
    # #
    ##################################################################################"""
    if errorMsg:
    @@ -340,14 +340,14 @@
    # -----------------------------------------------------------------------------
    # f i l e B a s e O f ( ) -- Return base name of filename --
    # ^^^^^^^^^^^^^^^^^^^^^^^
    -#
    +#
    def fileBaseOf(filename,withPath=0) :
    """fileBaseOf(filename,withPath) ---> string
    Return base name of filename. The returned string never includes the extension.
    - Use os.path.basename() to return the basename with the extension. The
    - second argument is optional. If specified and if set to 'true' (non zero)
    - the string returned contains the full path of the file name. Otherwise the
    + Use os.path.basename() to return the basename with the extension. The
    + second argument is optional. If specified and if set to 'true' (non zero)
    + the string returned contains the full path of the file name. Otherwise the
    path is excluded.
    [Example]
    @@ -370,8 +370,8 @@
    'abcdef'
    >>> fileBaseOf(fn,1)
    'abcdef'
    - """
    - pos = filename.rfind('.')
    + """
    + pos = filename.rfind('.')
    if pos > 0:
    filename = filename[:pos]
    if withPath:
    @@ -381,49 +381,49 @@
    # -----------------------------------------------------------------------------
    # m k d i r ( ) -- Create a directory (and possibly the entire tree) --
    # ^^^^^^^^^^^^^
    -#
    +#
    def mkdir(directory) :
    """Create a directory (and possibly the entire tree).
    The os.mkdir() will fail to create a directory if one of the
    directory in the specified path does not exist. mkdir()
    solves this problem. It creates every intermediate directory
    - required to create the final path. Under Unix, the function
    + required to create the final path. Under Unix, the function
    only supports forward slash separator, but under Windows and MacOS
    the function supports the forward slash and the OS separator (backslash
    under windows).
    - """
    + """
    # translate the path separators
    directory = unixpath(directory)
    # build a list of all directory elements
    aList = filter(lambda x: len(x)>0, directory.split('/'))
    - theLen = len(aList)
    + theLen = len(aList)
    # if the first element is a Windows-style disk drive
    # concatenate it with the first directory
    if aList[0].endswith(':'):
    if theLen > 1:
    aList[1] = aList[0] + '/' + aList[1]
    - del aList[0]
    - theLen -= 1
    + del aList[0]
    + theLen -= 1
    # if the original directory starts at root,
    - # make sure the first element of the list
    + # make sure the first element of the list
    # starts at root too
    - if directory[0] == '/':
    + if directory[0] == '/':
    aList[0] = '/' + aList[0]
    - # Now iterate through the list, check if the
    + # Now iterate through the list, check if the
    # directory exists and if not create it
    theDir = ''
    for i in range(theLen):
    theDir += aList[i]
    if not os.path.exists(theDir):
    os.mkdir(theDir)
    - theDir += '/'
    -
    + theDir += '/'
    +
    # -----------------------------------------------------------------------------
    # u n i x p a t h ( ) -- Return a path name that contains Unix separator. --
    # ^^^^^^^^^^^^^^^^^^^
    -#
    +#
    def unixpath(thePath) :
    r"""Return a path name that contains Unix separator.
    @@ -432,7 +432,7 @@
    'd:/test'
    >>> unixpath("d:/test/file.txt")
    'd:/test/file.txt'
    - >>>
    + >>>
    """
    thePath = os.path.normpath(thePath)
    if os.sep == '/':
    @@ -440,11 +440,11 @@
    else:
    return thePath.replace(os.sep,'/')
    -# -----------------------------------------------------------------------------
    +# -----------------------------------------------------------------------------
    # S c r i p t e x e c u t i o n -- Runs when invoked from the command line --
    # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    -#
    +#
    if __name__ == "__main__":
    import getopt # command line parsing
    argc = len(sys.argv)
    @@ -453,11 +453,11 @@
    sys.exit(1)
    # If there is some arguments, parse the command line
    validOptions = "ehmpv"
    - validLongOptions = ['domain=', 'moTarget=']
    + validLongOptions = ['domain=', 'moTarget=']
    option = {}
    option['forceEnglish'] = 0
    option['mo'] = 0
    - option['po'] = 0
    + option['po'] = 0
    option['verbose'] = 0
    option['domain'] = None
    option['moTarget'] = None
    @@ -465,11 +465,11 @@
    optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions)
    except getopt.GetoptError, e:
    printUsage(e[0])
    - sys.exit(1)
    + sys.exit(1)
    for (opt,val) in optionList:
    - if (opt == '-h'):
    + if (opt == '-h'):
    printUsage()
    - sys.exit(0)
    + sys.exit(0)
    elif (opt == '-e'): option['forceEnglish'] = 1
    elif (opt == '-m'): option['mo'] = 1
    elif (opt == '-p'): option['po'] = 1
    @@ -497,7 +497,7 @@
    printUsage(e[1] + '\n You must write a file app.fil that contains the list of all files to parse.')
    if option['mo']:
    makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish'])
    - sys.exit(1)
    -
    + sys.exit(1)
    +
    # -----------------------------------------------------------------------------
    --- a/plcopen/plcopen.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/plcopen/plcopen.py Mon Aug 14 19:13:01 2017 +0300
    @@ -49,14 +49,14 @@
    VarOrder = ["Local","Temp","Input","Output","InOut","External","Global","Access"]
    """
    -Define which action qualifier must be associated with a duration
    +Define which action qualifier must be associated with a duration
    """
    -QualifierList = OrderedDict([("N", False), ("R", False), ("S", False),
    - ("L", True), ("D", True), ("P", False), ("P0", False),
    +QualifierList = OrderedDict([("N", False), ("R", False), ("S", False),
    + ("L", True), ("D", True), ("P", False), ("P0", False),
    ("P1", False), ("SD", True), ("DS", True), ("SL", True)])
    -FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)"
    +FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)"
    def update_address(address, address_model, new_leading):
    result = address_model.match(address)
    @@ -73,10 +73,10 @@
    return v1
    """
    -Helper class for bounding_box calculation
    +Helper class for bounding_box calculation
    """
    class rect:
    -
    +
    def __init__(self, x=None, y=None, width=None, height=None):
    self.x_min = x
    self.x_max = None
    @@ -86,19 +86,19 @@
    self.x_max = x + width
    if height is not None and y is not None:
    self.y_max = y + height
    -
    +
    def update(self, x, y):
    self.x_min = _init_and_compare(min, self.x_min, x)
    self.x_max = _init_and_compare(max, self.x_max, x)
    self.y_min = _init_and_compare(min, self.y_min, y)
    self.y_max = _init_and_compare(max, self.y_max, y)
    -
    +
    def union(self, rect):
    self.x_min = _init_and_compare(min, self.x_min, rect.x_min)
    self.x_max = _init_and_compare(max, self.x_max, rect.x_max)
    self.y_min = _init_and_compare(min, self.y_min, rect.y_min)
    self.y_max = _init_and_compare(max, self.y_max, rect.y_max)
    -
    +
    def bounding_box(self):
    width = height = None
    if self.x_min is not None and self.x_max is not None:
    @@ -125,7 +125,7 @@
    test_result = []
    result = criteria["pattern"].search(text)
    while result is not None:
    - prev_pos=result.endpos
    + prev_pos=result.endpos
    start = TextLenInRowColumn(text[:result.start()])
    end = TextLenInRowColumn(text[:result.end() - 1])
    test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1])))
    @@ -141,11 +141,11 @@
    PLCOpen_XPath = lambda xpath: etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP)
    LOAD_POU_PROJECT_TEMPLATE = """
    -<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201"
    - xmlns:xhtml="http://www.w3.org/1999/xhtml"
    - xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    +<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201"
    + xmlns:xhtml="http://www.w3.org/1999/xhtml"
    + xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.plcopen.org/xml/tc6_0201">
    - <fileHeader companyName="" productName="" productVersion=""
    + <fileHeader companyName="" productName="" productVersion=""
    creationDateTime="1970-01-01T00:00:00"/>
    <contentHeader name="paste_project">
    <coordinateInfo>
    @@ -176,8 +176,8 @@
    PLCOpen_v1_xml = PLCOpen_v1_file.read()
    PLCOpen_v1_file.close()
    PLCOpen_v1_xml = PLCOpen_v1_xml.replace(
    - "http://www.plcopen.org/xml/tc6.xsd",
    - "http://www.plcopen.org/xml/tc6_0201")
    + "http://www.plcopen.org/xml/tc6.xsd",
    + "http://www.plcopen.org/xml/tc6_0201")
    PLCOpen_v1_xsd = etree.XMLSchema(etree.fromstring(PLCOpen_v1_xml))
    # XPath for file compatibility process
    @@ -190,21 +190,21 @@
    def LoadProjectXML(project_xml):
    project_xml = project_xml.replace(
    - "http://www.plcopen.org/xml/tc6.xsd",
    + "http://www.plcopen.org/xml/tc6.xsd",
    "http://www.plcopen.org/xml/tc6_0201")
    for cre, repl in [
    (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
    (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
    project_xml = cre.sub(repl, project_xml)
    -
    +
    try:
    tree, error = PLCOpenParser.LoadXMLString(project_xml)
    if error is None:
    return tree, None
    -
    +
    if PLCOpen_v1_xsd.validate(tree):
    # Make file compatible with PLCOpen v2
    -
    +
    # Update resource interval value
    for resource in ProjectResourcesXPath(tree):
    for task in resource.gettask():
    @@ -229,13 +229,13 @@
    else:
    text += "%dms"%(time_values[3] / 1000)
    task.set("interval", text)
    -
    +
    # Update resources pou instance attributes
    for pouInstance in ResourceInstancesXpath(resource):
    type_name = pouInstance.attrib.pop("type")
    if type_name is not None:
    pouInstance.set("typeName", type_name)
    -
    +
    # Update transitions condition
    for transition_condition in TransitionsConditionXPath(tree):
    connections = ConditionConnectionsXPath(transition_condition)
    @@ -245,23 +245,23 @@
    connectionPointIn.setrelPositionXY(0, 0)
    for connection in connections:
    connectionPointIn.append(connection)
    -
    +
    # Update actionBlocks
    for actionBlock in ActionBlocksXPath(tree):
    for connectionPointOut in ActionBlocksConnectionPointOutXPath(actionBlock):
    actionBlock.remove(connectionPointOut)
    -
    +
    for action in actionBlock.getaction():
    action.set("localId", "0")
    relPosition = PLCOpenParser.CreateElement("relPosition", "action")
    relPosition.set("x", "0")
    relPosition.set("y", "0")
    action.setrelPosition(relPosition)
    -
    +
    return tree, None
    -
    +
    return tree, error
    -
    +
    except Exception, e:
    return None, e.message
    @@ -288,9 +288,9 @@
    def SaveProject(project, filepath):
    project_file = open(filepath, 'w')
    project_file.write(etree.tostring(
    - project,
    - pretty_print=True,
    - xml_declaration=True,
    + project,
    + pretty_print=True,
    + xml_declaration=True,
    encoding='utf-8'))
    project_file.close()
    @@ -302,7 +302,7 @@
    text = pattern.sub(new_name, text)
    self.setanyText(text)
    setattr(cls, "updateElementName", updateElementName)
    -
    +
    def updateElementAddress(self, address_model, new_leading):
    text = self.getanyText()
    startpos = 0
    @@ -315,28 +315,28 @@
    result = address_model.search(text, startpos)
    self.setanyText(text)
    setattr(cls, "updateElementAddress", updateElementAddress)
    -
    +
    def hasblock(self, block_type):
    - text = self.getanyText()
    + text = self.getanyText()
    pattern = re.compile('\\b' + block_type + '\\b', re.IGNORECASE)
    return pattern.search(text) is not None
    setattr(cls, "hasblock", hasblock)
    -
    +
    def Search(self, criteria, parent_infos):
    return [(tuple(parent_infos),) + result for result in TestTextElement(self.getanyText(), criteria)]
    setattr(cls, "Search", Search)
    -
    +
    cls = PLCOpenParser.GetElementClass("project")
    if cls:
    -
    +
    def setname(self, name):
    self.contentHeader.setname(name)
    setattr(cls, "setname", setname)
    -
    +
    def getname(self):
    return self.contentHeader.getname()
    setattr(cls, "getname", getname)
    -
    +
    def getfileHeader(self):
    fileheader_obj = self.fileHeader
    return {
    @@ -351,7 +351,7 @@
    ("contentDescription", fileheader_obj.getcontentDescription())]
    }
    setattr(cls, "getfileHeader", getfileHeader)
    -
    +
    def setfileHeader(self, fileheader):
    fileheader_obj = self.fileHeader
    for attr in ["companyName", "companyURL", "productName",
    @@ -361,7 +361,7 @@
    if value is not None:
    setattr(fileheader_obj, attr, value)
    setattr(cls, "setfileHeader", setfileHeader)
    -
    +
    def getcontentHeader(self):
    contentheader_obj = self.contentHeader
    contentheader = {
    @@ -378,7 +378,7 @@
    contentheader["scaling"] = self.contentHeader.getscaling()
    return contentheader
    setattr(cls, "getcontentHeader", getcontentHeader)
    -
    +
    def setcontentHeader(self, contentheader):
    contentheader_obj = self.contentHeader
    for attr, value in contentheader.iteritems():
    @@ -392,7 +392,7 @@
    elif attr in ["modificationDateTime", "organization", "language"]:
    setattr(contentheader_obj, attr, value)
    setattr(cls, "setcontentHeader", setcontentHeader)
    -
    +
    def gettypeElementFunc(element_type):
    elements_xpath = PLCOpen_XPath(
    "ppx:types/ppx:%(element_type)ss/ppx:%(element_type)s[@name=$name]" % locals())
    @@ -402,7 +402,7 @@
    return elements[0]
    return None
    return gettypeElement
    -
    +
    datatypes_xpath = PLCOpen_XPath("ppx:types/ppx:dataTypes/ppx:dataType")
    filtered_datatypes_xpath = PLCOpen_XPath(
    "ppx:types/ppx:dataTypes/ppx:dataType[@name!=$exclude]")
    @@ -411,43 +411,43 @@
    return filtered_datatypes_xpath(self, exclude=exclude)
    return datatypes_xpath(self)
    setattr(cls, "getdataTypes", getdataTypes)
    -
    +
    setattr(cls, "getdataType", gettypeElementFunc("dataType"))
    -
    +
    def appenddataType(self, name):
    if self.getdataType(name) is not None:
    raise ValueError, "\"%s\" Data Type already exists !!!"%name
    self.types.appenddataTypeElement(name)
    setattr(cls, "appenddataType", appenddataType)
    -
    +
    def insertdataType(self, index, datatype):
    self.types.insertdataTypeElement(index, datatype)
    setattr(cls, "insertdataType", insertdataType)
    -
    +
    def removedataType(self, name):
    self.types.removedataTypeElement(name)
    setattr(cls, "removedataType", removedataType)
    -
    +
    def getpous(self, exclude=None, filter=[]):
    return self.xpath(
    - "ppx:types/ppx:pous/ppx:pou%s%s" %
    + "ppx:types/ppx:pous/ppx:pou%s%s" %
    (("[@name!='%s']" % exclude) if exclude is not None else '',
    ("[%s]" % " or ".join(
    map(lambda x: "@pouType='%s'" % x, filter)))
    if len(filter) > 0 else ""),
    namespaces=PLCOpenParser.NSMAP)
    setattr(cls, "getpous", getpous)
    -
    +
    setattr(cls, "getpou", gettypeElementFunc("pou"))
    -
    +
    def appendpou(self, name, pou_type, body_type):
    self.types.appendpouElement(name, pou_type, body_type)
    setattr(cls, "appendpou", appendpou)
    -
    +
    def insertpou(self, index, pou):
    self.types.insertpouElement(index, pou)
    setattr(cls, "insertpou", insertpou)
    -
    +
    def removepou(self, name):
    self.types.removepouElement(name)
    setattr(cls, "removepou", removepou)
    @@ -473,7 +473,7 @@
    new_configuration = PLCOpenParser.CreateElement("configuration", "configurations")
    new_configuration.setname(name)
    self.instances.configurations.appendconfiguration(new_configuration)
    - setattr(cls, "addconfiguration", addconfiguration)
    + setattr(cls, "addconfiguration", addconfiguration)
    def removeconfiguration(self, name):
    configuration = self.getconfiguration(name)
    @@ -481,7 +481,7 @@
    raise ValueError, ("\"%s\" configuration doesn't exist !!!") % name
    self.instances.configurations.remove(configuration)
    setattr(cls, "removeconfiguration", removeconfiguration)
    -
    +
    resources_xpath = PLCOpen_XPath(
    "ppx:instances/ppx:configurations/ppx:configuration[@name=$configname]/ppx:resource[@name=$name]")
    def getconfigurationResource(self, config_name, name):
    @@ -562,11 +562,11 @@
    cls = PLCOpenParser.GetElementClass("contentHeader", "project")
    if cls:
    -
    +
    def setpageSize(self, width, height):
    self.coordinateInfo.setpageSize(width, height)
    setattr(cls, "setpageSize", setpageSize)
    -
    +
    def getpageSize(self):
    return self.coordinateInfo.getpageSize()
    setattr(cls, "getpageSize", getpageSize)
    @@ -575,7 +575,7 @@
    for language, (x, y) in scaling.items():
    self.coordinateInfo.setscaling(language, x, y)
    setattr(cls, "setscaling", setscaling)
    -
    +
    def getscaling(self):
    scaling = {}
    scaling["FBD"] = self.coordinateInfo.getscaling("FBD")
    @@ -595,7 +595,7 @@
    self.pageSize.setx(width)
    self.pageSize.sety(height)
    setattr(cls, "setpageSize", setpageSize)
    -
    +
    def getpageSize(self):
    if self.pageSize is not None:
    return self.pageSize.getx(), self.pageSize.gety()
    @@ -613,7 +613,7 @@
    self.sfc.scaling.setx(x)
    self.sfc.scaling.sety(y)
    setattr(cls, "setscaling", setscaling)
    -
    +
    def getscaling(self, language):
    if language == "FBD":
    return self.fbd.scaling.getx(), self.fbd.scaling.gety()
    @@ -685,7 +685,7 @@
    cls = PLCOpenParser.GetElementClass("configuration", "configurations")
    if cls:
    -
    +
    def addglobalVar(self, var_type, name, location="", description=""):
    globalvars = self.getglobalVars()
    if len(globalvars) == 0:
    @@ -701,7 +701,7 @@
    var.setdocumentation(ft)
    globalvars[-1].appendvariable(var)
    setattr(cls, "addglobalVar", addglobalVar)
    -
    +
    def updateElementName(self, old_name, new_name):
    _updateConfigurationResourceElementName(self, old_name, new_name)
    for resource in self.getresource():
    @@ -727,7 +727,7 @@
    search_result.extend(resource.Search(criteria, parent_infos))
    return search_result
    setattr(cls, "Search", Search)
    -
    +
    cls = PLCOpenParser.GetElementClass("resource", "configuration")
    if cls:
    def updateElementName(self, old_name, new_name):
    @@ -788,7 +788,7 @@
    setattr(cls, "updateElementAddress", updateElementAddress)
    def Search(self, criteria, parent_infos=[]):
    - return _Search([("single", self.getsingle()),
    + return _Search([("single", self.getsingle()),
    ("interval", self.getinterval()),
    ("priority", str(self.getpriority()))],
    criteria, parent_infos)
    @@ -802,7 +802,7 @@
    setattr(cls, "updateElementName", updateElementName)
    def Search(self, criteria, parent_infos=[]):
    - return _Search([("name", self.getname()),
    + return _Search([("name", self.getname()),
    ("type", self.gettypeName())],
    criteria, parent_infos)
    setattr(cls, "Search", Search)
    @@ -822,22 +822,22 @@
    elif vartype_content_name == "array":
    base_type = vartype_content.baseType.getcontent()
    base_type_name = base_type.getLocalTag()
    - # Array derived directly from a user defined type
    + # Array derived directly from a user defined type
    if base_type_name == "derived":
    basetype_name = base_type.getname()
    - # Array derived directly from a string type
    + # Array derived directly from a string type
    elif base_type_name in ["string", "wstring"]:
    basetype_name = base_type_name.upper()
    - # Array derived directly from an elementary type
    + # Array derived directly from an elementary type
    else:
    basetype_name = base_type_name
    return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content.getdimension())), basetype_name)
    # Variable type is an elementary type
    return vartype_content_name
    setattr(cls, "gettypeAsText", gettypeAsText)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    - search_result = _Search([("name", self.getname()),
    + search_result = _Search([("name", self.getname()),
    ("type", self.gettypeAsText()),
    ("location", self.getaddress())],
    criteria, parent_infos)
    @@ -855,7 +855,7 @@
    def getdataTypeElements(self):
    return self.dataTypes.getdataType()
    setattr(cls, "getdataTypeElements", getdataTypeElements)
    -
    +
    def getdataTypeElement(self, name):
    elements = self.dataTypes.getdataType()
    for element in elements:
    @@ -870,11 +870,11 @@
    new_datatype.setname(name)
    new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL", "dataType"))
    setattr(cls, "appenddataTypeElement", appenddataTypeElement)
    -
    +
    def insertdataTypeElement(self, index, dataType):
    self.dataTypes.insertdataType(index, dataType)
    setattr(cls, "insertdataTypeElement", insertdataTypeElement)
    -
    +
    def removedataTypeElement(self, name):
    found = False
    for element in self.dataTypes.getdataType():
    @@ -885,11 +885,11 @@
    if not found:
    raise ValueError, _("\"%s\" Data Type doesn't exist !!!")%name
    setattr(cls, "removedataTypeElement", removedataTypeElement)
    -
    +
    def getpouElements(self):
    return self.pous.getpou()
    setattr(cls, "getpouElements", getpouElements)
    -
    +
    def getpouElement(self, name):
    elements = self.pous.getpou()
    for element in elements:
    @@ -909,11 +909,11 @@
    new_pou.appendbody(PLCOpenParser.CreateElement("body", "pou"))
    new_pou.setbodyType(body_type)
    setattr(cls, "appendpouElement", appendpouElement)
    -
    +
    def insertpouElement(self, index, pou):
    self.pous.insertpou(index, pou)
    setattr(cls, "insertpouElement", insertpouElement)
    -
    +
    def removepouElement(self, name):
    found = False
    for element in self.pous.getpou():
    @@ -941,7 +941,7 @@
    cls = PLCOpenParser.GetElementClass("dataType", "dataTypes")
    if cls:
    setattr(cls, "updateElementName", _updateBaseTypeElementName)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    search_result = []
    filter = criteria["filter"]
    @@ -956,7 +956,7 @@
    cls = PLCOpenParser.GetElementClass("dataType")
    if cls:
    -
    +
    def updateElementName(self, old_name, new_name):
    content_name = self.content.getLocalTag()
    if content_name in ["derived", "array", "subrangeSigned", "subrangeUnsigned"]:
    @@ -987,7 +987,7 @@
    if TextMatched(self.name, old_name):
    self.name = new_name
    setattr(cls, "updateElementName", updateElementName)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    return [(tuple(parent_infos),) + result for result in TestTextElement(self.name, criteria)]
    setattr(cls, "Search", Search)
    @@ -995,7 +995,7 @@
    cls = PLCOpenParser.GetElementClass("array", "dataType")
    if cls:
    setattr(cls, "updateElementName", _updateBaseTypeElementName)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    search_result = self.baseType.Search(criteria, parent_infos)
    for i, dimension in enumerate(self.getdimension()):
    @@ -1024,11 +1024,11 @@
    cls = PLCOpenParser.GetElementClass("enum", "dataType")
    if cls:
    -
    +
    def updateElementName(self, old_name, new_name):
    pass
    setattr(cls, "updateElementName", updateElementName)
    -
    +
    enumerated_datatype_values_xpath = PLCOpen_XPath("ppx:values/ppx:value")
    def Search(self, criteria, parent_infos=[]):
    search_result = []
    @@ -1044,21 +1044,21 @@
    if type_content_type == "derived":
    return type_content.getname()
    return type_content_type.upper()
    -
    +
    cls = PLCOpenParser.GetElementClass("pou", "pous")
    if cls:
    -
    +
    block_inputs_xpath = PLCOpen_XPath(
    "ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars]/ppx:variable")
    block_outputs_xpath = PLCOpen_XPath(
    "ppx:interface/*[self::ppx:outputVars or self::ppx:inOutVars]/ppx:variable")
    - def getblockInfos(self):
    + def getblockInfos(self):
    block_infos = {
    - "name" : self.getname(),
    - "type" : self.getpouType(),
    + "name" : self.getname(),
    + "type" : self.getpouType(),
    "extensible" : False,
    - "inputs" : [],
    - "outputs" : [],
    + "inputs" : [],
    + "outputs" : [],
    "comment" : self.getdescription()}
    if self.interface is not None:
    return_type = self.interface.getreturnType()
    @@ -1071,15 +1071,15 @@
    block_infos["outputs"].extend(
    [(var.getname(), _getvariableTypeinfos(var.type), "none")
    for var in block_outputs_xpath(self)])
    -
    - block_infos["usage"] = ("\n (%s) => (%s)" %
    - (", ".join(["%s:%s" % (input[1], input[0])
    +
    + block_infos["usage"] = ("\n (%s) => (%s)" %
    + (", ".join(["%s:%s" % (input[1], input[0])
    for input in block_infos["inputs"]]),
    - ", ".join(["%s:%s" % (output[1], output[0])
    + ", ".join(["%s:%s" % (output[1], output[0])
    for output in block_infos["outputs"]])))
    return block_infos
    setattr(cls, "getblockInfos", getblockInfos)
    -
    +
    def setdescription(self, description):
    doc = self.getdocumentation()
    if doc is None:
    @@ -1087,14 +1087,14 @@
    self.setdocumentation(doc)
    doc.setanyText(description)
    setattr(cls, "setdescription", setdescription)
    -
    +
    def getdescription(self):
    doc = self.getdocumentation()
    if doc is not None:
    return doc.getanyText()
    return ""
    setattr(cls, "getdescription", getdescription)
    -
    +
    def setbodyType(self, body_type):
    if len(self.body) > 0:
    if body_type in ["IL", "ST", "LD", "FBD", "SFC"]:
    @@ -1102,66 +1102,66 @@
    else:
    raise ValueError, "%s isn't a valid body type!"%type
    setattr(cls, "setbodyType", setbodyType)
    -
    +
    def getbodyType(self):
    if len(self.body) > 0:
    return self.body[0].getcontent().getLocalTag()
    setattr(cls, "getbodyType", getbodyType)
    -
    +
    def resetexecutionOrder(self):
    if len(self.body) > 0:
    self.body[0].resetexecutionOrder()
    setattr(cls, "resetexecutionOrder", resetexecutionOrder)
    -
    +
    def compileexecutionOrder(self):
    if len(self.body) > 0:
    self.body[0].compileexecutionOrder()
    setattr(cls, "compileexecutionOrder", compileexecutionOrder)
    -
    +
    def setelementExecutionOrder(self, instance, new_executionOrder):
    if len(self.body) > 0:
    self.body[0].setelementExecutionOrder(instance, new_executionOrder)
    setattr(cls, "setelementExecutionOrder", setelementExecutionOrder)
    -
    +
    def addinstance(self, instance):
    if len(self.body) > 0:
    self.body[0].appendcontentInstance(instance)
    setattr(cls, "addinstance", addinstance)
    -
    +
    def getinstances(self):
    if len(self.body) > 0:
    return self.body[0].getcontentInstances()
    return []
    setattr(cls, "getinstances", getinstances)
    -
    +
    def getinstance(self, id):
    if len(self.body) > 0:
    return self.body[0].getcontentInstance(id)
    return None
    setattr(cls, "getinstance", getinstance)
    -
    +
    def getinstancesIds(self):
    if len(self.body) > 0:
    return self.body[0].getcontentInstancesIds()
    return []
    setattr(cls, "getinstancesIds", getinstancesIds)
    -
    +
    def getinstanceByName(self, name):
    if len(self.body) > 0:
    return self.body[0].getcontentInstanceByName(name)
    return None
    setattr(cls, "getinstanceByName", getinstanceByName)
    -
    +
    def removeinstance(self, id):
    if len(self.body) > 0:
    self.body[0].removecontentInstance(id)
    setattr(cls, "removeinstance", removeinstance)
    -
    +
    def settext(self, text):
    if len(self.body) > 0:
    self.body[0].settext(text)
    setattr(cls, "settext", settext)
    -
    +
    def gettext(self):
    if len(self.body) > 0:
    return self.body[0].gettext()
    @@ -1178,17 +1178,17 @@
    vars.append((reverse_types[varlist.getLocalTag()], varlist))
    return vars
    setattr(cls, "getvars", getvars)
    -
    +
    def setvars(self, vars):
    if self.interface is None:
    self.interface = PLCOpenParser.CreateElement("interface", "pou")
    self.interface.setcontent(vars)
    setattr(cls, "setvars", setvars)
    -
    +
    def addpouExternalVar(self, var_type, name):
    self.addpouVar(var_type, name, "externalVars")
    setattr(cls, "addpouExternalVar", addpouExternalVar)
    -
    +
    def addpouVar(self, var_type, name, var_class="localVars", location="", description="", initval=""):
    if self.interface is None:
    self.interface = PLCOpenParser.CreateElement("interface", "pou")
    @@ -1218,11 +1218,11 @@
    el = PLCOpenParser.CreateElement("initialValue", "variable")
    el.setvalue(initval)
    var.setinitialValue(el)
    -
    +
    varlist.appendvariable(var)
    setattr(cls, "addpouVar", addpouVar)
    setattr(cls, "addpouLocalVar", addpouVar)
    -
    +
    def changepouVar(self, old_type, old_name, new_type, new_name):
    if self.interface is not None:
    content = self.interface.getcontent()
    @@ -1236,7 +1236,7 @@
    vartype_content.setname(new_type)
    return
    setattr(cls, "changepouVar", changepouVar)
    -
    +
    def removepouVar(self, var_type, name):
    if self.interface is not None:
    content = self.interface.getcontent()
    @@ -1255,14 +1255,14 @@
    if self.getbodyType() in ["SFC"]:
    for instance in self.getinstances():
    if isinstance(instance, PLCOpenParser.GetElementClass("step", "sfcObjects")) and TextMatched(instance.getname(), name):
    - return True
    + return True
    return False
    setattr(cls, "hasstep", hasstep)
    -
    +
    def hasblock(self, name=None, block_type=None):
    if self.getbodyType() in ["FBD", "LD", "SFC"]:
    for instance in self.getinstances():
    - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
    + if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
    (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))):
    return True
    if self.transitions:
    @@ -1279,7 +1279,7 @@
    return self.body[0].hasblock(block_type)
    return False
    setattr(cls, "hasblock", hasblock)
    -
    +
    def addtransition(self, name, body_type):
    if self.transitions is None:
    self.addtransitions()
    @@ -1291,7 +1291,7 @@
    if body_type == "ST":
    transition.settext(":= ;")
    setattr(cls, "addtransition", addtransition)
    -
    +
    def gettransition(self, name):
    if self.transitions is not None:
    for transition in self.transitions.gettransition():
    @@ -1299,13 +1299,13 @@
    return transition
    return None
    setattr(cls, "gettransition", gettransition)
    -
    +
    def gettransitionList(self):
    if self.transitions is not None:
    return self.transitions.gettransition()
    return []
    setattr(cls, "gettransitionList", gettransitionList)
    -
    +
    def removetransition(self, name):
    if self.transitions is not None:
    removed = False
    @@ -1314,7 +1314,7 @@
    if transition.getbodyType() in ["FBD", "LD", "SFC"]:
    for instance in transition.getinstances():
    if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")):
    - self.removepouVar(instance.gettypeName(),
    + self.removepouVar(instance.gettypeName(),
    instance.getinstanceName())
    self.transitions.remove(transition)
    removed = True
    @@ -1332,7 +1332,7 @@
    action.setname(name)
    action.setbodyType(body_type)
    setattr(cls, "addaction", addaction)
    -
    +
    def getaction(self, name):
    if self.actions is not None:
    for action in self.actions.getaction():
    @@ -1340,13 +1340,13 @@
    return action
    return None
    setattr(cls, "getaction", getaction)
    -
    +
    def getactionList(self):
    if self.actions is not None:
    return self.actions.getaction()
    return []
    setattr(cls, "getactionList", getactionList)
    -
    +
    def removeaction(self, name):
    if self.actions is not None:
    removed = False
    @@ -1355,7 +1355,7 @@
    if action.getbodyType() in ["FBD", "LD", "SFC"]:
    for instance in action.getinstances():
    if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")):
    - self.removepouVar(instance.gettypeName(),
    + self.removepouVar(instance.gettypeName(),
    instance.getinstanceName())
    self.actions.remove(action)
    removed = True
    @@ -1417,7 +1417,7 @@
    if result is not None:
    content.remove(variable)
    setattr(cls, "removeVariableByFilter", removeVariableByFilter)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    search_result = []
    filter = criteria["filter"]
    @@ -1494,7 +1494,7 @@
    def hasblock(self, name=None, block_type=None):
    if self.getbodyType() in ["FBD", "LD", "SFC"]:
    for instance in self.getinstances():
    - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
    + if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
    (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))):
    return True
    elif block_type is not None:
    @@ -1506,7 +1506,7 @@
    def updateElementAddress(self, address_model, new_leading):
    self.body.updateElementAddress(address_model, new_leading)
    -
    +
    cls = PLCOpenParser.GetElementClass("transition", "transitions")
    if cls:
    @@ -1526,7 +1526,7 @@
    setattr(cls, "hasblock", hasblock)
    setattr(cls, "updateElementName", updateElementName)
    setattr(cls, "updateElementAddress", updateElementAddress)
    -
    +
    def Search(self, criteria, parent_infos):
    search_result = []
    parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
    @@ -1554,7 +1554,7 @@
    setattr(cls, "hasblock", hasblock)
    setattr(cls, "updateElementName", updateElementName)
    setattr(cls, "updateElementAddress", updateElementAddress)
    -
    +
    def Search(self, criteria, parent_infos):
    search_result = []
    parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
    @@ -1571,24 +1571,24 @@
    def resetcurrentExecutionOrderId(self):
    object.__setattr__(self, "currentExecutionOrderId", 0)
    setattr(cls, "resetcurrentExecutionOrderId", resetcurrentExecutionOrderId)
    -
    +
    def getnewExecutionOrderId(self):
    object.__setattr__(self, "currentExecutionOrderId", self.currentExecutionOrderId + 1)
    return self.currentExecutionOrderId
    setattr(cls, "getnewExecutionOrderId", getnewExecutionOrderId)
    -
    +
    def resetexecutionOrder(self):
    if self.content.getLocalTag() == "FBD":
    for element in self.content.getcontent():
    - if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"),
    - PLCOpenParser.GetElementClass("connector", "commonObjects"),
    + if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"),
    + PLCOpenParser.GetElementClass("connector", "commonObjects"),
    PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
    element.setexecutionOrderId(0)
    self.checkedBlocksDict.clear()
    else:
    raise TypeError, _("Can only generate execution order on FBD networks!")
    setattr(cls, "resetexecutionOrder", resetexecutionOrder)
    -
    +
    def compileexecutionOrder(self):
    if self.content.getLocalTag() == "FBD":
    self.resetexecutionOrder()
    @@ -1602,7 +1602,7 @@
    else:
    raise TypeError, _("Can only generate execution order on FBD networks!")
    setattr(cls, "compileexecutionOrder", compileexecutionOrder)
    -
    +
    def compileelementExecutionOrder(self, link):
    if self.content.getLocalTag() == "FBD":
    localid = link.getrefLocalId()
    @@ -1626,7 +1626,7 @@
    else:
    raise TypeError, _("Can only generate execution order on FBD networks!")
    setattr(cls, "compileelementExecutionOrder", compileelementExecutionOrder)
    -
    +
    def setelementExecutionOrder(self, instance, new_executionOrder):
    if self.content.getLocalTag() == "FBD":
    old_executionOrder = instance.getexecutionOrderId()
    @@ -1642,21 +1642,21 @@
    else:
    raise TypeError, _("Can only generate execution order on FBD networks!")
    setattr(cls, "setelementExecutionOrder", setelementExecutionOrder)
    -
    +
    def appendcontentInstance(self, instance):
    if self.content.getLocalTag() in ["LD","FBD","SFC"]:
    self.content.appendcontent(instance)
    else:
    raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
    setattr(cls, "appendcontentInstance", appendcontentInstance)
    -
    +
    def getcontentInstances(self):
    if self.content.getLocalTag() in ["LD","FBD","SFC"]:
    return self.content.getcontent()
    else:
    raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
    setattr(cls, "getcontentInstances", getcontentInstances)
    -
    +
    instance_by_id_xpath = PLCOpen_XPath("*[@localId=$localId]")
    instance_by_name_xpath = PLCOpen_XPath("ppx:block[@instanceName=$name]")
    def getcontentInstance(self, local_id):
    @@ -1668,7 +1668,7 @@
    else:
    raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
    setattr(cls, "getcontentInstance", getcontentInstance)
    -
    +
    def getcontentInstancesIds(self):
    if self.content.getLocalTag() in ["LD","FBD","SFC"]:
    return OrderedDict([(instance.getlocalId(), True)
    @@ -1676,7 +1676,7 @@
    else:
    raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
    setattr(cls, "getcontentInstancesIds", getcontentInstancesIds)
    -
    +
    def getcontentInstanceByName(self, name):
    if self.content.getLocalTag() in ["LD","FBD","SFC"]:
    instance = instance_by_name_xpath(self.content)
    @@ -1686,7 +1686,7 @@
    else:
    raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
    setattr(cls, "getcontentInstanceByName", getcontentInstanceByName)
    -
    +
    def removecontentInstance(self, local_id):
    if self.content.getLocalTag() in ["LD","FBD","SFC"]:
    instance = instance_by_id_xpath(self.content, localId=local_id)
    @@ -1697,7 +1697,7 @@
    else:
    raise TypeError, "%s body don't have instances!"%self.content.getLocalTag()
    setattr(cls, "removecontentInstance", removecontentInstance)
    -
    +
    def settext(self, text):
    if self.content.getLocalTag() in ["IL","ST"]:
    self.content.setanyText(text)
    @@ -1711,14 +1711,14 @@
    else:
    raise TypeError, _("%s body don't have text!")%self.content.getLocalTag()
    setattr(cls, "gettext", gettext)
    -
    +
    def hasblock(self, block_type):
    if self.content.getLocalTag() in ["IL","ST"]:
    return self.content.hasblock(block_type)
    else:
    raise TypeError, _("%s body don't have text!")%self.content.getLocalTag()
    setattr(cls, "hasblock", hasblock)
    -
    +
    def updateElementName(self, old_name, new_name):
    if self.content.getLocalTag() in ["IL", "ST"]:
    self.content.updateElementName(old_name, new_name)
    @@ -1753,7 +1753,7 @@
    def setx(self, x):
    self.position.setx(x)
    -
    +
    def sety(self, y):
    self.position.sety(y)
    @@ -1828,7 +1828,7 @@
    def _translate(self, dx, dy):
    self.setx(self.getx() + dx)
    self.sety(self.gety() + dy)
    -
    +
    def _translateConnections(connectionPointIn, dx, dy):
    connections = connectionPointIn.getconnections()
    if connections is not None:
    @@ -1892,11 +1892,11 @@
    def setcontentText(self, text):
    self.content.setanyText(text)
    setattr(cls, "setcontentText", setcontentText)
    -
    +
    def getcontentText(self):
    return self.content.getanyText()
    setattr(cls, "getcontentText", getcontentText)
    -
    +
    def updateElementName(self, old_name, new_name):
    self.content.updateElementName(old_name, new_name)
    setattr(cls, "updateElementName", updateElementName)
    @@ -2004,7 +2004,7 @@
    condition.setcontent(PLCOpenParser.CreateElement("ST", "inline"))
    condition.settext(value)
    setattr(cls, "setconditionContent", setconditionContent)
    -
    +
    def getconditionContent(self):
    if self.condition is not None:
    content = self.condition.getcontent()
    @@ -2035,21 +2035,21 @@
    bbox.union(_getConnectionsBoundingBox(condition_connection))
    return bbox
    setattr(cls, "getBoundingBox", getBoundingBox)
    -
    +
    def translate(self, dx, dy):
    _translateSingle(self, dx, dy)
    condition_connection = self.getconditionConnection()
    if condition_connection is not None:
    _translateConnections(condition_connection, dx, dy)
    setattr(cls, "translate", translate)
    -
    +
    def filterConnections(self, connections):
    _filterConnectionsSingle(self, connections)
    condition_connection = self.getconditionConnection()
    if condition_connection is not None:
    _filterConnections(condition_connection, self.localId, connections)
    setattr(cls, "filterConnections", filterConnections)
    -
    +
    def updateConnectionsId(self, translation):
    connections_end = []
    if self.connectionPointIn is not None:
    @@ -2087,7 +2087,7 @@
    return condition_connection.getconnections()
    return None
    setattr(cls, "getconnections", getconnections)
    -
    +
    def Search(self, criteria, parent_infos=[]):
    parent_infos = parent_infos + ["transition", self.getlocalId()]
    search_result = []
    @@ -2104,7 +2104,7 @@
    _initElementClass("selectionConvergence", "sfcObjects", "multiple")
    _initElementClass("simultaneousDivergence", "sfcObjects", "single")
    _initElementClass("simultaneousConvergence", "sfcObjects", "multiple")
    -
    +
    cls = _initElementClass("jumpStep", "sfcObjects", "single")
    if cls:
    def Search(self, criteria, parent_infos):
    @@ -2117,7 +2117,7 @@
    if self.reference is not None:
    self.reference.setname(name)
    setattr(cls, "setreferenceName", setreferenceName)
    -
    +
    def getreferenceName(self):
    if self.reference is not None:
    return self.reference.getname()
    @@ -2129,7 +2129,7 @@
    self.inline.setcontent(PLCOpenParser.CreateElement("ST", "inline"))
    self.inline.settext(content)
    setattr(cls, "setinlineContent", setinlineContent)
    -
    +
    def getinlineContent(self):
    if self.inline is not None:
    return self.inline.gettext()
    @@ -2155,7 +2155,7 @@
    if qualifier is None:
    qualifier = "N"
    return _Search([("inline", self.getinlineContent()),
    - ("reference", self.getreferenceName()),
    + ("reference", self.getreferenceName()),
    ("qualifier", qualifier),
    ("duration", self.getduration()),
    ("indicator", self.getindicator())],
    @@ -2318,33 +2318,33 @@
    def removeconnections(self):
    self.content = None
    setattr(cls, "removeconnections", removeconnections)
    -
    +
    connection_xpath = PLCOpen_XPath("ppx:connection")
    connection_by_position_xpath = PLCOpen_XPath("ppx:connection[position()=$pos]")
    def getconnections(self):
    return connection_xpath(self)
    setattr(cls, "getconnections", getconnections)
    -
    +
    def getconnection(self, idx):
    connection = connection_by_position_xpath(self, pos=idx+1)
    if len(connection) > 0:
    return connection[0]
    return None
    setattr(cls, "getconnection", getconnection)
    -
    +
    def setconnectionId(self, idx, local_id):
    connection = self.getconnection(idx)
    if connection is not None:
    connection.setrefLocalId(local_id)
    setattr(cls, "setconnectionId", setconnectionId)
    -
    +
    def getconnectionId(self, idx):
    connection = self.getconnection(idx)
    if connection is not None:
    return connection.getrefLocalId()
    return None
    setattr(cls, "getconnectionId", getconnectionId)
    -
    +
    def setconnectionPoints(self, idx, points):
    connection = self.getconnection(idx)
    if connection is not None:
    @@ -2363,7 +2363,7 @@
    if connection is not None:
    connection.setformalParameter(parameter)
    setattr(cls, "setconnectionParameter", setconnectionParameter)
    -
    +
    def getconnectionParameter(self, idx):
    connection = self.getconnection(idx)
    if connection is not None:
    @@ -2398,7 +2398,7 @@
    content.setvalue(value)
    self.setcontent(content)
    setattr(cls, "setvalue", setvalue)
    -
    +
    def getvalue(self):
    return self.content.getvalue()
    setattr(cls, "getvalue", getvalue)
    @@ -2420,7 +2420,7 @@
    cls = PLCOpenParser.GetElementClass("arrayValue", "value")
    if cls:
    arrayValue_model = re.compile("([0-9]+)\((.*)\)$")
    -
    +
    def setvalue(self, value):
    elements = []
    for item in extractValues(value[1:-1]):
    @@ -2436,7 +2436,7 @@
    elements.append(element)
    self.value = elements
    setattr(cls, "setvalue", setvalue)
    -
    +
    def getvalue(self):
    values = []
    for element in self.value:
    @@ -2457,7 +2457,7 @@
    cls = PLCOpenParser.GetElementClass("structValue", "value")
    if cls:
    structValue_model = re.compile("(.*):=(.*)")
    -
    +
    def setvalue(self, value):
    elements = []
    for item in extractValues(value[1:-1]):
    @@ -2470,11 +2470,10 @@
    elements.append(element)
    self.value = elements
    setattr(cls, "setvalue", setvalue)
    -
    +
    def getvalue(self):
    values = []
    for element in self.value:
    values.append("%s := %s"%(element.getmember(), element.getvalue()))
    return "(%s)"%", ".join(values)
    setattr(cls, "getvalue", getvalue)
    -
    --- a/plcopen/structures.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/plcopen/structures.py Mon Aug 14 19:13:01 2017 +0300
    @@ -92,7 +92,7 @@
    """
    take a .csv file and translate it it a "csv_table"
    -"""
    +"""
    def csv_file_to_table(file):
    return [ map(string.strip,line.split(';')) for line in file.xreadlines()]
    @@ -119,18 +119,18 @@
    variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ!=''])
    standard_funtions_input_variables[variable_from_csv['name']] = variable_from_csv['type']
    return standard_funtions_input_variables
    -
    +
    """
    translate .csv file input declaration into PLCOpenEditor interessting values
    in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...}
    -return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")]
    +return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")]
    """
    def csv_input_translate(str_decl, variables, base):
    decl = str_decl.replace('(','').replace(')','').replace(' ','').split(',')
    params = []
    -
    +
    len_of_not_predifined_variable = len([True for param_type in decl if param_type not in variables])
    -
    +
    for param_type in decl:
    if param_type in variables.keys():
    param_name = param_type
    @@ -147,7 +147,7 @@
    """
    Returns this kind of declaration for all standard functions
    - [{"name" : "Numerical", 'list': [ {
    + [{"name" : "Numerical", 'list': [ {
    'baseinputnumber': 1,
    'comment': 'Addition',
    'extensible': True,
    @@ -158,19 +158,19 @@
    'type': 'function'}, ...... ] },.....]
    """
    def get_standard_funtions(table):
    -
    +
    variables = get_standard_funtions_input_variables(table)
    -
    +
    fonctions = find_section("Standard_functions_type",table)
    Standard_Functions_Decl = []
    Current_section = None
    -
    +
    translate = {
    "extensible" : lambda x: {"yes":True, "no":False}[x],
    "inputs" : lambda x:csv_input_translate(x,variables,baseinputnumber),
    "outputs":lambda x:[("OUT",x,"none")]}
    -
    +
    for fields in table:
    if fields[1]:
    # If function section name given
    @@ -191,14 +191,14 @@
    if param in translate:
    Function_decl[param] = translate[param](value)
    Function_decl["type"] = "function"
    -
    +
    if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*') :
    input_ovrloading_types = GetSubTypes(Function_decl["inputs"][0][1])
    output_types = GetSubTypes(Function_decl["outputs"][0][1])
    else:
    input_ovrloading_types = [None]
    output_types = [None]
    -
    +
    funcdeclname_orig = Function_decl["name"]
    funcdeclname = Function_decl["name"].strip('*_')
    fdc = Function_decl["inputs"][:]
    @@ -210,14 +210,14 @@
    Function_decl["inputs"] += [(decl_tpl[0], intype, decl_tpl[2])]
    else:
    Function_decl["inputs"] += [(decl_tpl)]
    -
    +
    if funcdeclname_orig.startswith('*'):
    - funcdeclin = intype + '_' + funcdeclname
    + funcdeclin = intype + '_' + funcdeclname
    else:
    funcdeclin = funcdeclname
    else:
    funcdeclin = funcdeclname
    -
    +
    for outype in output_types:
    if outype != None:
    decl_tpl = Function_decl["outputs"][0]
    @@ -234,7 +234,7 @@
    filter_name = Function_decl["filter"]
    store = True
    for (InTypes, OutTypes) in ANY_TO_ANY_FILTERS.get(filter_name,[]):
    - outs = reduce(lambda a,b: a or b,
    + outs = reduce(lambda a,b: a or b,
    map(lambda testtype : IsOfType(
    Function_decl["outputs"][0][1],
    testtype), OutTypes))
    @@ -253,7 +253,7 @@
    Current_section["list"].append(Function_decl_copy)
    else:
    raise "First function must be in a category"
    -
    +
    return Standard_Functions_Decl
    StdBlckLst.extend(get_standard_funtions(csv_file_to_table(open(StdFuncsCSV))))
    @@ -266,10 +266,10 @@
    words = desc["comment"].split('"')
    if len(words) > 1:
    desc["comment"] = words[1]
    - desc["usage"] = ("\n (%s) => (%s)" %
    - (", ".join(["%s:%s" % (input[1], input[0])
    + desc["usage"] = ("\n (%s) => (%s)" %
    + (", ".join(["%s:%s" % (input[1], input[0])
    for input in desc["inputs"]]),
    - ", ".join(["%s:%s" % (output[1], output[0])
    + ", ".join(["%s:%s" % (output[1], output[0])
    for output in desc["outputs"]])))
    BlkLst = StdBlckDct.setdefault(desc["name"],[])
    BlkLst.append((section["name"], desc))
    @@ -321,7 +321,7 @@
    # Keywords for Structured Text
    ST_BLOCK_START_KEYWORDS = ["IF", "ELSIF", "ELSE", "CASE", "FOR", "WHILE", "REPEAT"]
    ST_BLOCK_END_KEYWORDS = ["END_IF", "END_CASE", "END_FOR", "END_WHILE", "END_REPEAT"]
    -ST_KEYWORDS = ["TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT",
    +ST_KEYWORDS = ["TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT",
    "RETURN", "NOT", "MOD", "AND", "XOR", "OR"] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS
    # All the keywords of IEC
    @@ -338,5 +338,3 @@
    SFC_KEYWORDS, IL_KEYWORDS, ST_KEYWORDS])]:
    for keywords in keywords_list:
    all_keywords.extend([keyword for keyword in keywords if keyword not in all_keywords])
    -
    -
    --- a/py_ext/PythonEditor.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/py_ext/PythonEditor.py Mon Aug 14 19:13:01 2017 +0300
    @@ -32,12 +32,12 @@
    KEYWORDS = keyword.kwlist
    COMMENT_HEADER = "#"
    -
    +
    def SetCodeLexer(self):
    self.SetLexer(stc.STC_LEX_PYTHON)
    -
    +
    # Line numbers in margin
    - self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces)
    + self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces)
    # Highlighted brace
    self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces)
    # Unmatched brace
    @@ -69,15 +69,14 @@
    # Identifiers. I leave this as not bold because everything seems
    # to be an identifier if it doesn't match the above criterae
    self.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces)
    -
    +
    #-------------------------------------------------------------------------------
    # CFileEditor Main Frame Class
    #-------------------------------------------------------------------------------
    class PythonEditor(CodeFileEditor):
    -
    +
    CONFNODEEDITOR_TABS = [
    (_("Python code"), "_create_CodePanel")]
    CODE_EDITOR = PythonCodeEditor
    -
    --- a/py_ext/py_ext.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/py_ext/py_ext.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,10 +30,10 @@
    class PythonLibrary(POULibrary):
    def GetLibraryPath(self):
    - return paths.AbsNeighbourFile(__file__, "pous.xml")
    + return paths.AbsNeighbourFile(__file__, "pous.xml")
    def Generate_C(self, buildpath, varlist, IECCFLAGS):
    -
    +
    plc_python_filepath = paths.AbsNeighbourFile(__file__, "plc_python.c")
    plc_python_file = open(plc_python_filepath, 'r')
    plc_python_code = plc_python_file.read()
    @@ -44,21 +44,19 @@
    "PYTHON_POLL"]:
    python_eval_fb_list.append(v)
    python_eval_fb_count = max(1, len(python_eval_fb_list))
    -
    +
    # prepare python code
    plc_python_code = plc_python_code % {
    "python_eval_fb_count": python_eval_fb_count }
    -
    +
    Gen_Pythonfile_path = os.path.join(buildpath, "py_ext.c")
    pythonfile = open(Gen_Pythonfile_path,'w')
    pythonfile.write(plc_python_code)
    pythonfile.close()
    -
    +
    return (["py_ext"], [(Gen_Pythonfile_path, IECCFLAGS)], True), ""
    class PythonFile(PythonFileCTNMixin):
    -
    +
    def GetIconName(self):
    return "Pyfile"
    -
    -
    --- a/runtime/PLCObject.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/runtime/PLCObject.py Mon Aug 14 19:13:01 2017 +0300
    @@ -143,8 +143,8 @@
    self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
    self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID")
    - if len(md5) == 32 :
    - self.PLC_ID.value = md5
    + if len(md5) == 32 :
    + self.PLC_ID.value = md5
    self._startPLC = self.PLClibraryHandle.startPLC
    self._startPLC.restype = ctypes.c_int
    @@ -556,5 +556,3 @@
    return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" %
    (line_no, e_value, script.splitlines()[line_no - 1]))
    return (0, kwargs.get("returnVal", None))
    -
    -
    --- a/runtime/ServicePublisher.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/runtime/ServicePublisher.py Mon Aug 14 19:13:01 2017 +0300
    @@ -30,20 +30,20 @@
    def __init__(self):
    # type: fully qualified service type name
    self.serviceproperties = {'description':'Beremiz remote PLC'}
    -
    +
    self.name = None
    self.ip_32b = None
    self.port = None
    self.server = None
    self.service_name = None
    self.retrytimer = None
    -
    +
    def RegisterService(self, name, ip, port):
    try:
    self._RegisterService(name, ip, port)
    except Exception,e:
    self.retrytimer = threading.Timer(2,self.RegisterService,[name, ip, port])
    - self.retrytimer.start()
    + self.retrytimer.start()
    def _RegisterService(self, name, ip, port):
    # name: fully qualified service name
    @@ -66,20 +66,20 @@
    self.port,
    properties = self.serviceproperties))
    self.retrytimer=None
    -
    +
    def UnRegisterService(self):
    if self.retrytimer is not None:
    self.retrytimer.cancel()
    self.server.unregisterService(
    - Zeroconf.ServiceInfo(service_type,
    - self.service_name,
    - self.ip_32b,
    - self.port,
    + Zeroconf.ServiceInfo(service_type,
    + self.service_name,
    + self.ip_32b,
    + self.port,
    properties = self.serviceproperties))
    self.server.close()
    self.server = None
    -
    +
    def gethostaddr(self, dst = '224.0.1.41'):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
    --- a/svgui/pyjs/build.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/build.py Mon Aug 14 19:13:01 2017 +0300
    @@ -306,7 +306,7 @@
    # Second, (dynamic only), post-analyse the places where modules
    # haven't changed
    # Third, write everything out.
    -
    +
    for platform in app_platforms:
    mod_code[platform] = {}
    @@ -337,11 +337,11 @@
    print appcode
    #mod_code[platform][app_name] = appcode
    - # platform.Module.cache.js
    + # platform.Module.cache.js
    modules_done = ['pyjslib', 'sys', '_pyjs.js']
    #modules_to_do = [app_name] + app_translator.library_modules
    - modules_to_do = [app_name] + app_translator.library_modules
    + modules_to_do = [app_name] + app_translator.library_modules
    dependencies = {}
    @@ -403,9 +403,9 @@
    deps = uniquify(deps)
    #print "modname:", mod_name, deps
    dependencies[mod_name] = deps
    -
    +
    # work out the dependency ordering of the modules
    -
    +
    mod_levels[platform] = make_deps(None, dependencies, modules_done)
    # now write everything out
    @@ -473,7 +473,7 @@
    app_libs_ += mod_cache_html_output.read()
    # write out the dependency ordering of the modules
    -
    +
    app_modnames = []
    for md in mod_levels[platform]:
    @@ -512,7 +512,7 @@
    file_name = "%s.%s.%s" % (platform.lower(), app_name, digest)
    else:
    file_name = "%s.%s" % (platform.lower(), app_name)
    - file_name += ".cache.html"
    + file_name += ".cache.html"
    out_path = join(output, file_name)
    out_file = open(out_path, 'w')
    out_file.write(file_contents)
    @@ -599,7 +599,7 @@
    if has_nodeps(mod, deps):
    res.append(mod)
    return res
    -
    +
    # this function takes a dictionary of dependent modules and
    # creates a list of lists. the first list will be modules
    # that have no dependencies; the second list will be those
    @@ -721,4 +721,3 @@
    if __name__ == "__main__":
    main()
    -
    --- a/svgui/pyjs/jsonrpc/django/jsonrpc.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/jsonrpc/django/jsonrpc.py Mon Aug 14 19:13:01 2017 +0300
    @@ -19,13 +19,13 @@
    # (r'^service1/$', 'djangoapp.views.jsonservice'),
    class JSONRPCService(JSONRPCServiceBase):
    -
    +
    def __call__(self, request, extra=None):
    return self.process(request.raw_post_data)
    def jsonremote(service):
    """Make JSONRPCService a decorator so that you can write :
    -
    +
    from jsonrpc import JSONRPCService
    chatservice = JSONRPCService()
    @@ -62,7 +62,7 @@
    # part of the app:
    # (r'^formsservice/$', 'djangoapp.views.processor'),
    -from django import forms
    +from django import forms
    def builderrors(form):
    d = {}
    @@ -223,4 +223,3 @@
    for item in serialize('python', l, fields=fields):
    res.append(dict_datetimeflatten(item))
    return res
    -
    --- a/svgui/pyjs/jsonrpc/jsonrpc.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/jsonrpc/jsonrpc.py Mon Aug 14 19:13:01 2017 +0300
    @@ -39,5 +39,4 @@
    return self.error(id, 100, 'method "%s" does not exist' % method)
    def listmethods(self):
    - return self.methods.keys()
    -
    + return self.methods.keys()
    --- a/svgui/pyjs/lib/pyjslib.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/lib/pyjslib.py Mon Aug 14 19:13:01 2017 +0300
    @@ -20,7 +20,7 @@
    # must declare import _before_ importing sys
    def import_module(path, parent_module, module_name, dynamic=1, async=False):
    - """
    + """
    """
    JS("""
    @@ -38,7 +38,7 @@
    }
    var override_name = sys.platform + "." + module_name;
    - if (((sys.overrides != null) &&
    + if (((sys.overrides != null) &&
    (sys.overrides.has_key(override_name))))
    {
    cache_file = sys.overrides.__getitem__(override_name) ;
    @@ -67,7 +67,7 @@
    module_load_request[module_name] = 1;
    }
    - /* following a load, this first executes the script
    + /* following a load, this first executes the script
    * "preparation" function MODULENAME_loaded_fn()
    * and then sets up the loaded module in the namespace
    * of the parent.
    @@ -201,7 +201,7 @@
    self.parent_mod = parent_mod
    def next(self):
    -
    +
    for i in range(len(self.app_modlist[self.idx])):
    app = self.app_modlist[self.idx][i]
    import_module(self.path, self.parent_mod, app, self.dynamic, True);
    @@ -1109,7 +1109,7 @@
    if pyjslib.isUndefined(object_):
    return False
    if not pyjslib.isObject(object_):
    -
    +
    return False
    if _isinstance(classinfo, Tuple):
    for ci in classinfo:
    @@ -1362,4 +1362,3 @@
    if bases:
    JS("bss = bases.l;")
    JS(" return pyjs_type(clsname, bss, mths); ")
    -
    --- a/svgui/pyjs/lib/sys.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/lib/sys.py Mon Aug 14 19:13:01 2017 +0300
    @@ -5,11 +5,11 @@
    overrides = None # to be updated by app, on compile
    # the remote path for loading modules
    -loadpath = None
    +loadpath = None
    -stacktrace = None
    +stacktrace = None
    -appname = None
    +appname = None
    def setloadpath(lp):
    global loadpath
    --- a/svgui/pyjs/pyjs.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/pyjs/pyjs.py Mon Aug 14 19:13:01 2017 +0300
    @@ -345,7 +345,7 @@
    # added to the dependencies, and it's half way up the
    # module import directory structure!
    child_name = name[-1]
    - self.imported_modules_as.append(child_name)
    + self.imported_modules_as.append(child_name)
    print >> self.output, gen_mod_import(self.raw_module_name,
    strip_py(importName),
    self.dynamic)
    @@ -427,7 +427,7 @@
    print >>self.output, "%s = function%s {" % (function_name, function_args)
    self._default_args_handler(node, normal_arg_names, None)
    - local_arg_names = normal_arg_names + declared_arg_names
    + local_arg_names = normal_arg_names + declared_arg_names
    if node.varargs:
    self._varargs_handler(node, varargname, declared_arg_names, None)
    @@ -539,12 +539,12 @@
    except ValueError:
    # Must be a function call ...
    return ("pyjs_kwargs_function_call("+call_name+", "
    - + star_arg_name
    + + star_arg_name
    + ", ["+fn_args+"]"
    + ")" )
    else:
    return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', "
    - + star_arg_name
    + + star_arg_name
    + ", ["+fn_args+"]"
    + ")")
    else:
    @@ -628,7 +628,7 @@
    def modpfx(self):
    return strip_py(self.module_prefix)
    -
    +
    def _name(self, v, current_klass, top_level=False,
    return_none_for_module=False):
    @@ -904,7 +904,7 @@
    # default arguments
    self._default_args_handler(node, normal_arg_names, current_klass)
    - local_arg_names = normal_arg_names + declared_arg_names
    + local_arg_names = normal_arg_names + declared_arg_names
    if node.varargs:
    self._varargs_handler(node, varargname, declared_arg_names, current_klass)
    @@ -1170,7 +1170,7 @@
    def _discard(self, node, current_klass):
    -
    +
    if isinstance(node.expr, ast.CallFunc):
    debugStmt = self.debug and not self._isNativeFunc(node)
    if debugStmt and isinstance(node.expr.node, ast.Name) and \
    @@ -1774,4 +1774,3 @@
    if __name__ == "__main__":
    main()
    -
    --- a/svgui/svgui.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/svgui.py Mon Aug 14 19:13:01 2017 +0300
    @@ -35,7 +35,7 @@
    class SVGUILibrary(POULibrary):
    def GetLibraryPath(self):
    - return paths.AbsNeighbourFile(__file__, "pous.xml")
    + return paths.AbsNeighbourFile(__file__, "pous.xml")
    class SVGUI(PythonFileCTNMixin):
    @@ -70,18 +70,18 @@
    def CTNGenerate_C(self, buildpath, locations):
    """
    - Return C code generated by iec2c compiler
    + Return C code generated by iec2c compiler
    when _generate_softPLC have been called
    @param locations: ignored
    @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
    """
    -
    +
    current_location = self.GetCurrentLocation()
    # define a unique name for the generated C file
    location_str = "_".join(map(lambda x:str(x), current_location))
    -
    +
    res = ([], "", False)
    -
    +
    svgfile=self._getSVGpath()
    if os.path.exists(svgfile):
    res += (("gui.svg", file(svgfile,"rb")),)
    @@ -103,25 +103,25 @@
    svguilibfile.close()
    jsmodules = {"LiveSVGPage": "svguilib.js"}
    res += (("svguilib.js", file(svguilibpath,"rb")),)
    -
    +
    runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str)
    runtimefile = open(runtimefile_path, 'w')
    runtimefile.write(svguiservercode % {"svgfile" : "gui.svg"})
    runtimefile.write("""
    def _runtime_%(location)s_start():
    website.LoadHMI(%(svgui_class)s, %(jsmodules)s)
    -
    +
    def _runtime_%(location)s_stop():
    website.UnLoadHMI()
    -
    +
    """ % {"location": location_str,
    "svgui_class": "SVGUI_HMI",
    "jsmodules" : str(jsmodules),
    })
    runtimefile.close()
    -
    +
    res += (("runtime_%s.py"%location_str, file(runtimefile_path,"rb")),)
    -
    +
    return res
    def _ImportSVG(self):
    @@ -132,7 +132,7 @@
    shutil.copy(svgpath, self._getSVGpath())
    else:
    self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n")%svgpath)
    - dialog.Destroy()
    + dialog.Destroy()
    def _StartInkscape(self):
    svgfile = self._getSVGpath()
    --- a/svgui/svgui_server.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/svgui_server.py Mon Aug 14 19:13:01 2017 +0300
    @@ -38,7 +38,7 @@
    return currentId
    class SvguiWidget:
    -
    +
    def __init__(self, classname, id, **kwargs):
    self.classname = classname
    self.id = id
    @@ -50,7 +50,7 @@
    def setinput(self, attrname, value):
    self.inputs[attrname] = value
    -
    +
    def getinput(self, attrname, default=None):
    if not self.inputs.has_key(attrname):
    self.inputs[attrname] = default
    @@ -61,14 +61,14 @@
    self.outputs[attrname] = value
    self.changed = True
    self.RefreshInterface()
    -
    +
    def updateoutputs(self, **kwargs):
    for attrname, value in kwargs.iteritems():
    if self.outputs.get(attrname) != value:
    self.outputs[attrname] = value
    self.changed = True
    self.RefreshInterface()
    -
    +
    def RefreshInterface(self):
    interface = website.getHMI()
    if isinstance(interface, SVGUI_HMI) and self.changed and not self.inhibit:
    @@ -77,7 +77,7 @@
    if d is not None:
    self.inhibit = True
    d.addCallback(self.InterfaceRefreshed)
    -
    +
    def InterfaceRefreshed(self, result):
    self.inhibit = False
    if self.changed:
    @@ -103,23 +103,23 @@
    class SVGUI_HMI(website.PLCHMI):
    jsClass = u"LiveSVGPage.LiveSVGWidget"
    -
    - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    +
    + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
    tags.xml(loaders.xmlfile(os.path.join(WorkingDir, svgfile))),
    ])
    -
    +
    def HMIinitialisation(self):
    gadgets = []
    for gadget in svguiWidgets.values():
    gadgets.append(unicode(json.dumps(gadget, default=get_object_init_state, indent=2), 'ascii'))
    d = self.callRemote('init', gadgets)
    d.addCallback(self.HMIinitialised)
    -
    +
    def sendData(self,data):
    if self.initialised:
    return self.callRemote('receiveData',unicode(json.dumps(data, default=get_object_current_state, indent=2), 'ascii'))
    return None
    -
    +
    def setattr(self, id, attrname, value):
    svguiWidgets[id].setinput(attrname, value)
    @@ -148,4 +148,3 @@
    if gad is not None:
    return gad.getinput(attrname, default)
    return default
    -
    --- a/svgui/svguilib.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/svgui/svguilib.py Mon Aug 14 19:13:01 2017 +0300
    @@ -23,7 +23,7 @@
    # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    class button:
    -
    +
    def __init__(self, parent, id, args):
    self.parent = parent
    self.id = id
    @@ -40,19 +40,19 @@
    self.up = not self.state
    else:
    self.up = True
    -
    +
    # Add event on each element of the button
    if self.active:
    self.back_elt.addEventListener("mouseup", self, False)
    self.back_elt.addEventListener("mousedown", self, False)
    self.back_elt.addEventListener("mouseover", self, False)
    self.back_elt.addEventListener("mouseout", self, False)
    -
    +
    self.sele_elt.addEventListener("mouseup", self, False)
    self.sele_elt.addEventListener("mousedown", self, False)
    self.sele_elt.addEventListener("mouseover", self, False)
    self.sele_elt.addEventListener("mouseout", self, False)
    -
    +
    blockSVGElementDrag(self.back_elt)
    blockSVGElementDrag(self.sele_elt)
    @@ -66,7 +66,7 @@
    else:
    self.sele_elt.removeAttribute("display")
    self.back_elt.setAttribute("display", "none")
    -
    +
    def updateValues(self, values):
    if values.state != self.state:
    self.state = values.state
    @@ -80,9 +80,9 @@
    if evt.type == "mousedown":
    evt.stopPropagation()
    setCurrentObject(self)
    -
    +
    self.dragging = True
    -
    +
    if self.toggle:
    self.up = self.state
    else:
    @@ -90,18 +90,18 @@
    self.state = True
    updateAttr(self.id, 'state', self.state)
    self.updateElements()
    -
    +
    if isCurrentObject(self) and self.dragging:
    # Quand le bouton est survole
    if evt.type == "mouseover" and self.toggle:
    self.up = self.state
    self.updateElements()
    -
    +
    # Quand le curseur quitte la zone du bouton
    - elif evt.type == "mouseout" and self.toggle:
    + elif evt.type == "mouseout" and self.toggle:
    self.up = not self.state
    self.updateElements()
    -
    +
    # Quand le bouton de la souris est relache
    elif evt.type == "mouseup":
    evt.stopPropagation()
    @@ -116,7 +116,7 @@
    self.dragging = False
    class textControl:
    -
    +
    def __init__(self, parent, id, args):
    self.parent = parent
    self.id = id
    @@ -126,17 +126,15 @@
    else:
    self.text = ""
    self.updateElements()
    -
    +
    def updateValues(self, values):
    if values.text != self.value:
    self.text = values.text
    updateAttr(self.id, 'text', self.text)
    self.updateElements()
    -
    +
    def updateElements(self):
    self.back_elt.firstChild.firstChild.textContent = self.text
    -
    +
    def handleEvent(self, evt):
    pass
    -
    -
    --- a/targets/__init__.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/targets/__init__.py Mon Aug 14 19:13:01 2017 +0300
    @@ -41,14 +41,14 @@
    def _GetLocalTargetClassFactory(name):
    return lambda:getattr(__import__(name,globals(),locals()), name+"_target")
    -targets = dict([(name, {"xsd":path.join(_base_path, name, "XSD"),
    +targets = dict([(name, {"xsd":path.join(_base_path, name, "XSD"),
    "class":_GetLocalTargetClassFactory(name),
    - "code": { fname: path.join(_base_path, name, fname)
    + "code": { fname: path.join(_base_path, name, fname)
    for fname in listdir(path.join(_base_path, name))
    if fname.startswith("plc_%s_main"%name) and
    fname.endswith(".c")}})
    - for name in listdir(_base_path)
    - if path.isdir(path.join(_base_path, name))
    + for name in listdir(_base_path)
    + if path.isdir(path.join(_base_path, name))
    and not name.startswith("__")])
    toolchains = {"gcc": path.join(_base_path, "XSD_toolchain_gcc"),
    @@ -67,7 +67,7 @@
    DictXSD_toolchain["toolchain_"+toolchainname] = \
    open(xsdfilename).read()
    - # Get all xsd targets
    + # Get all xsd targets
    for targetname,nfo in targets.iteritems():
    xsd_string = open(nfo["xsd"]).read()
    targetchoices += xsd_string%DictXSD_toolchain
    @@ -86,4 +86,3 @@
    def GetCode(name):
    filename = paths.AbsNeighbourFile(__file__,name)
    return open(filename).read()
    -
    --- a/targets/toolchain_makefile.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/targets/toolchain_makefile.py Mon Aug 14 19:13:01 2017 +0300
    @@ -33,7 +33,7 @@
    class toolchain_makefile():
    def __init__(self, CTRInstance):
    self.CTRInstance = CTRInstance
    - self.md5key = None
    + self.md5key = None
    self.buildpath = None
    self.SetBuildPath(self.CTRInstance._getBuildPath())
    @@ -83,7 +83,7 @@
    def build(self):
    srcfiles= []
    cflags = []
    - wholesrcdata = ""
    + wholesrcdata = ""
    for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS:
    # Get CFiles list to give it to makefile
    for CFile, CFLAGS in CFilesAndCFLAGS:
    @@ -92,7 +92,7 @@
    srcfiles.append(CFileName)
    if CFLAGS not in cflags:
    cflags.append(CFLAGS)
    -
    +
    oldmd5 = self.md5key
    self.md5key = hashlib.md5(wholesrcdata).hexdigest()
    @@ -108,8 +108,8 @@
    "md5": self.md5key,
    "buildpath": self.buildpath
    }
    -
    - # clean sequence of multiple whitespaces
    +
    + # clean sequence of multiple whitespaces
    cmd = re.sub(r"[ ]+", " ", target.getCommand().strip())
    command = [ token % beremizcommand for token in cmd.split(' ')]
    @@ -125,4 +125,3 @@
    else :
    self.CTRInstance.logger.write(_("Source didn't change, no build.\n"))
    return True
    -
    --- a/util/ProcessLogger.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/util/ProcessLogger.py Mon Aug 14 19:13:01 2017 +0300
    @@ -53,7 +53,7 @@
    self.retval = self.Proc.poll()
    else:
    self.retval = self.Proc.returncode
    -
    +
    outchunk = self.fd.readline()
    if self.callback : self.callback(outchunk)
    while outchunk != '' and not self.killed :
    @@ -108,7 +108,7 @@
    self.errdata = []
    self.keyword = keyword
    self.kill_it = kill_it
    - self.startsem = Semaphore(0)
    + self.startsem = Semaphore(0)
    self.finishsem = Semaphore(0)
    self.endlock = Lock()
    @@ -130,7 +130,7 @@
    self.timeout.start()
    else:
    self.timeout = None
    -
    +
    self.Proc = subprocess.Popen( self.Command, **popenargs )
    self.outt = outputThread(
    @@ -169,7 +169,7 @@
    self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1 = str(ecode), a2 = str(pid)))
    def finish(self, pid,ecode):
    - # avoid running function before start is finished
    + # avoid running function before start is finished
    self.startsem.acquire()
    if self.timeout:
    self.timeout.cancel()
    @@ -185,7 +185,7 @@
    # avoid running kill before start is finished
    self.startsem.acquire()
    self.startsem.release()
    -
    +
    self.outt.killed = True
    self.errt.killed = True
    if wx.Platform == '__WXMSW__':
    @@ -215,4 +215,3 @@
    def spin(self):
    self.finishsem.acquire()
    return [self.exitcode, "".join(self.outdata), "".join(self.errdata)]
    -
    --- a/util/TranslationCatalogs.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/util/TranslationCatalogs.py Mon Aug 14 19:13:01 2017 +0300
    @@ -22,7 +22,7 @@
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    -import os
    +import os
    import wx
    --- a/util/Zeroconf.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/util/Zeroconf.py Mon Aug 14 19:13:01 2017 +0300
    @@ -19,7 +19,7 @@
    You should have received a copy of the GNU Lesser 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
    -
    +
    """
    """0.12 update - allow selection of binding interface
    @@ -101,7 +101,7 @@
    _BROWSER_TIME = 500
    # Some DNS constants
    -
    +
    _MDNS_ADDR = '224.0.0.251'
    _MDNS_PORT = 5353;
    _DNS_PORT = 53;
    @@ -208,7 +208,7 @@
    class DNSEntry(object):
    """A DNS entry"""
    -
    +
    def __init__(self, name, type, clazz):
    self.key = string.lower(name)
    self.name = name
    @@ -256,7 +256,7 @@
    class DNSQuestion(DNSEntry):
    """A DNS question entry"""
    -
    +
    def __init__(self, name, type, clazz):
    if not name.endswith(".local."):
    raise NonLocalNameException
    @@ -273,7 +273,7 @@
    class DNSRecord(DNSEntry):
    """A DNS record - like a DNS entry, but has a TTL"""
    -
    +
    def __init__(self, name, type, clazz, ttl):
    DNSEntry.__init__(self, name, type, clazz)
    self.ttl = ttl
    @@ -334,7 +334,7 @@
    class DNSAddress(DNSRecord):
    """A DNS address record"""
    -
    +
    def __init__(self, name, type, clazz, ttl, address):
    DNSRecord.__init__(self, name, type, clazz, ttl)
    self.address = address
    @@ -378,10 +378,10 @@
    def __repr__(self):
    """String representation"""
    return self.cpu + " " + self.os
    -
    +
    class DNSPointer(DNSRecord):
    """A DNS pointer record"""
    -
    +
    def __init__(self, name, type, clazz, ttl, alias):
    DNSRecord.__init__(self, name, type, clazz, ttl)
    self.alias = alias
    @@ -402,7 +402,7 @@
    class DNSText(DNSRecord):
    """A DNS text record"""
    -
    +
    def __init__(self, name, type, clazz, ttl, text):
    DNSRecord.__init__(self, name, type, clazz, ttl)
    self.text = text
    @@ -426,7 +426,7 @@
    class DNSService(DNSRecord):
    """A DNS service record"""
    -
    +
    def __init__(self, name, type, clazz, ttl, priority, weight, port, server):
    DNSRecord.__init__(self, name, type, clazz, ttl)
    self.priority = priority
    @@ -453,7 +453,7 @@
    class DNSIncoming(object):
    """Object representation of an incoming DNS packet"""
    -
    +
    def __init__(self, data):
    """Constructor from string holding bytes of packet"""
    self.offset = 0
    @@ -464,7 +464,7 @@
    self.numAnswers = 0
    self.numAuthorities = 0
    self.numAdditionals = 0
    -
    +
    self.readHeader()
    self.readQuestions()
    self.readOthers()
    @@ -491,7 +491,7 @@
    name = self.readName()
    info = struct.unpack(format, self.data[self.offset:self.offset+length])
    self.offset += length
    -
    +
    question = DNSQuestion(name, info[0], info[1])
    self.questions.append(question)
    @@ -561,7 +561,7 @@
    if rec is not None:
    self.answers.append(rec)
    -
    +
    def isQuery(self):
    """Returns true if this is a query"""
    return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY
    @@ -574,7 +574,7 @@
    """Reads a UTF-8 string of a given length from the packet"""
    result = self.data[offset:offset+len].decode('utf-8')
    return result
    -
    +
    def readName(self):
    """Reads a domain name from the packet"""
    result = ''
    @@ -607,11 +607,11 @@
    self.offset = off
    return result
    -
    -
    +
    +
    class DNSOutgoing(object):
    """Object representation of an outgoing packet"""
    -
    +
    def __init__(self, flags, multicast = 1):
    self.finished = 0
    self.id = 0
    @@ -620,7 +620,7 @@
    self.names = {}
    self.data = []
    self.size = 12
    -
    +
    self.questions = []
    self.answers = []
    self.authorities = []
    @@ -660,7 +660,7 @@
    format = '!H'
    self.data.insert(index, struct.pack(format, value))
    self.size += 2
    -
    +
    def writeShort(self, value):
    """Writes an unsigned short to the packet"""
    format = '!H'
    @@ -739,7 +739,7 @@
    self.size += 2
    record.write(self)
    self.size -= 2
    -
    +
    length = len(''.join(self.data[index:]))
    self.insertShort(index, length) # Here is the short we adjusted for
    @@ -758,7 +758,7 @@
    self.writeRecord(authority, 0)
    for additional in self.additionals:
    self.writeRecord(additional, 0)
    -
    +
    self.insertShort(0, len(self.additionals))
    self.insertShort(0, len(self.authorities))
    self.insertShort(0, len(self.answers))
    @@ -773,7 +773,7 @@
    class DNSCache(object):
    """A cache of DNS entries"""
    -
    +
    def __init__(self):
    self.cache = {}
    @@ -872,7 +872,7 @@
    result = self.readers.keys()
    self.condition.release()
    return result
    -
    +
    def addReader(self, reader, socket):
    self.condition.acquire()
    self.readers[socket] = reader
    @@ -897,7 +897,7 @@
    It requires registration with an Engine object in order to have
    the read() method called when a socket is availble for reading."""
    -
    +
    def __init__(self, zeroconf):
    self.zeroconf = zeroconf
    self.zeroconf.engine.addReader(self, self.zeroconf.socket)
    @@ -924,7 +924,7 @@
    class Reaper(threading.Thread):
    """A Reaper is used by this module to remove cache entries that
    have expired."""
    -
    +
    def __init__(self, zeroconf):
    threading.Thread.__init__(self)
    self.zeroconf = zeroconf
    @@ -948,7 +948,7 @@
    The listener object will have its addService() and
    removeService() methods called when this browser
    discovers changes in the services availability."""
    -
    +
    def __init__(self, zeroconf, type, listener):
    """Creates a browser for a specific type"""
    threading.Thread.__init__(self)
    @@ -959,7 +959,7 @@
    self.nextTime = currentTimeMillis()
    self.delay = _BROWSER_TIME
    self.list = []
    -
    +
    self.done = 0
    self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
    @@ -1019,11 +1019,11 @@
    if event is not None:
    event(self.zeroconf)
    -
    +
    class ServiceInfo(object):
    """Service information"""
    -
    +
    def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None):
    """Create a service description.
    @@ -1089,7 +1089,7 @@
    index += 1
    strs.append(text[index:index+length])
    index += length
    -
    +
    for s in strs:
    eindex = s.find('=')
    if eindex == -1:
    @@ -1112,7 +1112,7 @@
    except:
    traceback.print_exc()
    self.properties = None
    -
    +
    def getType(self):
    """Type accessor"""
    return self.type
    @@ -1200,7 +1200,7 @@
    result = 1
    finally:
    zeroconf.removeListener(self)
    -
    +
    return result
    def __eq__(self, other):
    @@ -1225,7 +1225,7 @@
    result += self.text[:17] + "..."
    result += "]"
    return result
    -
    +
    class Zeroconf(object):
    """Implementation of Zeroconf Multicast DNS Service Discovery
    @@ -1274,7 +1274,7 @@
    self.cache = DNSCache()
    self.condition = threading.Condition()
    -
    +
    self.engine = Engine(self)
    self.listener = Listener(self)
    self.reaper = Reaper(self)
    @@ -1465,7 +1465,7 @@
    record = entry
    else:
    self.cache.add(record)
    -
    +
    self.updateRecord(now, record)
    def handleQuery(self, msg, addr, port):
    @@ -1479,7 +1479,7 @@
    out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0)
    for question in msg.questions:
    out.addQuestion(question)
    -
    +
    for question in msg.questions:
    if question.type == _TYPE_PTR:
    for service in self.services.values():
    @@ -1491,16 +1491,16 @@
    try:
    if out is None:
    out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
    -
    +
    # Answer A record queries for any service addresses we know
    if question.type == _TYPE_A or question.type == _TYPE_ANY:
    for service in self.services.values():
    if service.server == question.name.lower():
    out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
    -
    +
    service = self.services.get(question.name.lower(), None)
    if not service: continue
    -
    +
    if question.type == _TYPE_SRV or question.type == _TYPE_ANY:
    out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server))
    if question.type == _TYPE_TXT or question.type == _TYPE_ANY:
    @@ -1509,7 +1509,7 @@
    out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
    except:
    traceback.print_exc()
    -
    +
    if out is not None and out.answers:
    out.id = msg.id
    self.send(out, addr, port)
    @@ -1534,11 +1534,11 @@
    self.unregisterAllServices()
    self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
    self.socket.close()
    -
    +
    # Test a few module features, including service registration, service
    # query (for Zoe), and service unregistration.
    -if __name__ == '__main__':
    +if __name__ == '__main__':
    print "Multicast DNS Service Discovery for Python, version", __version__
    r = Zeroconf()
    print "1. Testing registration of a service..."
    --- a/wxglade_hmi/wxglade_hmi.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/wxglade_hmi/wxglade_hmi.py Mon Aug 14 19:13:01 2017 +0300
    @@ -56,7 +56,7 @@
    path = None
    try:
    from wxglade import __file__ as fileName
    - path = os.path.dirname(fileName)
    + path = os.path.dirname(fileName)
    return path
    except ImportError:
    pass
    @@ -66,7 +66,7 @@
    path = defLibDir
    return path
    -
    +
    def launch_wxglade(self, options, wait=False):
    path = self.GetWxGladePath()
    glade = os.path.join(path, 'wxglade.py')
    @@ -82,24 +82,24 @@
    return PythonFileCTNMixin.OnCTNSave(self, from_project_path)
    def CTNGenerate_C(self, buildpath, locations):
    -
    +
    hmi_frames = []
    -
    +
    wxgfile_path=self._getWXGLADEpath()
    if os.path.exists(wxgfile_path):
    wxgfile = open(wxgfile_path, 'r')
    wxgtree = minidom.parse(wxgfile)
    wxgfile.close()
    -
    +
    for node in wxgtree.childNodes[1].childNodes:
    if node.nodeType == wxgtree.ELEMENT_NODE:
    hmi_frames.append({
    "name" : node.getAttribute("name"),
    "class" : node.getAttribute("class"),
    "handlers" : [
    - hnode.firstChild.data for hnode in
    + hnode.firstChild.data for hnode in
    node.getElementsByTagName("handler")]})
    -
    +
    hmipyfile_path=os.path.join(self._getBuildPath(), "hmi.py")
    if wx.Platform == '__WXMSW__':
    wxgfile_path = "\"%s\""%wxgfile_path
    @@ -107,20 +107,20 @@
    else:
    wxghmipyfile_path = hmipyfile_path
    self.launch_wxglade(['-o', wxghmipyfile_path, '-g', 'python', wxgfile_path], wait=True)
    -
    +
    hmipyfile = open(hmipyfile_path, 'r')
    define_hmi = hmipyfile.read().decode('utf-8')
    hmipyfile.close()
    -
    +
    else:
    define_hmi = ""
    -
    - declare_hmi = "\n".join(["%(name)s = None\n" % x +
    +
    + declare_hmi = "\n".join(["%(name)s = None\n" % x +
    "\n".join(["%(class)s.%(h)s = %(h)s"%
    dict(x,h=h) for h in x['handlers']])
    for x in hmi_frames])
    global_hmi = ("global %s\n"%",".join(
    - [x["name"] for x in hmi_frames])
    + [x["name"] for x in hmi_frames])
    if len(hmi_frames) > 0 else "")
    init_hmi = "\n".join(["""\
    def OnCloseFrame(evt):
    @@ -131,9 +131,9 @@
    %(name)s.Show()
    """ % x for x in hmi_frames])
    cleanup_hmi = "\n".join(
    - ["if %(name)s is not None: %(name)s.Destroy()" % x
    + ["if %(name)s is not None: %(name)s.Destroy()" % x
    for x in hmi_frames])
    -
    +
    self.PreSectionsTexts = {
    "globals":define_hmi,
    "start":global_hmi,
    @@ -174,4 +174,3 @@
    if wx.Platform == '__WXMSW__':
    wxg_filename = "\"%s\""%wxg_filename
    self.launch_wxglade([wxg_filename])
    -
    --- a/xmlclass/xmlclass.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/xmlclass/xmlclass.py Mon Aug 14 19:13:01 2017 +0300
    @@ -93,7 +93,7 @@
    def dst(self, dt):
    return ZERO
    -[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE,
    +[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE,
    ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
    ] = range(13)
    @@ -140,7 +140,7 @@
    def GetNormalizedString(attr, extract=True):
    """
    - Function that normalizes a string according to XML 1.0. Replace
    + Function that normalizes a string according to XML 1.0. Replace
    tabulations, line feed and carriage return by white space
    @param attr: tree node containing data to extract or data to normalize
    @param extract: attr is a tree node or not
    @@ -155,14 +155,14 @@
    def GetToken(attr, extract=True):
    """
    - Function that tokenizes a string according to XML 1.0. Remove any leading
    - and trailing white space and replace internal sequence of two or more
    + Function that tokenizes a string according to XML 1.0. Remove any leading
    + and trailing white space and replace internal sequence of two or more
    spaces by only one white space
    @param attr: tree node containing data to extract or data to tokenize
    @param extract: attr is a tree node or not
    @return: data tokenized as string
    """
    - return " ".join([part for part in
    + return " ".join([part for part in
    GetNormalizedString(attr, extract).split(" ")
    if part])
    @@ -186,7 +186,7 @@
    raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
    -def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None,
    +def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None,
    minExclusive=None, maxExclusive=None):
    """
    Function that generates an extraction function for integer defining min and
    @@ -517,7 +517,7 @@
    check that all extracted items match the model
    @param attr: tree node containing data to extract or data as a string
    @param extract: attr is a tree node or not
    - @return: data as a list of string if matching
    + @return: data as a list of string if matching
    """
    if extract:
    value = GetAttributeValue(attr)
    @@ -535,18 +535,18 @@
    return GetModelNameList
    def GenerateAnyInfos(infos):
    -
    +
    def GetTextElement(tree):
    if infos["namespace"][0] == "##any":
    return tree.xpath("p")[0]
    return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
    -
    +
    def ExtractAny(tree):
    return GetTextElement(tree).text
    -
    +
    def GenerateAny(tree, value):
    GetTextElement(tree).text = etree.CDATA(value)
    -
    +
    def InitialAny():
    if infos["namespace"][0] == "##any":
    element_name = "p"
    @@ -555,9 +555,9 @@
    p = etree.Element(element_name)
    p.text = etree.CDATA("")
    return p
    -
    +
    return {
    - "type": COMPLEXTYPE,
    + "type": COMPLEXTYPE,
    "extract": ExtractAny,
    "generate": GenerateAny,
    "initial": InitialAny,
    @@ -574,16 +574,16 @@
    return True
    else:
    return None
    -
    +
    def GenerateTag(value, name=None, indent=0):
    if name is not None and not (infos["minOccurs"] == 0 and value is None):
    ind1, ind2 = getIndent(indent, name)
    return ind1 + "<%s/>\n" % name
    else:
    return ""
    -
    +
    return {
    - "type": TAG,
    + "type": TAG,
    "extract": ExtractTag,
    "generate": GenerateTag,
    "initial": lambda: None,
    @@ -595,7 +595,7 @@
    namespace, name = DecomposeQualifiedName(infos)
    return factory.GetQualifiedNameInfos(name, namespace)
    return infos
    -
    +
    def GetElementInitialValue(factory, infos):
    infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
    if infos["minOccurs"] == 1:
    @@ -667,7 +667,7 @@
    prefix = ("%s:" % factory.TargetNamespace
    if factory.TargetNamespace is not None else "")
    choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys()))
    -
    +
    def GetContentInitial():
    content_name, infos = choices[0]
    if content_name == "sequence":
    @@ -678,7 +678,7 @@
    else:
    content_value = GetElementInitialValue(factory, infos)
    return content_value
    -
    +
    return {
    "type": COMPLEXTYPE,
    "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP),
    @@ -693,13 +693,13 @@
    def DecomposeQualifiedName(name):
    result = QName_model.match(name)
    if not result:
    - raise ValueError("\"%s\" isn't a valid QName value!" % name)
    + raise ValueError("\"%s\" isn't a valid QName value!" % name)
    parts = result.groups()[0].split(':')
    if len(parts) == 1:
    return None, parts[0]
    return parts
    -
    -def GenerateElement(element_name, attributes, elements_model,
    +
    +def GenerateElement(element_name, attributes, elements_model,
    accept_text=False):
    def ExtractElement(factory, node):
    attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
    @@ -746,20 +746,20 @@
    else:
    self.BaseFolder = self.FileName = None
    self.Debug = debug
    -
    +
    # Dictionary for stocking Classes and Types definitions created from
    # the XML tree
    self.XMLClassDefinitions = {}
    -
    +
    self.DefinedNamespaces = {}
    self.NSMAP = {}
    self.Namespaces = {}
    self.SchemaNamespace = None
    self.TargetNamespace = None
    self.etreeNamespaceFormat = "%s"
    -
    +
    self.CurrentCompilations = []
    -
    +
    # Dictionaries for stocking Classes and Types generated
    self.ComputeAfter = []
    if self.FileName is not None:
    @@ -920,15 +920,15 @@
    def ParseSchema(self):
    pass
    -
    +
    def AddEquivalentClass(self, name, base):
    if name != base:
    equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
    equivalences[self.etreeNamespaceFormat % name] = True
    -
    +
    def AddDistinctionBetweenParentsInLookupClass(
    self, lookup_classes, parent, typeinfos):
    - parent = (self.etreeNamespaceFormat % parent
    + parent = (self.etreeNamespaceFormat % parent
    if parent is not None else None)
    parent_class = lookup_classes.get(parent)
    if parent_class is not None:
    @@ -939,7 +939,7 @@
    lookup_classes[parent] = [typeinfos, parent_class]
    else:
    lookup_classes[parent] = typeinfos
    -
    +
    def AddToLookupClass(self, name, parent, typeinfos):
    lookup_name = self.etreeNamespaceFormat % name
    if isinstance(typeinfos, (StringType, UnicodeType)):
    @@ -958,7 +958,7 @@
    self.AddDistinctionBetweenParentsInLookupClass(
    lookup_classes, parent, typeinfos)
    self.ComputedClassesLookUp[lookup_name] = lookup_classes
    -
    +
    def ExtractTypeInfos(self, name, parent, typeinfos):
    if isinstance(typeinfos, (StringType, UnicodeType)):
    namespace, type_name = DecomposeQualifiedName(typeinfos)
    @@ -986,12 +986,12 @@
    return self.CreateClass(name, parent, typeinfos)
    elif typeinfos["type"] == SIMPLETYPE:
    return typeinfos
    -
    +
    def GetEquivalentParents(self, parent):
    return reduce(lambda x, y: x + y,
    [[p] + self.GetEquivalentParents(p)
    for p in self.EquivalentClassesParent.get(parent, {}).keys()], [])
    -
    +
    """
    Methods that generates the classes
    """
    @@ -1028,7 +1028,7 @@
    if result is not None and \
    not isinstance(result, (UnicodeType, StringType)):
    self.Namespaces[self.TargetNamespace][result["name"]] = result
    -
    +
    for name, parents in self.ComputedClassesLookUp.iteritems():
    if isinstance(parents, DictType):
    computed_classes = parents.items()
    @@ -1042,7 +1042,7 @@
    parents = dict(computed_classes)
    self.ComputedClassesLookUp[name] = parents
    parents[equivalent_parent] = computed_class
    -
    +
    return self.ComputedClasses
    def CreateClass(self, name, parent, classinfos, baseclass = False):
    @@ -1050,11 +1050,11 @@
    classname = "%s_%s" % (parent, name)
    else:
    classname = name
    -
    +
    # Checks that classe haven't been generated yet
    if self.AlreadyComputed.get(classname, False):
    return self.ComputedClassesInfos.get(classname, None)
    -
    +
    # If base classes haven't been generated
    bases = []
    base_infos = classinfos.get("base", None)
    @@ -1088,12 +1088,12 @@
    bases.append(DefaultElementClass)
    bases = tuple(bases)
    classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
    -
    +
    self.AlreadyComputed[classname] = True
    -
    +
    for attribute in classinfos["attributes"]:
    infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
    - if infos is not None:
    + if infos is not None:
    if infos["type"] != SIMPLETYPE:
    raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
    attrname = attribute["name"]
    @@ -1105,7 +1105,7 @@
    else:
    raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
    attribute["attr_type"] = infos
    -
    +
    for element in classinfos["elements"]:
    if element["type"] == CHOICE:
    elmtname = element["name"]
    @@ -1141,13 +1141,13 @@
    classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
    classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
    classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
    -
    +
    classmembers["_init_"] = generateInitMethod(self, classinfos)
    classmembers["StructurePattern"] = GetStructurePattern(classinfos)
    classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
    classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
    classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
    -
    +
    class_definition = classobj(str(name), bases, classmembers)
    setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
    setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
    @@ -1155,16 +1155,16 @@
    "name": classname,
    "initial": generateClassCreateFunction(class_definition),
    }
    -
    +
    if self.FileName is not None:
    self.ComputedClasses[self.FileName][classname] = class_definition
    else:
    self.ComputedClasses[classname] = class_definition
    self.ComputedClassesInfos[classname] = class_infos
    -
    +
    self.AddToLookupClass(name, parent, class_definition)
    self.AddToLookupClass(classname, None, class_definition)
    -
    +
    return class_infos
    """
    @@ -1183,7 +1183,7 @@
    else:
    for classname, xmlclass in items:
    print "%s: %s" % (classname, str(xmlclass))
    -
    +
    def PrintClassNames(self):
    classnames = self.XMLClassDefinitions.keys()
    classnames.sort()
    @@ -1191,7 +1191,7 @@
    print classname
    """
    -Method that generate the method for generating the xml tree structure model by
    +Method that generate the method for generating the xml tree structure model by
    following the attributes list defined
    """
    def ComputeMultiplicity(name, infos):
    @@ -1213,7 +1213,7 @@
    if infos["maxOccurs"] == "unbounded":
    return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
    else:
    - return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
    + return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
    infos["maxOccurs"])
    def GetStructurePattern(classinfos):
    @@ -1256,7 +1256,7 @@
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    -
    +
    def getattrMethod(self, name):
    if attributes.has_key(name):
    attribute_infos = attributes[name]
    @@ -1269,7 +1269,7 @@
    elif attribute_infos.has_key("default"):
    return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
    return None
    -
    +
    elif elements.has_key(name):
    element_infos = elements[name]
    element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
    @@ -1279,7 +1279,7 @@
    return content
    elif len(content) > 0:
    return content[0]
    - return None
    + return None
    elif element_infos["type"] == ANY:
    return element_infos["elmt_type"]["extract"](self)
    elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
    @@ -1290,7 +1290,7 @@
    values = self.findall(element_name)
    if element_infos["elmt_type"]["type"] == SIMPLETYPE:
    return map(lambda value:
    - element_infos["elmt_type"]["extract"](value.text, extract=False),
    + element_infos["elmt_type"]["extract"](value.text, extract=False),
    values)
    return values
    else:
    @@ -1298,19 +1298,19 @@
    if element_infos["elmt_type"]["type"] == SIMPLETYPE:
    return element_infos["elmt_type"]["extract"](value.text, extract=False)
    return value
    -
    +
    elif classinfos.has_key("base"):
    return classinfos["base"].__getattr__(self, name)
    -
    +
    return DefaultElementClass.__getattribute__(self, name)
    -
    +
    return getattrMethod
    def generateSetattrMethod(factory, class_definition, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
    elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]])
    -
    +
    def setattrMethod(self, name, value):
    if attributes.has_key(name):
    attribute_infos = attributes[name]
    @@ -1323,26 +1323,26 @@
    elif attribute_infos.has_key("fixed"):
    return
    return self.set(name, attribute_infos["attr_type"]["generate"](value))
    -
    +
    elif elements.has_key(name):
    element_infos = elements[name]
    element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
    if element_infos["type"] == ANY:
    element_infos["elmt_type"]["generate"](self, value)
    -
    +
    elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
    self.text = element_infos["elmt_type"]["generate"](value)
    -
    +
    else:
    prefix = ("%s:" % factory.TargetNamespace
    if factory.TargetNamespace is not None else "")
    element_xpath = (prefix + name
    if name != "content"
    else elements["content"]["elmt_type"]["choices_xpath"].path)
    -
    +
    for element in self.xpath(element_xpath, namespaces=factory.NSMAP):
    self.remove(element)
    -
    +
    if value is not None:
    element_idx = elements.keys().index(name)
    if element_idx > 0:
    @@ -1351,27 +1351,27 @@
    if x != "content"
    else elements["content"]["elmt_type"]["choices_xpath"].path,
    elements.keys()[:element_idx]))
    -
    +
    insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
    else:
    insertion_point = 0
    -
    +
    if not isinstance(value, ListType):
    value = [value]
    -
    +
    for element in reversed(value):
    if element_infos["elmt_type"]["type"] == SIMPLETYPE:
    tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
    tmp_element.text = element_infos["elmt_type"]["generate"](element)
    element = tmp_element
    self.insert(insertion_point, element)
    -
    +
    elif classinfos.has_key("base"):
    return classinfos["base"].__setattr__(self, name, value)
    -
    +
    else:
    raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
    -
    +
    return setattrMethod
    def gettypeinfos(name, facets):
    @@ -1398,7 +1398,7 @@
    attr_list.extend(classinfos["base"].getElementAttributes(self))
    for attr in classinfos["attributes"]:
    if attr["use"] != "prohibited":
    - attr_params = {"name" : attr["name"], "use" : attr["use"],
    + attr_params = {"name" : attr["name"], "use" : attr["use"],
    "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
    "value" : getattr(self, attr["name"], "")}
    attr_list.append(attr_params)
    @@ -1408,7 +1408,7 @@
    def generateGetElementInfos(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    -
    +
    def getElementInfos(self, name, path=None, derived=False):
    attr_type = "element"
    value = None
    @@ -1419,14 +1419,14 @@
    if attributes.has_key(parts[0]):
    if len(parts) != 1:
    raise ValueError("Wrong path!")
    - attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
    + attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
    attributes[parts[0]]["attr_type"]["facets"])
    value = getattr(self, parts[0], "")
    elif elements.has_key(parts[0]):
    if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
    if len(parts) != 1:
    raise ValueError("Wrong path!")
    - attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
    + attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
    elements[parts[0]]["elmt_type"]["facets"])
    value = getattr(self, parts[0], "")
    elif parts[0] == "content":
    @@ -1463,8 +1463,8 @@
    if self.content is not None:
    children.extend(self.content.getElementInfos(value)["children"])
    elif element["elmt_type"]["type"] == SIMPLETYPE:
    - children.append({"name": element_name, "require": element["minOccurs"] != 0,
    - "type": gettypeinfos(element["elmt_type"]["basename"],
    + children.append({"name": element_name, "require": element["minOccurs"] != 0,
    + "type": gettypeinfos(element["elmt_type"]["basename"],
    element["elmt_type"]["facets"]),
    "value": getattr(self, element_name, None)})
    else:
    @@ -1478,7 +1478,7 @@
    def generateSetElementValue(factory, classinfos):
    attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
    elements = dict([(element["name"], element) for element in classinfos["elements"]])
    -
    +
    def setElementValue(self, path, value):
    if path is not None:
    parts = path.split(".", 1)
    @@ -1489,7 +1489,7 @@
    setattr(self, parts[0], value)
    elif attributes[parts[0]]["use"] == "optional" and value == "":
    if attributes[parts[0]].has_key("default"):
    - setattr(self, parts[0],
    + setattr(self, parts[0],
    attributes[parts[0]]["attr_type"]["extract"](
    attributes[parts[0]]["default"], False))
    else:
    @@ -1680,39 +1680,39 @@
    NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ")
    class DefaultElementClass(etree.ElementBase):
    -
    +
    StructurePattern = re.compile("$")
    -
    +
    def _init_(self):
    pass
    -
    +
    def getLocalTag(self):
    return etree.QName(self.tag).localname
    -
    +
    def tostring(self):
    return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8')
    class XMLElementClassLookUp(etree.PythonElementClassLookup):
    -
    +
    def __init__(self, classes, *args, **kwargs):
    etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
    self.LookUpClasses = classes
    -
    +
    def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
    element_class = self.LookUpClasses.get(element_tag, (default, None))
    if not isinstance(element_class, DictType):
    if isinstance(element_class[0], (StringType, UnicodeType)):
    return self.GetElementClass(element_class[0], default=default)
    return element_class[0]
    -
    +
    element_with_parent_class = element_class.get(parent_tag, default)
    if isinstance(element_with_parent_class, (StringType, UnicodeType)):
    return self.GetElementClass(element_with_parent_class, default=default)
    return element_with_parent_class
    -
    +
    def lookup(self, document, element):
    parent = element.getparent()
    - element_class = self.GetElementClass(element.tag,
    + element_class = self.GetElementClass(element.tag,
    parent.tag if parent is not None else None)
    if isinstance(element_class, ListType):
    children = "".join([
    @@ -1741,24 +1741,24 @@
    self.RootNSMAP = namespaces
    self.BaseClass = base_class
    self.XSDSchema = xsd_schema
    -
    +
    def set_element_class_lookup(self, class_lookup):
    etree.XMLParser.set_element_class_lookup(self, class_lookup)
    self.ClassLookup = class_lookup
    -
    +
    def LoadXMLString(self, xml_string):
    tree = etree.fromstring(xml_string, self)
    if not self.XSDSchema.validate(tree):
    error = self.XSDSchema.error_log.last_error
    return tree, (error.line, error.message)
    - return tree, None
    -
    + return tree, None
    +
    def Dumps(self, xml_obj):
    return etree.tostring(xml_obj, encoding='utf-8')
    -
    +
    def Loads(self, xml_string):
    return etree.fromstring(xml_string, self)
    -
    +
    def CreateRoot(self):
    if self.BaseClass is not None:
    root = self.makeelement(
    @@ -1767,14 +1767,14 @@
    root._init_()
    return root
    return None
    -
    +
    def GetElementClass(self, element_tag, parent_tag=None):
    return self.ClassLookup.GetElementClass(
    - self.DefaultNamespaceFormat % element_tag,
    - self.DefaultNamespaceFormat % parent_tag
    - if parent_tag is not None else parent_tag,
    + self.DefaultNamespaceFormat % element_tag,
    + self.DefaultNamespaceFormat % parent_tag
    + if parent_tag is not None else parent_tag,
    None)
    -
    +
    def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
    element_class = self.GetElementClass(element_tag, parent_tag)
    if isinstance(element_class, ListType):
    @@ -1787,14 +1787,14 @@
    DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
    new_element._init_()
    return new_element
    -
    +
    def GenerateParser(factory, xsdstring):
    ComputedClasses = factory.CreateClasses()
    -
    +
    if factory.FileName is not None:
    ComputedClasses = ComputedClasses[factory.FileName]
    BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
    -
    +
    parser = XMLClassParser(
    factory.NSMAP,
    factory.etreeNamespaceFormat,
    @@ -1803,6 +1803,5 @@
    strip_cdata = False, remove_blank_text=True)
    class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
    parser.set_element_class_lookup(class_lookup)
    -
    +
    return parser
    -
    --- a/xmlclass/xsdschema.py Fri Aug 11 15:18:19 2017 +0300
    +++ b/xmlclass/xsdschema.py Mon Aug 14 19:13:01 2017 +0300
    @@ -62,14 +62,14 @@
    text += "</%s>\n" % name
    return text
    return generateXMLTextMethod
    -
    +
    DEFAULT_FACETS = GenerateDictFacets(["pattern", "whiteSpace", "enumeration"])
    NUMBER_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"])
    DECIMAL_FACETS = GenerateDictFacets(NUMBER_FACETS.keys() + ["totalDigits", "fractionDigits"])
    STRING_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["length", "minLength", "maxLength"])
    -ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive",
    - "maxExclusive", "minInclusive", "minExclusive", "totalDigits",
    +ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive",
    + "maxExclusive", "minInclusive", "minExclusive", "totalDigits",
    "fractionDigits", "length", "minLength", "maxLength"]
    @@ -81,12 +81,12 @@
    # Documentation elements
    def ReduceAppInfo(factory, attributes, elements):
    - return {"type": "appinfo", "source": attributes.get("source", None),
    + return {"type": "appinfo", "source": attributes.get("source", None),
    "content": "\n".join(elements)}
    def ReduceDocumentation(factory, attributes, elements):
    - return {"type": "documentation", "source": attributes.get("source", None),
    + return {"type": "documentation", "source": attributes.get("source", None),
    "language": attributes.get("lang", "any"), "content": "\n".join(elements)}
    @@ -124,7 +124,7 @@
    def ReduceList(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    list = {"type": "list", "itemType": attributes.get("itemType", None), "doc": annotations}
    -
    +
    if len(children) > 0 and children[0]["type"] == SIMPLETYPE:
    if list["itemType"] is None:
    list["itemType"] = children[0]
    @@ -138,7 +138,7 @@
    def ReduceUnion(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    union = {"type": "union", "memberTypes": attributes.get("memberTypes", []), "doc": annotations}
    -
    +
    for child in children:
    if child["type"] == SIMPLETYPE:
    union["memberTypes"].appendchild
    @@ -153,29 +153,29 @@
    simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", [])}
    if attributes.has_key("name"):
    simpleType["name"] = attributes["name"]
    -
    +
    if typeinfos["type"] in ["restriction", "extension"]:
    # Search for base type definition
    if isinstance(typeinfos["base"], (StringType, UnicodeType)):
    basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE)
    if basetypeinfos is None:
    - raise "\"%s\" isn't defined!" % typeinfos["base"]
    + raise "\"%s\" isn't defined!" % typeinfos["base"]
    else:
    basetypeinfos = typeinfos["base"]
    -
    +
    # Check that base type is a simple type
    if basetypeinfos["type"] != SIMPLETYPE:
    raise ValueError("Base type given isn't a simpleType!")
    -
    +
    simpleType["basename"] = basetypeinfos["basename"]
    -
    +
    # Check that derivation is allowed
    if basetypeinfos.has_key("final"):
    if "#all" in basetypeinfos["final"]:
    raise ValueError("Base type can't be derivated!")
    if "restriction" in basetypeinfos["final"] and typeinfos["type"] == "restriction":
    raise ValueError("Base type can't be derivated by restriction!")
    -
    +
    # Extract simple type facets
    for facet in typeinfos.get("facets", []):
    facettype = facet["type"]
    @@ -286,12 +286,12 @@
    elif basevalue is not None and value > basevalue:
    raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!")
    facets[facettype] = (value, facet.get("fixed", False))
    -
    - # Report not redefined facet from base type to new created type
    +
    + # Report not redefined facet from base type to new created type
    for facettype, facetvalue in basetypeinfos["facets"].items():
    if not facets.has_key(facettype):
    facets[facettype] = facetvalue
    -
    +
    # Generate extract value for new created type
    def ExtractSimpleTypeValue(attr, extract=True):
    value = basetypeinfos["extract"](attr, extract)
    @@ -317,7 +317,7 @@
    model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
    result = model.match(value)
    if result is None:
    - if len(facetvalue) > 1:
    + if len(facetvalue) > 1:
    raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
    else:
    raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
    @@ -327,7 +327,7 @@
    elif facetvalue == "collapse":
    value = GetToken(value, False)
    return value
    -
    +
    def CheckSimpleTypeValue(value):
    for facetname, (facetvalue, facetfixed) in facets.items():
    if facetvalue is not None:
    @@ -351,12 +351,12 @@
    model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
    result = model.match(value)
    if result is None:
    - if len(facetvalue) > 1:
    + if len(facetvalue) > 1:
    raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
    else:
    raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
    return True
    -
    +
    def SimpleTypeInitialValue():
    for facetname, (facetvalue, facetfixed) in facets.items():
    if facetvalue is not None:
    @@ -375,9 +375,9 @@
    elif facetname == "maxExclusive" and facetvalue <= 0:
    return facetvalue - 1
    return basetypeinfos["initial"]()
    -
    +
    GenerateSimpleType = basetypeinfos["generate"]
    -
    +
    elif typeinfos["type"] == "list":
    # Search for item type definition
    if isinstance(typeinfos["itemType"], (StringType, UnicodeType)):
    @@ -386,41 +386,41 @@
    raise "\"%s\" isn't defined!" % typeinfos["itemType"]
    else:
    itemtypeinfos = typeinfos["itemType"]
    -
    +
    # Check that item type is a simple type
    if itemtypeinfos["type"] != SIMPLETYPE:
    raise ValueError, "Item type given isn't a simpleType!"
    -
    +
    simpleType["basename"] = "list"
    -
    +
    # Check that derivation is allowed
    if itemtypeinfos.has_key("final"):
    if itemtypeinfos["final"].has_key("#all"):
    raise ValueError("Item type can't be derivated!")
    if itemtypeinfos["final"].has_key("list"):
    raise ValueError("Item type can't be derivated by list!")
    -
    +
    # Generate extract value for new created type
    def ExtractSimpleTypeValue(attr, extract = True):
    values = []
    for value in GetToken(attr, extract).split(" "):
    values.append(itemtypeinfos["extract"](value, False))
    return values
    -
    +
    def CheckSimpleTypeValue(value):
    for item in value:
    result = itemtypeinfos["check"](item)
    if not result:
    return result
    return True
    -
    +
    SimpleTypeInitialValue = lambda: []
    -
    +
    GenerateSimpleType = GenerateSimpleTypeXMLText(lambda x: " ".join(map(itemtypeinfos["generate"], x)))
    -
    +
    facets = GenerateDictFacets(["length", "maxLength", "minLength", "enumeration", "pattern"])
    facets["whiteSpace"] = ("collapse", False)
    -
    +
    elif typeinfos["type"] == "union":
    # Search for member types definition
    membertypesinfos = []
    @@ -431,22 +431,22 @@
    raise ValueError("\"%s\" isn't defined!" % membertype)
    else:
    infos = membertype
    -
    +
    # Check that member type is a simple type
    if infos["type"] != SIMPLETYPE:
    raise ValueError("Member type given isn't a simpleType!")
    -
    +
    # Check that derivation is allowed
    if infos.has_key("final"):
    if infos["final"].has_key("#all"):
    raise ValueError("Item type can't be derivated!")
    if infos["final"].has_key("union"):
    raise ValueError("Member type can't be derivated by union!")
    -
    +
    membertypesinfos.append(infos)
    -
    +
    simpleType["basename"] = "union"
    -
    +
    # Generate extract value for new created type
    def ExtractSimpleTypeValue(attr, extract = True):
    if extract:
    @@ -459,25 +459,25 @@
    except:
    pass
    raise ValueError("\"%s\" isn't valid for type defined for union!")
    -
    +
    def CheckSimpleTypeValue(value):
    for infos in membertypesinfos:
    result = infos["check"](value)
    if result:
    return result
    return False
    -
    +
    SimpleTypeInitialValue = membertypesinfos[0]["initial"]
    -
    +
    def GenerateSimpleTypeFunction(value):
    if isinstance(value, BooleanType):
    return {True: "true", False: "false"}[value]
    else:
    return str(value)
    GenerateSimpleType = GenerateSimpleTypeXMLText(GenerateSimpleTypeFunction)
    -
    +
    facets = GenerateDictFacets(["pattern", "enumeration"])
    -
    +
    simpleType["facets"] = facets
    simpleType["extract"] = ExtractSimpleTypeValue
    simpleType["initial"] = SimpleTypeInitialValue
    @@ -488,10 +488,10 @@
    def ReduceSimpleType(factory, attributes, elements):
    # Reduce all the simple type children
    annotations, children = factory.ReduceElements(elements)
    -
    +
    simpleType = CreateSimpleType(factory, attributes, children[0])
    simpleType["doc"] = annotations
    -
    +
    return simpleType
    # Complex type
    @@ -503,7 +503,7 @@
    basetypeinfos = factory.FindSchemaElement(base)
    if not isinstance(basetypeinfos, (UnicodeType, StringType)) and basetypeinfos["type"] == COMPLEXTYPE:
    attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"]))
    -
    +
    for element in elements:
    if element["type"] == ATTRIBUTE:
    if attrnames.get(element["name"], False):
    @@ -534,7 +534,7 @@
    raise ValueError("Only one base type can be defined for restriction!")
    if restriction["base"] is None:
    raise ValueError("No base type has been defined for restriction!")
    -
    +
    while len(children) > 0 and children[0]["type"] in ALL_FACETS:
    restriction["facets"].append(children.pop(0))
    restriction["attributes"] = ExtractAttributes(factory, children, restriction["base"])
    @@ -571,9 +571,9 @@
    def ReduceSimpleContent(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    simpleContent = children[0].copy()
    -
    +
    basetypeinfos = factory.FindSchemaElement(simpleContent["base"])
    if basetypeinfos["type"] == SIMPLETYPE:
    contenttypeinfos = simpleContent.copy()
    @@ -588,7 +588,7 @@
    else:
    raise ValueError("No compatible base type defined for simpleContent!")
    contenttypeinfos = CreateSimpleType(factory, attributes, contenttypeinfos)
    -
    +
    simpleContent["elements"] = [{"name": "content", "type": ELEMENT,
    "elmt_type": contenttypeinfos, "doc": annotations,
    "minOccurs": 1, "maxOccurs": 1}]
    @@ -605,7 +605,7 @@
    def ReduceComplexType(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    if len(children) > 0:
    if children[0]["type"] in ["simpleContent", "complexContent"]:
    complexType = children[0].copy()
    @@ -666,20 +666,20 @@
    def ReduceAttribute(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    if attributes.has_key("default"):
    if attributes.has_key("fixed"):
    raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!")
    elif attributes.get("use", "optional") != "optional":
    raise ValueError("if \"default\" present, \"use\" can only have the value \"optional\"!")
    -
    +
    attribute = {"type": ATTRIBUTE, "attr_type": attributes.get("type", None), "doc": annotations}
    if len(children) > 0:
    if attribute["attr_type"] is None:
    attribute["attr_type"] = children[0]
    else:
    raise ValueError("Only one type can be defined for attribute!")
    -
    +
    if attributes.has_key("ref"):
    if attributes.has_key("name"):
    raise ValueError("\"ref\" and \"name\" can't be defined at the same time!")
    @@ -689,7 +689,7 @@
    raise ValueError("if \"ref\" is present, no type can be defined!")
    elif attribute["attr_type"] is None:
    raise ValueError("No type has been defined for attribute \"%s\"!" % attributes["name"])
    -
    +
    if attributes.has_key("type"):
    tmp_attrs = attributes.copy()
    tmp_attrs.pop("type")
    @@ -711,14 +711,14 @@
    def ReduceAny(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    any = {"type": ANY, "doc": annotations}
    any.update(attributes)
    return any
    def ReduceElement(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    types = []
    constraints = []
    for child in children:
    @@ -726,10 +726,10 @@
    constraints.append(child)
    else:
    types.append(child)
    -
    +
    if attributes.has_key("default") and attributes.has_key("fixed"):
    raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!")
    -
    +
    if attributes.has_key("ref"):
    for attr in ["name", "default", "fixed", "form", "block", "type"]:
    if attributes.has_key(attr):
    @@ -738,7 +738,7 @@
    raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!")
    if len(types) > 0:
    raise ValueError("No type and no constraints can be defined where \"ref\" is defined!")
    -
    +
    infos = factory.FindSchemaElement(attributes["ref"], ELEMENT)
    if infos is not None:
    element = infos.copy()
    @@ -748,7 +748,7 @@
    return element
    else:
    raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name)
    -
    +
    elif attributes.has_key("name"):
    element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "constraints": constraints, "doc": annotations}
    if len(types) > 0:
    @@ -759,7 +759,7 @@
    elif element["elmt_type"] is None:
    element["elmt_type"] = "tag"
    element["type"] = TAG
    -
    +
    if attributes.has_key("type"):
    tmp_attrs = attributes.copy()
    tmp_attrs.pop("type")
    @@ -772,18 +772,18 @@
    def ReduceAll(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    for child in children:
    if children["maxOccurs"] == "unbounded" or children["maxOccurs"] > 1:
    raise ValueError("\"all\" item can't have \"maxOccurs\" attribute greater than 1!")
    -
    +
    return {"type": "all", "elements": children, "minOccurs": attributes["minOccurs"],
    "maxOccurs": attributes["maxOccurs"], "order": False, "doc": annotations}
    def ReduceChoice(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    choices = []
    for child in children:
    if child["type"] in [ELEMENT, ANY, TAG]:
    @@ -795,7 +795,7 @@
    elif child["type"] == CHOICE:
    choices.extend(child["choices"])
    elif child["type"] == "group":
    - elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP)
    + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP)
    if not elmtgroup.has_key("choices"):
    raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!")
    choices_tmp = []
    @@ -810,18 +810,18 @@
    else:
    choices_tmp.append(choice)
    choices.extend(choices_tmp)
    -
    +
    for choice in choices:
    attributes["minOccurs"] = min(attributes["minOccurs"], choice["minOccurs"])
    choice["minOccurs"] = 1
    -
    +
    return {"type": CHOICE, "choices": choices, "minOccurs": attributes["minOccurs"],
    "maxOccurs": attributes["maxOccurs"], "doc": annotations}
    def ReduceSequence(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    sequence = []
    for child in children:
    if child["type"] in [ELEMENT, ANY, TAG, CHOICE]:
    @@ -844,14 +844,14 @@
    else:
    elements_tmp.append(element)
    sequence.extend(elements_tmp)
    -
    +
    return {"type": "sequence", "elements": sequence, "minOccurs": attributes["minOccurs"],
    "maxOccurs": attributes["maxOccurs"], "order": True, "doc": annotations}
    -
    -
    +
    +
    def ReduceGroup(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    if attributes.has_key("ref"):
    return {"type": "group", "ref": attributes["ref"], "doc": annotations}
    else:
    @@ -868,39 +868,39 @@
    def ReduceUnique(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    unique = {"type": CONSTRAINT, "const_type": "unique", "selector": children[0], "fields": children[1:]}
    unique.update(attributes)
    return unique
    -
    +
    def ReduceKey(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    key = {"type": CONSTRAINT, "const_type": "key", "selector": children[0], "fields": children[1:]}
    key.update(attributes)
    return key
    def ReduceKeyRef(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    keyref = {"type": CONSTRAINT, "const_type": "keyref", "selector": children[0], "fields": children[1:]}
    keyref.update(attributes)
    return keyref
    -
    +
    def ReduceSelector(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    selector = {"type": CONSTRAINT, "const_type": "selector"}
    selector.update(attributes)
    return selector
    def ReduceField(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    field = {"type": CONSTRAINT, "const_type": "field"}
    field.update(attributes)
    return field
    -
    +
    # Inclusion elements
    @@ -910,7 +910,7 @@
    def ReduceInclude(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    -
    +
    if factory.FileName is None:
    raise ValueError("Include in XSD string not yet supported")
    filepath = attributes["schemaLocation"]
    @@ -922,7 +922,7 @@
    include_factory = XSDClassFactory(minidom.parse(xsdfile), filepath)
    xsdfile.close()
    include_factory.CreateClasses()
    -
    +
    if factory.TargetNamespace == include_factory.TargetNamespace:
    factory.Namespaces[factory.TargetNamespace].update(include_factory.Namespaces[include_factory.TargetNamespace])
    else:
    @@ -931,7 +931,7 @@
    factory.ComputedClassesLookUp.update(include_factory.ComputedClassesLookUp)
    factory.EquivalentClassesParent.update(include_factory.EquivalentClassesParent)
    return None
    -
    +
    def ReduceRedefine(factory, attributes, elements):
    annotations, children = factory.ReduceElements(elements)
    raise ValueError("\"redefine\" element isn't supported yet!")
    @@ -944,15 +944,15 @@
    factory.ElementFormDefault = attributes["elementFormDefault"]
    factory.BlockDefault = attributes["blockDefault"]
    factory.FinalDefault = attributes["finalDefault"]
    -
    +
    targetNamespace = attributes.get("targetNamespace", None)
    factory.TargetNamespace = factory.DefinedNamespaces.get(targetNamespace, None)
    if factory.TargetNamespace is not None:
    factory.etreeNamespaceFormat = "{%s}%%s" % targetNamespace
    factory.Namespaces[factory.TargetNamespace] = {}
    -
    +
    annotations, children = factory.ReduceElements(elements, True)
    -
    +
    for child in children:
    if child.has_key("name"):
    infos = factory.GetQualifiedNameInfos(child["name"], factory.TargetNamespace, True)
    @@ -987,7 +987,7 @@
    else:
    return True
    return schema == reference
    -
    +
    #-------------------------------------------------------------------------------
    # Base class for XSD schema extraction
    #-------------------------------------------------------------------------------
    @@ -999,7 +999,7 @@
    ClassFactory.__init__(self, document, filepath, debug)
    self.Namespaces["xml"] = {
    "lang": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("lang", LANGUAGE_model)
    }
    @@ -1007,31 +1007,31 @@
    }
    self.Namespaces["xsi"] = {
    "noNamespaceSchemaLocation": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": NotSupportedYet("noNamespaceSchemaLocation")
    }
    },
    "nil": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": NotSupportedYet("nil")
    }
    },
    "schemaLocation": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": NotSupportedYet("schemaLocation")
    }
    },
    "type": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": NotSupportedYet("type")
    }
    }
    }
    -
    +
    def ParseSchema(self):
    for child in self.Document.childNodes:
    if child.nodeType == self.Document.ELEMENT_NODE:
    @@ -1067,7 +1067,7 @@
    if element_type is not None and element["type"] != element_type:
    raise ValueError("\"%s\" isn't of the expected type!" % element_name)
    return element
    -
    +
    def CreateSchemaElement(self, element_name, element_type):
    for type, attributes, elements in self.Schema[2]:
    namespace, name = DecomposeQualifiedName(type)
    @@ -1091,7 +1091,7 @@
    return None
    """
    -This function opens the xsd file and generate a xml parser with class lookup from
    +This function opens the xsd file and generate a xml parser with class lookup from
    the xml tree
    """
    def GenerateParserFromXSD(filepath):
    @@ -1129,9 +1129,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, element*)
    </all>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"],
    + "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"],
    re.compile("((?:annotation )?(?:element )*)"))
    },
    "reduce": ReduceAll
    @@ -1143,9 +1143,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (appinfo | documentation)*
    </annotation>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("annotation", ["id"],
    + "default": GenerateElement("annotation", ["id"],
    re.compile("((?:app_info |documentation )*)"))
    },
    "reduce": ReduceAnnotation
    @@ -1161,10 +1161,10 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </any>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("any",
    - ["id", "maxOccurs", "minOccurs", "namespace", "processContents"],
    + "default": GenerateElement("any",
    + ["id", "maxOccurs", "minOccurs", "namespace", "processContents"],
    re.compile("((?:annotation )?(?:simpleType )*)"))
    },
    "reduce": ReduceAny
    @@ -1178,7 +1178,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </anyAttribute>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("anyAttribute",
    ["id", "namespace", "processContents"], ONLY_ANNOTATION)
    @@ -1192,7 +1192,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: ({any})*
    </appinfo>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("appinfo", ["source"], re.compile("(.*)"), True)
    },
    @@ -1212,13 +1212,13 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, simpleType?)
    </attribute>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("attribute",
    - ["default", "fixed", "form", "id", "name", "ref", "type", "use"],
    + "default": GenerateElement("attribute",
    + ["default", "fixed", "form", "id", "name", "ref", "type", "use"],
    re.compile("((?:annotation )?(?:simpleType )?)")),
    - "schema": GenerateElement("attribute",
    - ["default", "fixed", "form", "id", "name", "type"],
    + "schema": GenerateElement("attribute",
    + ["default", "fixed", "form", "id", "name", "type"],
    re.compile("((?:annotation )?(?:simpleType )?)"))
    },
    "reduce": ReduceAttribute
    @@ -1232,12 +1232,12 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?))
    </attributeGroup>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("attributeGroup",
    + "default": GenerateElement("attributeGroup",
    ["id", "ref"], ONLY_ANNOTATION),
    "schema": GenerateElement("attributeGroup",
    - ["id", "name"],
    + ["id", "name"],
    re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))"))
    },
    "reduce": ReduceAttributeGroup
    @@ -1251,9 +1251,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (element | group | choice | sequence | any)*)
    </choice>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"],
    + "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"],
    re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)"))
    },
    "reduce": ReduceChoice
    @@ -1266,9 +1266,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (restriction | extension))
    </complexContent>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("complexContent", ["id", "mixed"],
    + "default": GenerateElement("complexContent", ["id", "mixed"],
    re.compile("((?:annotation )?(?:restriction |extension ))"))
    },
    "reduce": ReduceComplexContent
    @@ -1285,10 +1285,10 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
    </complexType>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("complexType",
    - ["abstract", "block", "final", "id", "mixed", "name"],
    + "default": GenerateElement("complexType",
    + ["abstract", "block", "final", "id", "mixed", "name"],
    re.compile("((?:annotation )?(?:simpleContent |complexContent |(?:(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))))"))
    },
    "reduce": ReduceComplexType
    @@ -1301,9 +1301,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: ({any})*
    </documentation>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("documentation",
    + "default": GenerateElement("documentation",
    ["source", "lang"], re.compile("(.*)"), True)
    },
    "reduce": ReduceDocumentation
    @@ -1328,13 +1328,13 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
    </element>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("element",
    - ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"],
    + "default": GenerateElement("element",
    + ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"],
    re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")),
    - "schema": GenerateElement("element",
    - ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"],
    + "schema": GenerateElement("element",
    + ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"],
    re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)"))
    },
    "reduce": ReduceElement
    @@ -1347,7 +1347,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </enumeration>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("enumeration", ["id", "value"], ONLY_ANNOTATION)
    },
    @@ -1361,11 +1361,11 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))
    </extension>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("extension", ["base", "id"],
    + "default": GenerateElement("extension", ["base", "id"],
    re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")),
    - "complexContent": GenerateElement("extension", ["base", "id"],
    + "complexContent": GenerateElement("extension", ["base", "id"],
    re.compile("((?:annotation )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))"))
    },
    "reduce": ReduceExtension
    @@ -1378,7 +1378,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </field>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("field", ["id", "xpath"], ONLY_ANNOTATION)
    },
    @@ -1393,9 +1393,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </fractionDigits>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("fractionDigits",
    + "default": GenerateElement("fractionDigits",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("fractionDigits", True)
    @@ -1411,13 +1411,13 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (all | choice | sequence)?)
    </group>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("group",
    - ["id", "maxOccurs", "minOccurs", "ref"],
    + ["id", "maxOccurs", "minOccurs", "ref"],
    re.compile("((?:annotation )?(?:all |choice |sequence )?)")),
    "schema": GenerateElement("group",
    - ["id", "name"],
    + ["id", "name"],
    re.compile("((?:annotation )?(?:all |choice |sequence )?)"))
    },
    "reduce": ReduceGroup
    @@ -1431,7 +1431,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </import>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("import",
    ["id", "namespace", "schemaLocation"], ONLY_ANNOTATION)
    @@ -1446,7 +1446,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </include>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("include",
    ["id", "schemaLocation"], ONLY_ANNOTATION)
    @@ -1461,9 +1461,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (selector, field+))
    </key>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("key", ["id", "name"],
    + "default": GenerateElement("key", ["id", "name"],
    re.compile("((?:annotation )?(?:selector (?:field )+))"))
    },
    "reduce": ReduceKey
    @@ -1477,9 +1477,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (selector, field+))
    </keyref>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("keyref", ["id", "name", "refer"],
    + "default": GenerateElement("keyref", ["id", "name", "refer"],
    re.compile("((?:annotation )?(?:selector (?:field )+))"))
    },
    "reduce": ReduceKeyRef
    @@ -1493,9 +1493,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </length>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("length",
    + "default": GenerateElement("length",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("length", True)
    @@ -1508,9 +1508,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, simpleType?)
    </list>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("list", ["id", "itemType"],
    + "default": GenerateElement("list", ["id", "itemType"],
    re.compile("((?:annotation )?(?:simpleType )?)$"))
    },
    "reduce": ReduceList
    @@ -1524,7 +1524,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </maxInclusive>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("maxExclusive",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    @@ -1540,9 +1540,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </maxExclusive>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("maxInclusive",
    + "default": GenerateElement("maxInclusive",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("maxInclusive", True)
    @@ -1556,9 +1556,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </maxLength>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("maxLength",
    + "default": GenerateElement("maxLength",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("maxLength", True)
    @@ -1572,9 +1572,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </minExclusive>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("minExclusive",
    + "default": GenerateElement("minExclusive",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("minExclusive", True)
    @@ -1588,9 +1588,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </minInclusive>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("minInclusive",
    + "default": GenerateElement("minInclusive",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("minInclusive", True)
    @@ -1604,7 +1604,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </minLength>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("minLength",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    @@ -1619,7 +1619,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </pattern>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("pattern", ["id", "value"], ONLY_ANNOTATION)
    },
    @@ -1633,9 +1633,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation | (simpleType | complexType | group | attributeGroup))*
    </redefine>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("refine", ["id", "schemaLocation"],
    + "default": GenerateElement("refine", ["id", "schemaLocation"],
    re.compile("((?:annotation |(?:simpleType |complexType |group |attributeGroup ))*)"))
    },
    "reduce": ReduceRedefine
    @@ -1648,13 +1648,13 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))
    </restriction>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("restriction", ["base", "id"],
    + "default": GenerateElement("restriction", ["base", "id"],
    re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)))")),
    - "simpleContent": GenerateElement("restriction", ["base", "id"],
    + "simpleContent": GenerateElement("restriction", ["base", "id"],
    re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")),
    - "complexContent": GenerateElement("restriction", ["base", "id"],
    + "complexContent": GenerateElement("restriction", ["base", "id"],
    re.compile("((?:annotation )?(?:(?:simpleType )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")),
    },
    "reduce": ReduceRestriction
    @@ -1673,10 +1673,10 @@
    {any attributes with non-schema namespace . . .}>
    Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*)
    </schema>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("schema",
    - ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"],
    + ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"],
    re.compile("((?:include |import |redefine |annotation )*(?:(?:(?:simpleType |complexType |group |attributeGroup )|element |attribute |annotation )(?:annotation )*)*)"))
    }
    },
    @@ -1688,7 +1688,7 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </selector>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    "default": GenerateElement("selector", ["id", "xpath"], ONLY_ANNOTATION)
    },
    @@ -1703,9 +1703,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (element | group | choice | sequence | any)*)
    </sequence>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"],
    + "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"],
    re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)"))
    },
    "reduce": ReduceSequence
    @@ -1717,9 +1717,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (restriction | extension))
    </simpleContent>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("simpleContent", ["id"],
    + "default": GenerateElement("simpleContent", ["id"],
    re.compile("((?:annotation )?(?:restriction |extension ))"))
    },
    "reduce": ReduceSimpleContent
    @@ -1733,9 +1733,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (restriction | list | union))
    </simpleType>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("simpleType", ["final", "id", "name"],
    + "default": GenerateElement("simpleType", ["final", "id", "name"],
    re.compile("((?:annotation )?(?:restriction |list |union ))"))
    },
    "reduce": ReduceSimpleType
    @@ -1749,9 +1749,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </totalDigits>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("totalDigits",
    + "default": GenerateElement("totalDigits",
    ["fixed", "id", "value"], ONLY_ANNOTATION),
    },
    "reduce": GenerateFacetReducing("totalDigits", True)
    @@ -1764,9 +1764,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, simpleType*)
    </union>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("union", ["id", "memberTypes"],
    + "default": GenerateElement("union", ["id", "memberTypes"],
    re.compile("((?:annotation )?(?:simpleType )*)"))
    },
    "reduce": ReduceUnion
    @@ -1779,14 +1779,14 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?, (selector, field+))
    </unique>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("unique", ["id", "name"],
    + "default": GenerateElement("unique", ["id", "name"],
    re.compile("((?:annotation )?(?:selector |(?:field )+))"))
    },
    "reduce": ReduceUnique
    },
    -
    +
    "whiteSpace": {"struct" : """
    <whiteSpace
    fixed = boolean : false
    @@ -1795,9 +1795,9 @@
    {any attributes with non-schema namespace . . .}>
    Content: (annotation?)
    </whiteSpace>""",
    - "type": SYNTAXELEMENT,
    + "type": SYNTAXELEMENT,
    "extract": {
    - "default": GenerateElement("whiteSpace",
    + "default": GenerateElement("whiteSpace",
    ["fixed", "id", "value"], ONLY_ANNOTATION)
    },
    "reduce": GenerateFacetReducing("whiteSpace", True)
    @@ -1808,7 +1808,7 @@
    #-------------------------------------------------------------------------------
    "abstract": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetBoolean
    },
    @@ -1818,7 +1818,7 @@
    },
    "attributeFormDefault": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateEnumeratedExtraction("member attributeFormDefault", ["qualified", "unqualified"])
    },
    @@ -1826,23 +1826,23 @@
    "default": "unqualified"
    }
    },
    -
    +
    "base": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member base", QName_model)
    }
    },
    -
    +
    "block": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateGetList("block", ["restriction", "extension", "substitution"])
    }
    },
    -
    +
    "blockDefault": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateGetList("block", ["restriction", "extension", "substitution"])
    },
    @@ -1850,16 +1850,16 @@
    "default": ""
    }
    },
    -
    +
    "default": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetAttributeValue
    }
    },
    "elementFormDefault": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateEnumeratedExtraction("member elementFormDefault", ["qualified", "unqualified"])
    },
    @@ -1867,9 +1867,9 @@
    "default": "unqualified"
    }
    },
    -
    +
    "final": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateGetList("final", ["restriction", "extension", "substitution"]),
    "simpleType": GenerateGetList("final", ["list", "union", "restriction"])
    @@ -1877,7 +1877,7 @@
    },
    "finalDefault": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateGetList("finalDefault", ["restriction", "extension", "list", "union"])
    },
    @@ -1885,9 +1885,9 @@
    "default": ""
    }
    },
    -
    +
    "fixed": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetBoolean,
    "attribute": GetAttributeValue,
    @@ -1901,35 +1901,35 @@
    },
    "form": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateEnumeratedExtraction("member form", ["qualified", "unqualified"])
    }
    },
    "id": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member id", NCName_model)
    }
    },
    -
    +
    "itemType": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member itemType", QName_model)
    }
    },
    "memberTypes": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameListExtraction("member memberTypes", QNames_model)
    },
    },
    -
    +
    "maxOccurs": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateLimitExtraction(),
    "all": GenerateLimitExtraction(1, 1, False)
    @@ -1940,7 +1940,7 @@
    },
    "minOccurs": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateLimitExtraction(unbounded = False),
    "all": GenerateLimitExtraction(0, 1, False)
    @@ -1951,7 +1951,7 @@
    },
    "mixed": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetBoolean
    },
    @@ -1960,16 +1960,16 @@
    "complexType": False
    }
    },
    -
    +
    "name": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member name", NCName_model)
    }
    },
    -
    +
    "namespace": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member namespace", URI_model),
    "any": GetNamespaces
    @@ -1981,14 +1981,14 @@
    },
    "nillable": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetBoolean
    },
    },
    -
    +
    "processContents": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateEnumeratedExtraction("member processContents", ["lax", "skip", "strict"])
    },
    @@ -1996,9 +1996,9 @@
    "default": "strict"
    }
    },
    -
    +
    "ref": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member ref", QName_model)
    }
    @@ -2010,44 +2010,44 @@
    "default": GenerateModelNameExtraction("member refer", QName_model)
    }
    },
    -
    +
    "schemaLocation": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member schemaLocation", URI_model)
    }
    },
    -
    +
    "source": {
    "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member source", URI_model)
    }
    },
    -
    +
    "substitutionGroup": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member substitutionGroup", QName_model)
    }
    },
    "targetNamespace": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member targetNamespace", URI_model)
    }
    },
    -
    +
    "type": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateModelNameExtraction("member type", QName_model)
    }
    },
    "use": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GenerateEnumeratedExtraction("member usage", ["required", "optional", "prohibited"])
    },
    @@ -2057,7 +2057,7 @@
    },
    "value": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    "default": GetAttributeValue,
    "fractionDigits": GenerateIntegerExtraction(minInclusive=0),
    @@ -2077,13 +2077,13 @@
    },
    "xpath": {
    - "type": SYNTAXATTRIBUTE,
    + "type": SYNTAXATTRIBUTE,
    "extract": {
    # "default": NotSupportedYet("xpath")
    "default": GetAttributeValue
    }
    },
    -
    +
    #-------------------------------------------------------------------------------
    # Simple types definition
    #-------------------------------------------------------------------------------
    @@ -2110,27 +2110,27 @@
    "token": {
    "type": SIMPLETYPE,
    - "basename": "token",
    + "basename": "token",
    "extract": GetToken,
    "facets": STRING_FACETS,
    "generate": GenerateSimpleTypeXMLText(lambda x : x),
    "initial": lambda: "",
    "check": lambda x: isinstance(x, (StringType, UnicodeType))
    },
    -
    +
    "base64Binary": {
    - "type": SIMPLETYPE,
    - "basename": "base64Binary",
    + "type": SIMPLETYPE,
    + "basename": "base64Binary",
    "extract": NotSupportedYet("base64Binary"),
    "facets": STRING_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    "initial": lambda: 0,
    "check": lambda x: isinstance(x, (IntType, LongType))
    },
    -
    +
    "hexBinary": {
    "type": SIMPLETYPE,
    - "basename": "hexBinary",
    + "basename": "hexBinary",
    "extract": GetHexInteger,
    "facets": STRING_FACETS,
    "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x),
    @@ -2140,24 +2140,24 @@
    "integer": {
    "type": SIMPLETYPE,
    - "basename": "integer",
    + "basename": "integer",
    "extract": GenerateIntegerExtraction(),
    "facets": DECIMAL_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    "initial": lambda: 0,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "positiveInteger": {
    "type": SIMPLETYPE,
    - "basename": "positiveInteger",
    + "basename": "positiveInteger",
    "extract": GenerateIntegerExtraction(minExclusive=0),
    "facets": DECIMAL_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    "initial": lambda: 1,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "negativeInteger": {
    "type": SIMPLETYPE,
    "basename": "negativeInteger",
    @@ -2167,27 +2167,27 @@
    "initial": lambda: -1,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "nonNegativeInteger": {
    - "type": SIMPLETYPE,
    - "basename": "nonNegativeInteger",
    + "type": SIMPLETYPE,
    + "basename": "nonNegativeInteger",
    "extract": GenerateIntegerExtraction(minInclusive=0),
    "facets": DECIMAL_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    "initial": lambda: 0,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "nonPositiveInteger": {
    "type": SIMPLETYPE,
    - "basename": "nonPositiveInteger",
    + "basename": "nonPositiveInteger",
    "extract": GenerateIntegerExtraction(maxInclusive=0),
    "facets": DECIMAL_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    "initial": lambda: 0,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "long": {
    "type": SIMPLETYPE,
    "basename": "long",
    @@ -2197,7 +2197,7 @@
    "initial": lambda: 0,
    "check": lambda x: isinstance(x, IntType)
    },
    -
    +
    "unsignedLong": {
    "type": SIMPLETYPE,
    "basename": "unsignedLong",
    @@ -2240,7 +2240,7 @@
    "unsignedShort": {
    "type": SIMPLETYPE,
    - "basename": "unsignedShort",
    + "basename": "unsignedShort",
    "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**16),
    "facets": DECIMAL_FACETS,
    "generate": GenerateSimpleTypeXMLText(str),
    @@ -2306,7 +2306,7 @@
    "generate": GenerateSimpleTypeXMLText(lambda x:{True : "true", False : "false"}[x]),
    "initial": lambda: False,
    "check": lambda x: isinstance(x, BooleanType)
    - },
    + },
    "duration": {
    "type": SIMPLETYPE,
    @@ -2337,7 +2337,7 @@
    "initial": lambda: datetime.date(1,1,1),
    "check": lambda x: isinstance(x, datetime.date)
    },
    -
    +
    "time": {
    "type": SIMPLETYPE,
    "basename": "time",
    @@ -2407,7 +2407,7 @@
    "initial": lambda: "",
    "check": lambda x: isinstance(x, (StringType, UnicodeType))
    },
    -
    +
    "QName": {
    "type": SIMPLETYPE,
    "basename": "QName",
    @@ -2531,4 +2531,3 @@
    # Complex Types
    "anyType": {"type": COMPLEXTYPE, "extract": lambda x:None},
    }
    -