Adding support for function and functionBlock (standard or user) library
authorlbessard
Fri, 22 Aug 2008 17:22:54 +0200
changeset 239 d12779e971bd
parent 238 389f2046e495
child 240 714000906db1
Adding support for function and functionBlock (standard or user) library
PLCOpenEditor.py
RessourceEditor.py
Viewer.py
--- a/PLCOpenEditor.py	Fri Aug 22 17:18:12 2008 +0200
+++ b/PLCOpenEditor.py	Fri Aug 22 17:22:54 2008 +0200
@@ -52,11 +52,13 @@
 [ID_PLCOPENEDITOR, ID_PLCOPENEDITORTREENOTEBOOK,
  ID_PLCOPENEDITORTYPESTREE, ID_PLCOPENEDITORINSTANCESTREE, 
  ID_PLCOPENEDITORMAINSPLITTER, ID_PLCOPENEDITORSECONDSPLITTER, 
- ID_PLCOPENEDITOREDITORPANEL, ID_PLCOPENEDITORTABSOPENED, 
+ ID_PLCOPENEDITORTHIRDSPLITTER, ID_PLCOPENEDITORLIBRARYPANEL, 
+ ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, 
+ ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED,
  ID_PLCOPENEDITORTOOLBAR, ID_PLCOPENEDITORDEFAULTTOOLBAR, 
  ID_PLCOPENEDITORSFCTOOLBAR, ID_PLCOPENEDITORFBDTOOLBAR, 
  ID_PLCOPENEDITORLDTOOLBAR,
-] = [wx.NewId() for _init_ctrls in range(13)]
+] = [wx.NewId() for _init_ctrls in range(17)]
 
 [ID_PLCOPENEDITORFILEMENUGENERATE, 
 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]
@@ -335,6 +337,22 @@
         self._init_coll_FileMenu_Items(self.FileMenu)
         self._init_coll_EditMenu_Items(self.EditMenu)
         self._init_coll_HelpMenu_Items(self.HelpMenu)
+
+    def _init_coll_MainLibrarySizer_Items(self, parent):
+        parent.AddWindow(self.LibraryTree, 0, border=0, flag=wx.GROW)
+        parent.AddSizer(self.LibraryComment, 0, border=0, flag=wx.GROW)
+
+    def _init_coll_MainLibrarySizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(0)
+
+    def _init_sizers(self):
+        self.MainLibrarySizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
+        
+        self._init_coll_MainLibrarySizer_Growables(self.MainLibrarySizer)
+        self._init_coll_MainLibrarySizer_Items(self.MainLibrarySizer)
+        
+        self.LibraryPanel.SetSizer(self.MainLibrarySizer)
         
     def _init_ctrls(self, prnt):
         wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name=u'PLCOpenEditor',
@@ -430,6 +448,15 @@
             
             self.MainSplitter.SplitVertically(self.TreeNoteBook, self.SecondSplitter, 200)
             
+            self.VariablePanelIndexer = VariablePanelIndexer(self.SecondSplitter, self, self.Controler)
+        
+            self.ThirdSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORTHIRDSPLITTER,
+                  name='ThirdSplitter', parent=self.SecondSplitter, point=wx.Point(0, 0),
+                  size=wx.Size(0, 0), style=wx.SP_3D)
+            self.ThirdSplitter.SetMinimumPaneSize(1)
+            
+            self.SecondSplitter.SplitHorizontally(self.ThirdSplitter, self.VariablePanelIndexer, -200)
+            
             self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED,
                   name='TabsOpened', parent=self.SecondSplitter, pos=wx.Point(0,
                   0), size=wx.Size(0, 0), style=0)
@@ -439,22 +466,48 @@
             else:
                 wx.EVT_NOTEBOOK_PAGE_CHANGED(self.TabsOpened, ID_PLCOPENEDITORTABSOPENED,
                     self.OnPouSelectedChanged)
-        else:
+        
+            self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL,
+                  name='LibraryPanel', parent=self.ThirdSplitter, pos=wx.Point(0,
+                  0), size=wx.Size(0, 0), style=wx.SUNKEN_BORDER)
+            
+            self.ThirdSplitter.SplitVertically(self.TabsOpened, self.LibraryPanel, -250)
+        else:
+            self.VariablePanelIndexer = VariablePanelIndexer(self, self, self.Controler)
+            self.AUIManager.AddPane(self.VariablePanelIndexer, wx.aui.AuiPaneInfo().Caption("Variables").Bottom().Layer(0).BestSize(wx.Size(800, 200)).CloseButton(False))
+        
             self.TabsOpened = wx.aui.AuiNotebook(self)
             self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,
                     self.OnPouSelectedChanged)
             self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane())
-            
-        if wx.VERSION < (2, 8, 0):
-            self.VariablePanelIndexer = VariablePanelIndexer(self.SecondSplitter, self, self.Controler)
-            
-            self.SecondSplitter.SplitHorizontally(self.TabsOpened, self.VariablePanelIndexer, -200)
-        else:
-            self.VariablePanelIndexer = VariablePanelIndexer(self, self, self.Controler)
-            self.AUIManager.AddPane(self.VariablePanelIndexer, wx.aui.AuiPaneInfo().Caption("Variables").Bottom().Layer(0).BestSize(wx.Size(800, 200)).CloseButton(False))
-            
+        
+            self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL,
+                  name='LibraryPanel', parent=self, pos=wx.Point(0,
+                  0), size=wx.Size(0, 0), style=0)
+            self.AUIManager.AddPane(self.LibraryPanel, wx.aui.AuiPaneInfo().Caption("Library").Right().Layer(0).BestSize(wx.Size(250, 400)).CloseButton(False))
+        
             self.AUIManager.Update()
         
+        if wx.Platform == '__WXMSW__':
+            treestyle = wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER
+        else:
+            treestyle = wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_SINGLE|wx.SUNKEN_BORDER
+        self.LibraryTree = wx.TreeCtrl(id=ID_PLCOPENEDITORLIBRARYTREE,
+                  name='LibraryTree', parent=self.LibraryPanel, 
+                  pos=wx.Point(0, 0), size=wx.Size(0, 0),
+                  style=treestyle)
+        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnLibraryTreeItemSelected,
+              id=ID_PLCOPENEDITORLIBRARYTREE)
+        self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnLibraryTreeBeginDrag,
+              id=ID_PLCOPENEDITORLIBRARYTREE)
+        
+        self.LibraryComment = wx.TextCtrl(id=ID_PLCOPENEDITORLIBRARYCOMMENT,
+                  name='LibraryComment', parent=self.LibraryPanel, 
+                  pos=wx.Point(0, 0), size=wx.Size(0, 60), 
+                  style=wx.TE_READONLY|wx.TE_MULTILINE)
+        
+        self._init_sizers()
+        
     def __init__(self, parent, controler = None, fileOpen = None):
         self.ModeSolo = controler == None
         if self.ModeSolo:
@@ -525,6 +578,7 @@
         if not self.ModeSolo or fileOpen is not None:
             self.RefreshTypesTree()
             self.RefreshInstancesTree()
+            self.RefreshLibraryTree()
         
         self.RefreshFileMenu()
         self.RefreshEditMenu()
@@ -675,6 +729,7 @@
             self.RefreshEditMenu()
             self.RefreshTypesTree()
             self.RefreshInstancesTree()
+            self.RefreshLibraryTree()
         event.Skip()
 
     def OnOpenProjectMenu(self, event):
@@ -700,6 +755,7 @@
                 self.Controler.OpenXMLFile(filepath)
                 self.RefreshTypesTree()
                 self.RefreshInstancesTree()
+                self.RefreshLibraryTree()
             self.RefreshTitle()
             self.RefreshFileMenu()
             self.RefreshEditMenu()
@@ -916,6 +972,8 @@
         self.RefreshTitle()
         self.RefreshEditMenu()
         self.RefreshTypesTree()
+        self.RefreshInstancesTree()
+        self.RefreshLibraryTree()
         event.Skip()
     
     def OnRedoMenu(self, event):
@@ -935,6 +993,8 @@
         self.RefreshTitle()
         self.RefreshEditMenu()
         self.RefreshTypesTree()
+        self.RefreshInstancesTree()
+        self.RefreshLibraryTree()
         event.Skip()
 
     def OnCutMenu(self, event):
@@ -1022,6 +1082,8 @@
                 self.RefreshTitle()
                 self.RefreshEditMenu()
                 self.RefreshTypesTree()
+                self.RefreshInstancesTree()
+                self.RefreshLibraryTree()
                 self.RefreshToolBar()
         elif isinstance(window, (Viewer, TextViewer)):
             event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
@@ -1097,7 +1159,7 @@
 
 
 #-------------------------------------------------------------------------------
-#                         Project Tree Management Functions
+#                         Types Tree Management Functions
 #-------------------------------------------------------------------------------
 
     def RefreshTypesTree(self):
@@ -1106,7 +1168,7 @@
         if not root.IsOk():
             root = self.TypesTree.AddRoot(infos["name"])
         self.GenerateTypesTreeBranch(root, infos)
-        self.TypesTree.Expand(self.TypesTree.GetRootItem())
+        self.TypesTree.Expand(root)
 
     def GenerateTypesTreeBranch(self, root, infos, topology=False):
         to_delete = []
@@ -1577,7 +1639,7 @@
 
 
 #-------------------------------------------------------------------------------
-#                         Topology Tree Management Functions
+#                         Instances Tree Management Functions
 #-------------------------------------------------------------------------------
 
     def RefreshInstancesTree(self):
@@ -1586,7 +1648,7 @@
         if not root.IsOk():
             root = self.InstancesTree.AddRoot(infos["name"])
         self.GenerateInstancesTreeBranch(root, infos)
-        self.InstancesTree.Expand(self.InstancesTree.GetRootItem())
+        self.InstancesTree.Expand(root)
 
     def GenerateInstancesTreeBranch(self, root, infos):
         to_delete = []
@@ -1615,6 +1677,79 @@
             self.InstancesTree.Delete(item)
 
 #-------------------------------------------------------------------------------
+#                         Library Tree Management Functions
+#-------------------------------------------------------------------------------
+
+    def RefreshLibraryTree(self):
+        to_delete = []
+        blocktypes = self.Controler.GetBlockTypes()
+        if wx.Platform == '__WXMSW__':
+            root = self.LibraryTree.AddRoot("Block Types")
+            self.TypeTree.SetPyData(root, {"type" : CATEGORY})
+        else:
+            root = self.LibraryTree.AddRoot("")
+        if wx.VERSION >= (2, 6, 0):
+            category_item, root_cookie = self.LibraryTree.GetFirstChild(root)
+        else:
+            category_item, root_cookie = self.LibraryTree.GetFirstChild(root, 0)
+        for category in blocktypes:
+            print category["name"]
+            if not category_item.IsOk():
+                category_item = self.LibraryTree.AppendItem(root, category["name"])
+                if wx.Platform != '__WXMSW__':
+                    category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
+            else:
+                self.LibraryTree.SetItemText(category_item, category["name"])
+            self.LibraryTree.SetPyData(category_item, {"type" : CATEGORY})
+            if wx.VERSION >= (2, 6, 0):
+                blocktype_item, category_cookie = self.LibraryTree.GetFirstChild(category_item)
+            else:
+                blocktype_item, category_cookie = self.LibraryTree.GetFirstChild(category_item, 0)        
+            for blocktype in category["list"]:
+                if not blocktype_item.IsOk():
+                    blocktype_item = self.LibraryTree.AppendItem(category_item, blocktype["name"])
+                    if wx.Platform != '__WXMSW__':
+                        blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
+                else:
+                    self.LibraryTree.SetItemText(blocktype_item, blocktype["name"])
+                self.LibraryTree.SetPyData(blocktype_item, {"type" : BLOCK, "block_type" : blocktype["type"], "inputs" : tuple([type for name, type, modifier in blocktype["inputs"]])})
+                blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
+            while blocktype_item.IsOk():
+                to_delete.append(blocktype_item)
+                blocktype_item, category_cookie = self.LibraryTree.GetNextChild(category_item, category_cookie)
+            category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
+        while category_item.IsOk():
+            to_delete.append(category_item)
+            category_item, root_cookie = self.LibraryTree.GetNextChild(root, root_cookie)
+        for item in to_delete:
+            self.LibraryTree.Delete(item)
+        if wx.Platform == '__WXMSW__':        
+            self.LibraryTree.Expand(root)
+
+    def OnLibraryTreeItemSelected(self, event):
+        selected = event.GetItem()
+        pydata = self.LibraryTree.GetPyData(selected)
+        if pydata["type"] != CATEGORY:
+            blocktype = self.Controler.GetBlockType(self.LibraryTree.GetItemText(selected), pydata["inputs"])
+            if blocktype:
+                self.LibraryComment.SetValue(blocktype["comment"])
+            else:
+                self.LibraryComment.SetValue("")
+        else:
+            self.LibraryComment.SetValue("")
+        event.Skip()
+
+    def OnLibraryTreeBeginDrag(self, event):
+        selected = event.GetItem()
+        pydata = self.LibraryTree.GetPyData(selected)
+        if selected is not None and pydata["type"] == BLOCK:
+            data = wx.TextDataObject(str((self.LibraryTree.GetItemText(selected), 
+                pydata["block_type"], "", pydata["inputs"])))
+            dragSource = wx.DropSource(self.LibraryTree)
+            dragSource.SetData(data)
+            dragSource.DoDragDrop()
+
+#-------------------------------------------------------------------------------
 #                          ToolBar Management Functions
 #-------------------------------------------------------------------------------
 
@@ -1848,6 +1983,7 @@
                 self.RefreshTitle()
                 self.RefreshEditMenu()
                 self.RefreshTypesTree()
+                self.RefreshLibraryTree()
             dialog.Destroy()
             event.Skip()
         return OnAddPouMenu
@@ -1863,7 +1999,7 @@
                 self.RefreshTitle()
                 self.RefreshEditMenu()
                 self.RefreshTypesTree()
-            dialog.Destroy()
+                dialog.Destroy()
             event.Skip()
         return OnAddTransitionMenu
 
@@ -1892,6 +2028,7 @@
             self.RefreshTitle()
             self.RefreshEditMenu()
             self.RefreshTypesTree()
+            self.RefreshInstancesTree()
         dialog.Destroy()
         event.Skip()
 
@@ -1906,6 +2043,7 @@
                 self.RefreshTitle()
                 self.RefreshEditMenu()
                 self.RefreshTypesTree()
+                self.RefreshInstancesTree()
             dialog.Destroy()
             event.Skip()
         return OnAddResourceMenu
@@ -1949,6 +2087,8 @@
                 self.RefreshTitle()
                 self.RefreshEditMenu()
                 self.RefreshTypesTree()
+                self.RefreshInstancesTree()
+                self.RefreshLibraryTree()
                 self.RefreshToolBar()
             else:
                 message = wx.MessageDialog(self, "%s is used by one or more POUs. It can't be removed!"%selected, "Error", wx.OK|wx.ICON_ERROR)
@@ -1956,21 +2096,6 @@
                 message.Destroy()
         event.Skip()
 
-    def OnRemoveConfigurationMenu(self, event):
-        selected = self.TypesTree.GetSelection()
-        if self.TypesTree.GetPyData(selected) == ITEM_CONFIGURATION: 
-            name = self.TypesTree.GetItemText(selected)
-            self.Controler.ProjectRemoveConfiguration(name)
-            tagname = self.Controler.ComputeConfigurationName(name)
-            idx = self.IsOpened(tagname)
-            if idx is not None:
-                self.VariablePanelIndexer.RemoveVariablePanel(tagname)
-                self.TabsOpened.DeletePage(idx)
-            self.RefreshTitle()
-            self.RefreshEditMenu()
-            self.RefreshTypesTree()
-        event.Skip()
-
     def OnRemoveTransitionMenu(self, event):
         selected = self.TypesTree.GetSelection()
         if self.TypesTree.GetPyData(selected) == ITEM_TRANSITION: 
@@ -2013,6 +2138,22 @@
             self.RefreshTypesTree()
         event.Skip()
 
+    def OnRemoveConfigurationMenu(self, event):
+        selected = self.TypesTree.GetSelection()
+        if self.TypesTree.GetPyData(selected) == ITEM_CONFIGURATION: 
+            name = self.TypesTree.GetItemText(selected)
+            self.Controler.ProjectRemoveConfiguration(name)
+            tagname = self.Controler.ComputeConfigurationName(name)
+            idx = self.IsOpened(tagname)
+            if idx is not None:
+                self.VariablePanelIndexer.RemoveVariablePanel(tagname)
+                self.TabsOpened.DeletePage(idx)
+            self.RefreshTitle()
+            self.RefreshEditMenu()
+            self.RefreshTypesTree()
+            self.RefreshInstancesTree()
+        event.Skip()
+
     def OnRemoveResourceMenu(self, event):
         selected = self.TypesTree.GetSelection()
         if self.TypesTree.GetPyData(selected) == ITEM_RESOURCE:
@@ -2032,6 +2173,7 @@
             self.RefreshTitle()
             self.RefreshEditMenu()
             self.RefreshTypesTree()
+            self.RefreshInstancesTree()
         event.Skip()
     
     def OnPLCOpenEditorMenu(self, event):
@@ -3759,6 +3901,7 @@
         self.SaveValues()
         self.RefreshValues()
         self.RefreshButtons()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnDeleteButton(self, event):
@@ -3767,16 +3910,19 @@
         self.SaveValues()
         self.RefreshValues()
         self.RefreshButtons()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnUpButton(self, event):
         self.MoveValue(self.VariablesGrid.GetGridCursorRow(), -1)
         self.RefreshButtons()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnDownButton(self, event):
         self.MoveValue(self.VariablesGrid.GetGridCursorRow(), 1)
         self.RefreshButtons()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnVariablesGridCellChange(self, event):
@@ -3813,11 +3959,13 @@
                 self.ParentWindow.RefreshEditor(variablepanel = False)
                 self.ParentWindow.RefreshTitle()
                 self.ParentWindow.RefreshEditMenu()
+                self.ParentWindow.RefreshInstancesTree()
                 event.Skip()
         else:
             self.SaveValues()
             if colname == "Class":
                 self.Table.ResetView(self.VariablesGrid)
+            self.ParentWindow.RefreshInstancesTree()
             event.Skip()
     
     def OnVariablesGridEditorShown(self, event):
--- a/RessourceEditor.py	Fri Aug 22 17:18:12 2008 +0200
+++ b/RessourceEditor.py	Fri Aug 22 17:22:54 2008 +0200
@@ -523,6 +523,7 @@
         self.InstancesTable.AppendRow(self.InstancesDefaultValue.copy())
         self.RefreshModel()
         self.RefreshView()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnDeleteInstanceButton(self, event):
@@ -530,6 +531,7 @@
         self.InstancesTable.RemoveRow(row)
         self.RefreshModel()
         self.RefreshView()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnUpInstanceButton(self, event):
@@ -537,6 +539,7 @@
         self.InstancesTable.MoveRow(row, -1, self.InstancesGrid)
         self.RefreshModel()
         self.RefreshView()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnDownInstanceButton(self, event):
@@ -544,6 +547,7 @@
         self.InstancesTable.MoveRow(row, 1, self.InstancesGrid)
         self.RefreshModel()
         self.RefreshView()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
     def OnTasksGridCellChange(self, event):
@@ -568,6 +572,7 @@
     def OnInstancesGridCellChange(self, event):
         self.RefreshModel()
         self.RefreshView()
+        self.ParentWindow.RefreshInstancesTree()
         event.Skip()
 
 #-------------------------------------------------------------------------------
--- a/Viewer.py	Fri Aug 22 17:18:12 2008 +0200
+++ b/Viewer.py	Fri Aug 22 17:22:54 2008 +0200
@@ -108,10 +108,16 @@
                     message = "\"%s\" is already used by \"%s\"!"%(name, values[0])
                 else:
                     blockname = values[2]
+                    if len(values) > 3:
+                        blockinputs = values[3]
+                    else:
+                        blockinputs = None
                     if values[1] != "function" and blockname == "":
-                        dialog = wx.TextEntryDialog(self.ParentWindow, "Block name", "Please enter a block name", "", wx.OK|wx.CANCEL|wx.CENTRE)
-                        if dialog.ShowModal():
+                        dialog = wx.TextEntryDialog(self.ParentWindow.ParentWindow, "Block name", "Please enter a block name", "", wx.OK|wx.CANCEL|wx.CENTRE)
+                        if dialog.ShowModal() == wx.ID_OK:
                             blockname = dialog.GetValue()
+                        else:
+                            return
                         dialog.Destroy()
                     if blockname.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames()]:
                         message = "\"%s\" pou already exists!"%blockname
@@ -119,7 +125,7 @@
                         message = "\"%s\" element for this pou already exists!"%blockname
                     else:
                         id = self.ParentWindow.GetNewId()
-                        block = FBD_Block(self.ParentWindow, values[0], blockname, id)
+                        block = FBD_Block(self.ParentWindow, values[0], blockname, id, inputs = blockinputs)
                         width, height = block.GetMinSize()
                         if scaling is not None:
                             x = round(float(x) / float(scaling[0])) * scaling[0]
@@ -1432,6 +1438,7 @@
             self.RefreshBuffer()
             self.RefreshScrollBars()
             self.ParentWindow.RefreshVariablePanel(self.TagName)
+            self.ParentWindow.RefreshInstancesTree()
             block.Refresh()
         dialog.Destroy()
     
@@ -1721,6 +1728,7 @@
             self.RefreshBuffer()
             self.RefreshScrollBars()
             self.ParentWindow.RefreshVariablePanel(self.TagName)
+            self.ParentWindow.RefreshInstancesTree()
             block.Refresh(rect)
         dialog.Destroy()
 
@@ -2105,7 +2113,8 @@
         for element in elements:
             element.RefreshModel()
         wx.CallAfter(self.ParentWindow.RefreshVariablePanel, self.TagName)
-
+        wx.CallAfter(self.ParentWindow.RefreshInstancesTree)
+        
     def DeleteVariable(self, variable):
         connectors = variable.GetConnectors()
         if connectors["output"]:
@@ -2238,6 +2247,8 @@
             self.SelectedElement = None
             self.RefreshBuffer()
             self.RefreshScrollBars()
+            self.ParentWindow.RefreshVariablePanel(self.TagName)
+            self.ParentWindow.RefreshInstancesTree()
             self.RefreshRect(self.GetScrolledRect(rect), False)
         
     def Copy(self):
@@ -2255,6 +2266,7 @@
             self.RefreshBuffer()
             self.RefreshScrollBars()
             self.ParentWindow.RefreshVariablePanel(self.TagName)
+            self.ParentWindow.RefreshInstancesTree()
 
     def CanAddBlock(self, block):
         if self.CurrentLanguage == "SFC":