# HG changeset patch # User Laurent Bessard # Date 1368448283 -7200 # Node ID b1705000eba193365b39d45a86c029d8c0caa90c # Parent 55ed55ef7aeabda82dfcb0dc4b40a13200eb761f Fixed support for defining python runtime code using sections like in c_ext diff -r 55ed55ef7aea -r b1705000eba1 CodeFileTreeNode.py --- a/CodeFileTreeNode.py Sun May 12 23:32:30 2013 +0200 +++ b/CodeFileTreeNode.py Mon May 13 14:31:23 2013 +0200 @@ -2,26 +2,82 @@ from xml.dom import minidom import cPickle -from xmlclass import * +from xmlclass import GenerateClassesFromXSDstring, UpdateXMLClassGlobals from PLCControler import UndoBuffer -from editors.CodeFileEditor import SECTIONS_NAMES -CodeFileClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "code_file.xsd")) +CODEFILE_XSD = """ + + + + + %(includes_section)s + + + + + + + + + + + + + + + + + + + + + + + %(sections)s + + + + + + Formatted text according to parts of XHTML 1.1 + + + + + +""" + +SECTION_TAG_ELEMENT = "" class CodeFile: + CODEFILE_NAME = "CodeFile" + SECTIONS_NAMES = [] + def __init__(self): + sections_str = {"codefile_name": self.CODEFILE_NAME} + if "includes" in self.SECTIONS_NAMES: + sections_str["includes_section"] = SECTION_TAG_ELEMENT % "includes" + else: + sections_str["includes_section"] = "" + sections_str["sections"] = "\n".join( + [SECTION_TAG_ELEMENT % name + for name in self.SECTIONS_NAMES if name != "includes"]) + + self.CodeFileClasses = GenerateClassesFromXSDstring( + CODEFILE_XSD % sections_str) + filepath = self.CodeFileName() - self.CodeFile = CodeFileClasses["CodeFile"]() + self.CodeFile = self.CodeFileClasses[self.CODEFILE_NAME]() if os.path.isfile(filepath): xmlfile = open(filepath, 'r') tree = minidom.parse(xmlfile) xmlfile.close() for child in tree.childNodes: - if child.nodeType == tree.ELEMENT_NODE and child.nodeName in ["CodeFile", "CFile"]: + if child.nodeType == tree.ELEMENT_NODE and child.nodeName in [self.CODEFILE_NAME]: self.CodeFile.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"]) self.CreateCodeFileBuffer(True) else: @@ -37,7 +93,7 @@ def SetVariables(self, variables): self.CodeFile.variables.setvariable([]) for var in variables: - variable = CodeFileClasses["variables_variable"]() + variable = self.CodeFileClasses["variables_variable"]() variable.setname(var["Name"]) variable.settype(var["Type"]) variable.setinitial(var["Initial"]) @@ -46,33 +102,21 @@ def GetVariables(self): datas = [] for var in self.CodeFile.variables.getvariable(): - datas.append({"Name" : var.getname(), "Type" : var.gettype(), "Initial" : var.getinitial()}) + datas.append({"Name" : var.getname(), + "Type" : var.gettype(), + "Initial" : var.getinitial()}) return datas def SetTextParts(self, parts): - for section, code_object in zip( - SECTIONS_NAMES, - [self.CodeFile.includes, - self.CodeFile.globals, - self.CodeFile.initFunction, - self.CodeFile.cleanUpFunction, - self.CodeFile.retrieveFunction, - self.CodeFile.publishFunction]): - code_object.settext(parts[section]) - + for section in self.SECTIONS_NAMES: + section_code = parts.get(section) + if section_code is not None: + getattr(self.CodeFile, section).settext(section_code) + def GetTextParts(self): - parts = {} - for section, code_object in zip( - SECTIONS_NAMES, - [self.CodeFile.includes, - self.CodeFile.globals, - self.CodeFile.initFunction, - self.CodeFile.cleanUpFunction, - self.CodeFile.retrieveFunction, - self.CodeFile.publishFunction]): - parts[section] = code_object.gettext() - return parts - + return dict([(section, getattr(self.CodeFile, section).gettext()) + for section in self.SECTIONS_NAMES]) + def CTNTestModified(self): return self.ChangesToSave or not self.CodeFileIsSaved() @@ -80,10 +124,7 @@ filepath = self.CodeFileName() text = "\n" - extras = {"xmlns":"http://www.w3.org/2001/XMLSchema", - "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", - "xsi:schemaLocation" : "code_file.xsd"} - text += self.CodeFile.generateXMLText("CodeFile", 0, extras) + text += self.CodeFile.generateXMLText(self.CODEFILE_NAME, 0) xmlfile = open(filepath,"w") xmlfile.write(text.encode("utf-8")) @@ -103,25 +144,33 @@ # Current Buffering Management Functions #------------------------------------------------------------------------------- + def cPickle_loads(self, str_obj): + UpdateXMLClassGlobals(self.CodeFileClasses) + return cPickle.loads(str_obj) + + def cPickle_dumps(self, obj): + UpdateXMLClassGlobals(self.CodeFileClasses) + return cPickle.dumps(obj) + """ Return a copy of the codefile model """ def Copy(self, model): - return cPickle.loads(cPickle.dumps(model)) + return self.cPickle_loads(self.cPickle_dumps(model)) def CreateCodeFileBuffer(self, saved): self.Buffering = False - self.CodeFileBuffer = UndoBuffer(cPickle.dumps(self.CodeFile), saved) + self.CodeFileBuffer = UndoBuffer(self.cPickle_dumps(self.CodeFile), saved) def BufferCodeFile(self): - self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile)) + self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile)) def StartBuffering(self): self.Buffering = True def EndBuffering(self): if self.Buffering: - self.CodeFileBuffer.Buffering(cPickle.dumps(self.CodeFile)) + self.CodeFileBuffer.Buffering(self.cPickle_dumps(self.CodeFile)) self.Buffering = False def MarkCodeFileAsSaved(self): @@ -133,10 +182,10 @@ def LoadPrevious(self): self.EndBuffering() - self.CodeFile = cPickle.loads(self.CodeFileBuffer.Previous()) + self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Previous()) def LoadNext(self): - self.CodeFile = cPickle.loads(self.CodeFileBuffer.Next()) + self.CodeFile = self.cPickle_loads(self.CodeFileBuffer.Next()) def GetBufferState(self): first = self.CodeFileBuffer.IsFirst() and not self.Buffering diff -r 55ed55ef7aea -r b1705000eba1 c_ext/c_ext.py --- a/c_ext/c_ext.py Sun May 12 23:32:30 2013 +0200 +++ b/c_ext/c_ext.py Mon May 13 14:31:23 2013 +0200 @@ -1,3 +1,4 @@ + import os from CFileEditor import CFileEditor @@ -14,8 +15,19 @@ """ + CODEFILE_NAME = "CFile" + SECTIONS_NAMES = [ + "includes", + "globals", + "initFunction", + "cleanUpFunction", + "retrieveFunction", + "publishFunction"] EditorType = CFileEditor + def GenerateClassesFromXSDstring(self, xsd_string): + return GenerateClassesFromXSDstring(xsd_string) + def GetIconName(self): return "Cfile" diff -r 55ed55ef7aea -r b1705000eba1 code_file.xsd --- a/code_file.xsd Sun May 12 23:32:30 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Formatted text according to parts of XHTML 1.1 - - - - - - diff -r 55ed55ef7aea -r b1705000eba1 editors/CodeFileEditor.py --- a/editors/CodeFileEditor.py Sun May 12 23:32:30 2013 +0200 +++ b/editors/CodeFileEditor.py Mon May 13 14:31:23 2013 +0200 @@ -12,9 +12,6 @@ from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD -SECTIONS_NAMES = ["Includes", "Globals", "Init", - "CleanUp", "Retrieve", "Publish"] - [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, STC_CODE_SECTION] = range(15, 18) @@ -120,7 +117,7 @@ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) self.SectionsComments = {} - for section in SECTIONS_NAMES: + for section in self.Controler.SECTIONS_NAMES: section_comment = " %s section " % (section) len_headers = 78 - len(section_comment) section_comment = self.COMMENT_HEADER * (len_headers / 2) + \ @@ -131,10 +128,11 @@ "comment": section_comment, } - for i, section in enumerate(SECTIONS_NAMES): + for i, section in enumerate(self.Controler.SECTIONS_NAMES): section_infos = self.SectionsComments[section] - if i + 1 < len(SECTIONS_NAMES): - section_end = self.SectionsComments[SECTIONS_NAMES[i + 1]]["comment"] + if i + 1 < len(self.Controler.SECTIONS_NAMES): + section_end = self.SectionsComments[ + self.Controler.SECTIONS_NAMES[i + 1]]["comment"] else: section_end = "$" section_infos["pattern"] = re.compile( @@ -203,7 +201,7 @@ def GetCodeText(self): parts = self.Controler.GetTextParts() text = "" - for section in SECTIONS_NAMES: + for section in self.Controler.SECTIONS_NAMES: section_comments = self.SectionsComments[section] text += section_comments["comment"] if parts[section] == "": @@ -246,7 +244,7 @@ for line in xrange(self.GetLineCount()): self.SetLineState(line, 0) - for section in SECTIONS_NAMES: + for section in self.Controler.SECTIONS_NAMES: section_comments = self.SectionsComments[section] start_pos = text.find(section_comments["comment"]) end_pos = start_pos + len(section_comments["comment"]) @@ -263,7 +261,7 @@ def RefreshModel(self): text = self.GetText() parts = {} - for section in SECTIONS_NAMES: + for section in self.Controler.SECTIONS_NAMES: section_comments = self.SectionsComments[section] result = section_comments["pattern"].search(text) if result is not None: @@ -276,14 +274,26 @@ if self.CallTipActive(): self.CallTipCancel() key = event.GetKeyCode() - current_pos = self.GetSelection()[0] - + current_pos = self.GetCurrentPos() + selected = self.GetSelection() + text_selected = selected[0] != selected[1] + + # Disable to type any character in section header lines if (self.GetLineState(self.LineFromPosition(current_pos)) and + not text_selected and key not in NAVIGATION_KEYS + [ wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]): return + # Disable to delete line between code and header lines + elif (self.GetCurLine()[0].strip() != "" and not text_selected and + (key == wx.WXK_BACK and + self.GetLineState(self.LineFromPosition(max(0, current_pos - 1))) or + key in [wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE] and + self.GetLineState(self.LineFromPosition(min(len(self.GetText()), current_pos + 1))))): + return + elif key == 32 and event.ControlDown(): pos = self.GetCurrentPos() @@ -435,6 +445,7 @@ def Copy(self): self.CmdKeyExecute(wx.stc.STC_CMD_COPY) + self.ParentWindow.RefreshEditMenu() def Paste(self): self.ResetBuffer() diff -r 55ed55ef7aea -r b1705000eba1 py_ext/PythonFileCTNMixin.py --- a/py_ext/PythonFileCTNMixin.py Sun May 12 23:32:30 2013 +0200 +++ b/py_ext/PythonFileCTNMixin.py Mon May 13 14:31:23 2013 +0200 @@ -3,7 +3,7 @@ from PythonEditor import PythonEditor from xml.dom import minidom -from xmlclass import * +from xmlclass import GenerateClassesFromXSD import cPickle from CodeFileTreeNode import CodeFile @@ -12,6 +12,13 @@ class PythonFileCTNMixin(CodeFile): + CODEFILE_NAME = "PyFile" + SECTIONS_NAMES = [ + "globals", + "init", + "cleanup", + "start", + "stop"] EditorType = PythonEditor def __init__(self): @@ -39,53 +46,35 @@ def PythonFileName(self): return os.path.join(self.CTNPath(), "py_ext.xml") - def GetPythonCode(self): - current_location = self.GetCurrentLocation() - # define a unique name for the generated C file - location_str = "_".join(map(str, current_location)) + def GetSectionsCode(self): - text = "## Code generated by Beremiz python mixin confnode\n\n" - - # Adding includes - text += "## User includes\n" - text += self.CodeFile.includes.gettext().strip() - text += "\n" - - # Adding variables - text += "## User variables reference\n" + # Generate Beremiz python runtime variables code config = self.GetCTRoot().GetProjectConfigNames()[0] + variables_str = "" for variable in self.CodeFile.variables.variable: global_name = "%s_%s" % (config.upper(), variable.getname().upper()) - text += "# global_var:%s python_var:%s type:%s initial:%s\n" % ( + variables_str += "# global_var:%s python_var:%s type:%s initial:%s\n" % ( global_name, variable.getname(), variable.gettype(), str(variable.getinitial())) - text += "\n" - # Adding user global variables and routines - text += "## User internal user variables and routines\n" - text += self.CodeFile.globals.gettext().strip() - text += "\n" + sections_code = { + "variables": variables_str, + "globals": self.CodeFile.globals.gettext().strip() + } - # Adding Beremiz confnode functions - text += "## Beremiz confnode functions\n" - for func, args, return_code, code_object in [ - ("__init_", "*args, **kwargs", - "return 0", self.CodeFile.initFunction), - ("__cleanup_", "", "", self.CodeFile.cleanUpFunction), - ("__retrieve_", "", "", self.CodeFile.retrieveFunction), - ("__publish_", "", "", self.CodeFile.publishFunction),]: - text += "def %s%s(%s):\n" % (func, location_str, args) - lines = code_object.gettext().strip().splitlines() - if len(lines) > 0 or return_code != "": - for line in lines: - text += " " + line + "\n" - if return_code != "": - text += " " + return_code + "\n" - text += "\n" - else: - text += " pass\n\n" - - return text + # Generate Beremiz python runtime functions code + for section in self.SECTIONS_NAMES: + if section != "globals": + code_object = getattr(self.CodeFile, section) + section_str = "" + lines = code_object.gettext().strip().splitlines() + if len(lines) > 0: + for line in lines: + section_str += " " + line + "\n" + section_str += "\n" + sections_code[section] = section_str + + return sections_code diff -r 55ed55ef7aea -r b1705000eba1 py_ext/py_ext.py --- a/py_ext/py_ext.py Sun May 12 23:32:30 2013 +0200 +++ b/py_ext/py_ext.py Mon May 13 14:31:23 2013 +0200 @@ -38,9 +38,35 @@ # define a unique name for the generated C file location_str = "_".join(map(lambda x:str(x), current_location)) + sections_code = self.GetSectionsCode() + + text = "## Code generated by Beremiz python mixin confnode\n\n" + + # Adding variables + text += "## User variables reference\n" + text += sections_code["variables"] + text += "\n" + + # Adding user global variables and routines + text += "## User internal user variables and routines\n" + text += sections_code["globals"] + text += "\n" + + # Adding Beremiz python runtime functions + text += "## Beremiz python runtime functions\n" + for section in self.SECTIONS_NAMES: + if section != "globals": + code_object = getattr(self.CodeFile, section) + text += "def _runtime_%s_%s():\n" % (location_str, section) + section_code = sections_code.get(section) + if section_code: + text += section_code + else: + text += " pass\n\n" + runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str) runtimefile = open(runtimefile_path, 'w') - runtimefile.write(self.GetPythonCode()) + runtimefile.write(text) runtimefile.close() return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")) diff -r 55ed55ef7aea -r b1705000eba1 wxglade_hmi/wxglade_hmi.py --- a/wxglade_hmi/wxglade_hmi.py Sun May 12 23:32:30 2013 +0200 +++ b/wxglade_hmi/wxglade_hmi.py Mon May 13 14:31:23 2013 +0200 @@ -76,33 +76,59 @@ runtimefile.write(hmipyfile.read()) hmipyfile.close() - runtimefile.write(self.GetPythonCode()) - runtimefile.write(""" -%(declare)s - -def _runtime_%(location)s_start(): - global %(global)s - - def OnCloseFrame(evt): - wx.MessageBox(_("Please stop PLC to close")) - - %(init)s - -def _runtime_%(location)s_stop(): - global %(global)s - - %(cleanup)s - -""" % {"location": location_str, - "declare": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())), - "global": ",".join(hmi_frames.keys()), - "init": "\n".join(map(lambda x: """ + sections_code = self.GetSectionsCode() + + # Adding variables + runtimefile.write("## User variables reference\n" + + sections_code["variables"] + "\n") + + # Adding user global variables and routines + runtimefile.write("## User internal user variables and routines\n" + + sections_code["globals"] + "\n") + + for section in ["init", "cleanup"]: + if not sections_code[section]: + sections_code = " pass" + + sections_code.update({ + "location": location_str, + "declare_hmi": "\n".join(map(lambda x:"%s = None" % x, hmi_frames.keys())), + "global_hmi": ",".join(hmi_frames.keys()), + "init_hmi": "\n".join(map(lambda x: """ %(name)s = %(class)s(None) %(name)s.Bind(wx.EVT_CLOSE, OnCloseFrame) %(name)s.Show() """ % {"name": x[0], "class": x[1]}, hmi_frames.items())), - "cleanup": "\n ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))}) + "cleanup_hmi": "\n ".join(map(lambda x:"if %s is not None: %s.Destroy()" % (x,x), hmi_frames.keys()))}) + + runtimefile.write(""" +%(declare_hmi)s + +def _runtime_%(location)s_init(): +%(init)s + +def _runtime_%(location)s_cleanup(): +%(cleanup)s + +def _runtime_%(location)s_start(): + global %(global_hmi)s + +%(start)s + + def OnCloseFrame(evt): + wx.MessageBox(_("Please stop PLC to close")) + + %(init_hmi)s + +def _runtime_%(location)s_stop(): + global %(global_hmi)s + + %(cleanup_hmi)s + +%(stop)s + +""" % sections_code) runtimefile.close() return [], "", False, ("runtime_%s.py"%location_str, file(runtimefile_path,"rb")) diff -r 55ed55ef7aea -r b1705000eba1 xmlclass/__init__.py --- a/xmlclass/__init__.py Sun May 12 23:32:30 2013 +0200 +++ b/xmlclass/__init__.py Mon May 13 14:31:23 2013 +0200 @@ -24,5 +24,5 @@ # Package initialisation -from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr +from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr, UpdateXMLClassGlobals from xsdschema import XSDClassFactory, GenerateClassesFromXSD, GenerateClassesFromXSDstring diff -r 55ed55ef7aea -r b1705000eba1 xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Sun May 12 23:32:30 2013 +0200 +++ b/xmlclass/xmlclass.py Mon May 13 14:31:23 2013 +0200 @@ -1857,9 +1857,11 @@ def GenerateClasses(factory): ComputedClasses = factory.CreateClasses() if factory.FileName is not None and len(ComputedClasses) == 1: - globals().update(ComputedClasses[factory.FileName]) + UpdateXMLClassGlobals(ComputedClasses[factory.FileName]) return ComputedClasses[factory.FileName] else: - globals().update(ComputedClasses) + UpdateXMLClassGlobals(ComputedClasses) return ComputedClasses +def UpdateXMLClassGlobals(classes): + globals().update(classes)