Refuse close if PLC running.
authoretisserant
Wed, 27 Feb 2008 18:40:39 +0100
changeset 118 185d0d371ea4
parent 117 32099ee16154
child 119 f3819d2e8eff
Refuse close if PLC running.
Handling/display of change status for plugin. Now save when close.
Beremiz.py
plugger.py
plugins/canfestival/canfestival.py
--- a/Beremiz.py	Wed Feb 27 14:36:05 2008 +0100
+++ b/Beremiz.py	Wed Feb 27 18:40:39 2008 +0100
@@ -68,6 +68,8 @@
 SCROLLBAR_UNIT = 10
 WINDOW_COLOUR = wx.Colour(240,240,240)
 TITLE_COLOUR = wx.Colour(200,200,220)
+CHANGED_TITLE_COLOUR = wx.Colour(220,200,220)
+CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240)
 
 if wx.Platform == '__WXMSW__':
     faces = { 'times': 'Times New Roman',
@@ -322,6 +324,7 @@
         self.SetClientSize(wx.Size(1000, 600))
         self.SetMenuBar(self.menuBar1)
         self.Bind(wx.EVT_ACTIVATE, self.OnFrameActivated)
+        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
         
         if wx.VERSION < (2, 8, 0):
             self.MainSplitter = wx.SplitterWindow(id=ID_BEREMIZMAINSPLITTER,
@@ -384,6 +387,17 @@
             self.RefreshPluginTree()
         
         self.RefreshMainMenu()
+
+    def OnCloseFrame(self, event):
+        if self.PluginRoot.HasProjectOpened():
+            if self.PluginRoot.runningPLC is not None:
+                wx.MessageBox("Please stop any running PLC before closing")
+                event.Veto()
+                return
+            if self.PluginRoot.ProjectTestModified():
+                self.PluginRoot.SaveProject()
+                wx.MessageBox("Project saved")
+        event.Skip()
     
     def OnMoveWindow(self, event):
         self.GetBestSize()
@@ -422,8 +436,12 @@
         self.Freeze()
         self.ClearSizer(self.PLCParamsSizer)
         
-        if self.PluginRoot.HasProjectOpened():
+        if self.PluginRoot.HasProjectOpened():    
             plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
+            if self.PluginRoot.PlugTestModified():
+                bkgdclr = CHANGED_TITLE_COLOUR
+            else:
+                bkgdclr = TITLE_COLOUR
             plcwindow.SetBackgroundColour(TITLE_COLOUR)
             self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW)
             
@@ -494,6 +512,8 @@
                 event.Skip()
             minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id)
         
+            self.PluginInfos[self.PluginRoot] = {"main" : plcwindow, "params" : paramswindow}
+            
         self.PLCConfigMainSizer.Layout()
         self.RefreshScrollBars()
         self.Thaw()
@@ -541,19 +561,34 @@
         self.Freeze()
         self.ClearSizer(self.PluginTreeSizer)
         if self.PluginRoot.HasProjectOpened():
-            index = [0]
             for child in self.PluginRoot.IECSortedChilds():
-                self.GenerateTreeBranch(child, index)
+                self.GenerateTreeBranch(child)
                 if not self.PluginInfos[child]["expanded"]:
                     self.CollapsePlugin(child)
         self.PLCConfigMainSizer.Layout()
         self.RefreshScrollBars()
         self.Thaw()
 
+    def SetPluginParamsAttribute(self, plugin, *args, **kwargs):
+        res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs)
+        if StructChanged:
+            wx.CallAfter(self.RefreshPluginTree)
+        else:
+            if plugin == self.PluginRoot:
+                bkgdclr = CHANGED_TITLE_COLOUR
+                items = ["main", "params"]
+            else:
+                bkgdclr = CHANGED_WINDOW_COLOUR
+                items = ["left", "middle", "params"]
+            for i in items:
+                self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr)
+                self.PluginInfos[plugin][i].Refresh()
+        return res
+
     def ExpandPlugin(self, plugin, force = False):
         for child in self.PluginInfos[plugin]["children"]:
-            self.PluginTreeSizer.Show(self.PluginInfos[child]["left"])
-            self.PluginTreeSizer.Show(self.PluginInfos[child]["middle"])
+            self.PluginInfos[child]["left"].Show()
+            self.PluginInfos[child]["middle"].Show()
 #            self.PluginTreeSizer.Show(self.PluginInfos[child]["right"])
             if force or not self.PluginInfos[child]["expanded"]:
                 self.ExpandPlugin(child, force)
@@ -562,17 +597,22 @@
     
     def CollapsePlugin(self, plugin, force = False):
         for child in self.PluginInfos[plugin]["children"]:
-            self.PluginTreeSizer.Hide(self.PluginInfos[child]["left"])
-            self.PluginTreeSizer.Hide(self.PluginInfos[child]["middle"])
+            self.PluginInfos[child]["left"].Hide()
+            self.PluginInfos[child]["middle"].Hide()
 #            self.PluginTreeSizer.Hide(self.PluginInfos[child]["right"])
             if force or self.PluginInfos[child]["expanded"]:
                 self.CollapsePlugin(child, force)
                 if force:
                     self.PluginInfos[child]["expanded"] = False
 
-    def GenerateTreeBranch(self, plugin, index):
+    def GenerateTreeBranch(self, plugin):
         leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
-        leftwindow.SetBackgroundColour(WINDOW_COLOUR)
+        if plugin.PlugTestModified():
+            bkgdclr=CHANGED_WINDOW_COLOUR
+        else:
+            bkgdclr=WINDOW_COLOUR
+
+        leftwindow.SetBackgroundColour(bkgdclr)
         
         if plugin not in self.PluginInfos:
             self.PluginInfos[plugin] = {"expanded" : False, "left_visible" : False, "middle_visible" : False}
@@ -607,8 +647,7 @@
         enablebutton.SetBitmapSelected(wx.Bitmap(os.path.join(CWD, 'images', 'Enabled.png')))
         enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled())
         def toggleenablebutton(event):
-            res, StructChanged = plugin.SetParamsAttribute("BaseParams.Enabled", enablebutton.GetToggle(), self.Log)
-            if StructChanged: wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle(), self.Log)
             enablebutton.SetToggle(res)
             event.Skip()
         enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
@@ -690,10 +729,10 @@
             leftbuttonsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
         
         tc_id = wx.NewId()
-        tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 35), style=wx.TE_PROCESS_ENTER|wx.NO_BORDER)
+        tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 35), style=wx.NO_BORDER)
         tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
         tc.SetValue(plugin.MandatoryParams[1].getName())
-        tc.Bind(wx.EVT_TEXT_ENTER, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id)
+        tc.Bind(wx.EVT_KILL_FOCUS, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id)
         leftbuttonsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
        
 
@@ -726,7 +765,7 @@
         leftwindowsizer.AddWindow(lb, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
 
         middlewindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
-        middlewindow.SetBackgroundColour(wx.Colour(240,240,240))
+        middlewindow.SetBackgroundColour(bkgdclr)
         
         self.PluginTreeSizer.AddWindow(middlewindow, 0, border=0, flag=wx.GROW)
         
@@ -745,10 +784,11 @@
         middlewindowsizer.AddSizer(middleparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT)
         
         paramswindow = wx.Panel(middlewindow, -1, size=wx.Size(-1, -1))
-        paramswindow.SetBackgroundColour(WINDOW_COLOUR)
+        paramswindow.SetBackgroundColour(bkgdclr)
         
         psizer = wx.BoxSizer(wx.HORIZONTAL)
         paramswindow.SetSizer(psizer)
+        self.PluginInfos[plugin]["params"] = paramswindow
         
         middleparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL)
         
@@ -801,13 +841,11 @@
             event.Skip()
         middleminimizebutton.Bind(wx.EVT_BUTTON, togglemiddlerightwindow, id=middleminimizebutton_id)
         
-        self.PluginInfos[plugin]["left"] = index[0]
-        self.PluginInfos[plugin]["middle"] = index[0] + 1
-#        self.PluginInfos[plugin]["right"] = index[0] + 2
-#        index[0] += 3
-        index[0] += 2
+        self.PluginInfos[plugin]["left"] = leftwindow
+        self.PluginInfos[plugin]["middle"] = middlewindow
+#        self.PluginInfos[plugin]["right"] = rightwindow
         for child in self.PluginInfos[plugin]["children"]:
-            self.GenerateTreeBranch(child, index)
+            self.GenerateTreeBranch(child)
             if not self.PluginInfos[child]["expanded"]:
                 self.CollapsePlugin(child)
 
@@ -827,8 +865,7 @@
         
     def GetItemChannelChangedFunction(self, plugin, value):
         def OnPluginTreeItemChannelChanged(event):
-            res, StructChanged = plugin.SetParamsAttribute("BaseParams.IEC_Channel", value, self.Log)
-            wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value, self.Log)
             event.Skip()
         return OnPluginTreeItemChannelChanged
     
@@ -866,16 +903,14 @@
     
     def GetChoiceCallBackFunction(self, choicectrl, plugin, path):
         def OnChoiceChanged(event):
-            res, StructChanged = plugin.SetParamsAttribute(path, choicectrl.GetStringSelection(), self.Log)
-            if StructChanged: wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection(), self.Log)
             choicectrl.SetStringSelection(res)
             event.Skip()
         return OnChoiceChanged
     
     def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path):
         def OnChoiceContentChanged(event):
-            res, StructChanged = plugin.SetParamsAttribute(path, choicectrl.GetStringSelection(), self.Log)
-            if StructChanged: wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection(), self.Log)
             choicectrl.SetStringSelection(res)
             infos = self.PluginRoot.GetParamsAttributes(path)
             staticbox = staticboxsizer.GetStaticBox()
@@ -893,16 +928,14 @@
     
     def GetTextCtrlCallBackFunction(self, textctrl, plugin, path):
         def OnTextCtrlChanged(event):
-            res, StructChanged = plugin.SetParamsAttribute(path, textctrl.GetValue(), self.Log)
-            if StructChanged: wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue(), self.Log)
             textctrl.SetValue(res)
             event.Skip()
         return OnTextCtrlChanged
     
     def GetCheckBoxCallBackFunction(self, chkbx, plugin, path):
         def OnCheckBoxChanged(event):
-            res, StructChanged = plugin.SetParamsAttribute(path, chkbx.IsChecked(), self.Log)
-            if StructChanged: wx.CallAfter(self.RefreshPluginTree)
+            res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked(), self.Log)
             chkbx.SetValue(res)
             event.Skip()
         return OnCheckBoxChanged
@@ -1040,10 +1073,9 @@
                     spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
                 else:
                     textctrl = wx.TextCtrl(id=id, name=element_infos["name"], parent=parent, 
-                        pos=wx.Point(0, 0), size=wx.Size(150, 25), style=wx.TE_PROCESS_ENTER)
+                        pos=wx.Point(0, 0), size=wx.Size(150, 25), style=0)#wx.TE_PROCESS_ENTER)
                     boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
                     textctrl.SetValue(str(element_infos["value"]))
-                    textctrl.Bind(wx.EVT_TEXT_ENTER, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path), id=id)
                     textctrl.Bind(wx.EVT_KILL_FOCUS, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path))
             first = False
     
@@ -1100,6 +1132,7 @@
     def OnSaveProjectMenu(self, event):
         if self.PluginRoot.HasProjectOpened():
             self.PluginRoot.SaveProject()
+            self.RefreshAll()
         event.Skip()
     
     def OnPropertiesMenu(self, event):
--- a/plugger.py	Wed Feb 27 14:36:05 2008 +0100
+++ b/plugger.py	Wed Feb 27 18:40:39 2008 +0100
@@ -110,6 +110,19 @@
         return os.path.join(self.PlugParent.PlugPath(), PlugName + NameTypeSeparator + self.PlugType)
     
     def PlugTestModified(self):
+        return self.ChangesToSave
+
+    def ProjectTestModified(self):
+        """
+        recursively check modified status
+        """
+        if self.PlugTestModified():
+            return True
+
+        for PlugChild in self.IterChilds():
+            if PlugChild.ProjectTestModified():
+                return True
+
         return False
         
     def OnPlugSave(self):
@@ -132,13 +145,14 @@
             return params
         
     def SetParamsAttribute(self, path, value, logger):
+        self.ChangesToSave = True
         # Filter IEC_Channel and Name, that have specific behavior
         if path == "BaseParams.IEC_Channel":
             return self.FindNewIEC_Channel(value,logger), True
         elif path == "BaseParams.Name":
             res = self.FindNewName(value,logger)
             self.PlugRequestSave()
-            return res, False
+            return res, True
         
         parts = path.split(".", 1)
         if self.MandatoryParams and parts[0] == self.MandatoryParams[0]:
@@ -172,7 +186,9 @@
         result = self.OnPlugSave()
         if not result:
             return "Error while saving \"%s\""%self.PlugPath()
-        
+
+        # mark plugin as saved
+        self.ChangesToSave = False        
         # go through all childs and do the same
         for PlugChild in self.IterChilds():
             result = PlugChild.PlugRequestSave()
@@ -473,6 +489,8 @@
                         PlugClass.__init__(_self)
                     #Load and init all the childs
                     _self.LoadChilds(logger)
+                    #just loaded, nothing to saved
+                    _self.ChangesToSave = False
                 else:
                     # If plugin do not have corresponding file/dirs - they will be created on Save
                     os.mkdir(_self.PlugPath())
@@ -482,6 +500,8 @@
                     if getattr(PlugClass, "__init__", None):
                         PlugClass.__init__(_self)
                     _self.PlugRequestSave()
+                    #just created, must be saved
+                    _self.ChangesToSave = True
             
             def _getBuildPath(_self):
                 return self._getBuildPath()
@@ -658,22 +678,23 @@
         self._AddParamsMembers()
         self.PluggedChilds = {}
         """
-
+        # In both new or load scenario, no need to save
+        self.ChangesToSave = False        
         # root have no parent
         self.PlugParent = None
         # Keep track of the plugin type name
         self.PlugType = "Beremiz"
-        
         # After __init__ root plugin is not valid
         self.ProjectPath = None
         self.PLCEditor = None
-        
         # copy PluginMethods so that it can be later customized
         self.PluginMethods = [dic.copy() for dic in self.PluginMethods]
-        
+        # special root member for handlig PLC execution
         self.runningPLC = None
 
-    
+    def PlugTestModified(self):
+         return self.ChangesToSave or not self.ProjectIsSaved()
+
     def HasProjectOpened(self):
         """
         Return if a project is actually opened
--- a/plugins/canfestival/canfestival.py	Wed Feb 27 14:36:05 2008 +0100
+++ b/plugins/canfestival/canfestival.py	Wed Feb 27 18:40:39 2008 +0100
@@ -86,7 +86,7 @@
             self._View.Close()
 
     def PlugTestModified(self):
-        return self.HasChanged()
+        return self.ChangesToSave or self.HasChanged()
         
     def OnPlugSave(self):
         self.SetRoot(self.PlugPath())