# HG changeset patch # User laurent # Date 1248432597 -7200 # Node ID 020420ad89148aaa6d174266a0300de8842933d6 # Parent 6a72016d721a1b710fc50fbf56e2856de90ead4d Adding comments and code improvements diff -r 6a72016d721a -r 020420ad8914 PLCOpenEditor.py --- a/PLCOpenEditor.py Fri Jul 24 12:29:48 2009 +0200 +++ b/PLCOpenEditor.py Fri Jul 24 12:49:57 2009 +0200 @@ -64,9 +64,11 @@ ID_PLCOPENEDITORLDTOOLBAR, ] = [wx.NewId() for _init_ctrls in range(17)] +# Define PLCOpenEditor FileMenu extra items id [ID_PLCOPENEDITORFILEMENUGENERATE, ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] +# Define PLCOpenEditor EditMenu extra items id [ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, @@ -77,7 +79,7 @@ # ToolBars definitions #------------------------------------------------------------------------------- - +# Define PLCOpenEditor Toolbar items id [ID_PLCOPENEDITORTOOLBARSELECTION, ID_PLCOPENEDITORTOOLBARCOMMENT, ID_PLCOPENEDITORTOOLBARVARIABLE, ID_PLCOPENEDITORTOOLBARBLOCK, ID_PLCOPENEDITORTOOLBARCONNECTION, ID_PLCOPENEDITORTOOLBARWIRE, @@ -89,6 +91,14 @@ ID_PLCOPENEDITORTOOLBARJUMP, ] = [wx.NewId() for _init_coll_DefaultToolBar_Items in range(17)] +# Define behaviour of each Toolbar item according to current POU body type +# Informations meaning are in this order: +# - Item is toggled +# - PLCOpenEditor mode where item is displayed (could be more then one) +# - Item id +# - Item callback function name +# - Item icon filename +# - Item tooltip text ToolBarItems = { "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, ID_PLCOPENEDITORTOOLBARCOMMENT, "OnCommentTool", @@ -169,12 +179,56 @@ "IL" : [] } +#------------------------------------------------------------------------------- +# Helper Functions +#------------------------------------------------------------------------------- + +# Compatibility function for wx versions < 2.6 def AppendMenu(parent, help, id, kind, text): if wx.VERSION >= (2, 6, 0): parent.Append(help=help, id=id, kind=kind, text=text) else: parent.Append(helpString=help, id=id, kind=kind, item=text) +[TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, + INSTANCESTREE, LIBRARYTREE, SCALING +] = [wx.NewId() for _refresh_elements in range(9)] + +def GetShortcutKeyCallbackFunction(viewer_function): + def ShortcutKeyFunction(self, event): + control = self.FindFocus() + if isinstance(control, (Viewer, TextViewer)): + getattr(control, viewer_function)() + elif isinstance(control, wx.TextCtrl): + control.ProcessEvent(event) + event.Skip() + return ShortcutKeyFunction + +def GetParentName(tree, item, parent_type): + parent_item = tree.GetItemParent(item) + parent_item_type = tree.GetPyData(parent_item) + while parent_item_type != parent_type: + parent_item = tree.GetItemParent(parent_item) + parent_item_type = tree.GetPyData(parent_item) + return tree.GetItemText(parent_item) + +def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None): + def DeleteElementFunction(self, selected): + name = self.TypesTree.GetItemText(selected) + if check_function is None or not check_function(self.Controler, name, self.Debug): + if parent_type is not None: + parent_name = GetParentName(self.TypesTree, selected, parent_type) + remove_function(self.Controler, parent_name, name) + else: + remove_function(self.Controler, name) + else: + self.ShowErrorMessage("\"%s\" is used by one or more POUs. It can't be removed!"%name) + return DeleteElementFunction + +#------------------------------------------------------------------------------- +# PLCOpenEditor Main Class +#------------------------------------------------------------------------------- + class PLCOpenEditor(wx.Frame): # Compatibility function for wx versions < 2.6 @@ -430,6 +484,10 @@ name='InstancesTree', parent=self.TreeNoteBook, pos=wx.Point(0, 0), size=wx.Size(0, 0), style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER) + + self.TabsImageList = wx.ImageList(16, 31) + self.TabsImageListIndexes = {} + if self.Debug: if wx.VERSION >= (2, 6, 0): self.InstancesTree.Bind(wx.EVT_RIGHT_UP, self.OnInstancesTreeRightUp) @@ -540,9 +598,7 @@ self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED, name='TabsOpened', parent=self.ThirdSplitter, pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0) - self.TabsImageList = wx.ImageList(16, 31) self.TabsOpened.SetImageList(self.TabsImageList) - self.TabsImageListIndexes = {} if wx.VERSION >= (2, 6, 0): self.TabsOpened.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPouSelectedChanged, id=ID_PLCOPENEDITORTABSOPENED) @@ -579,11 +635,19 @@ if USE_AUI: self.AUIManager.Update() + ## Constructor of the PLCOpenEditor class. + # @param parent The parent window. + # @param controler The controler been used by PLCOpenEditor (default: None). + # @param fileOpen The filepath to open if no controler defined (default: None). + # @param debug The filepath to open if no controler defined (default: False). def __init__(self, parent, controler = None, fileOpen = None, debug = False): + # Variable indicating that PLCOpenEditor was opened with a defined controler self.ModeSolo = controler == None self.Debug = debug if self.ModeSolo: + # If no controler defined, create a new one self.Controler = PLCControler() + # Open the filepath if defined if fileOpen is not None: self.Controler.OpenXMLFile(fileOpen) else: @@ -591,16 +655,18 @@ self._init_ctrls(parent) - self.SetIcon(wx.Icon(os.path.join(CWD,"Images","poe.ico"),wx.BITMAP_TYPE_ICO)) - - self.TypesTreeImageList = wx.ImageList(16, 16) - self.InstancesTreeImageList = wx.ImageList(16, 16) - self.TypesTreeImageDict = {} - self.InstancesTreeImageDict = {} + # Define PLCOpenEditor icon + self.SetIcon(wx.Icon(os.path.join(CWD,"Images", "poe.ico"),wx.BITMAP_TYPE_ICO)) + + # Define Tree item icon list + self.TreeImageList = wx.ImageList(16, 16) + self.TreeImageDict = {} + + # Icons for languages for language in LANGUAGES: - self.TypesTreeImageDict[language]=self.TypesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language))) - self.InstancesTreeImageDict[language]=self.InstancesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language))) - + self.TreeImageDict[language]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language))) + + # Icons for other items for imgname, itemtype in [ #editables ("PROJECT", ITEM_PROJECT), @@ -628,10 +694,11 @@ ("CONFIGURATIONS", ITEM_CONFIGURATIONS), ("RESOURCES", ITEM_RESOURCES), ("PROPERTIES", ITEM_PROPERTIES)]: - self.TypesTreeImageDict[itemtype]=self.TypesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname))) - self.InstancesTreeImageDict[itemtype]=self.InstancesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname))) - self.TypesTree.AssignImageList(self.TypesTreeImageList) - self.InstancesTree.AssignImageList(self.InstancesTreeImageList) + self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname))) + + # Assign icon list to TreeCtrls + self.TypesTree.SetImageList(self.TreeImageList) + self.InstancesTree.SetImageList(self.TreeImageList) self.CurrentToolBar = [] self.CurrentLanguage = "" @@ -642,6 +709,7 @@ if USE_AUI: self.AuiTabCtrl = [] + # Initialize Printing configuring elements self.PrintData = wx.PrintData() self.PrintData.SetPaperId(wx.PAPER_A4) self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER) @@ -649,41 +717,41 @@ self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15)) self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20)) + # Refresh elements that need to if not self.ModeSolo or fileOpen is not None: - self.RefreshTypesTree() - self.RefreshInstancesTree() - self.RefreshLibraryTree() - - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshDisplayMenu() - self.RefreshTitle() - self.RefreshToolBar() - - def ResetSelectedItem(self): - self.SelectedItem = None - + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + + +#------------------------------------------------------------------------------- +# General Functions +#------------------------------------------------------------------------------- + + ## Call PLCOpenEditor refresh functions. + # @param elements List of elements to refresh. + def _Refresh(self, *elements): + for element in elements: + self.RefreshFunctions[element](self) + + ## Callback function when AUINotebook Page closed with CloseButton + # @param event AUINotebook Event. def OnPageClose(self, event): + # Get Selected Tab selected = event.GetSelection() if selected >= 0: + # Remove corresponding VariablePanel if not self.Debug: tagname = self.TabsOpened.GetPage(selected).GetTagName() self.VariablePanelIndexer.RemoveVariablePanel(tagname) + # Refresh Tab selection if self.TabsOpened.GetPageCount() > 0: new_index = min(selected, self.TabsOpened.GetPageCount() - 1) self.TabsOpened.SetSelection(new_index) if not self.Debug: tagname = self.TabsOpened.GetPage(new_index).GetTagName() self.VariablePanelIndexer.ChangeVariablePanel(tagname) - self.RefreshTitle() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshDisplayMenu() - self.RefreshToolBar() - wx.CallAfter(self.RefreshTabCtrlEvent) - event.Skip() - - def OnPageDragged(self, event): + # Refresh all window elements that have changed + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) wx.CallAfter(self.RefreshTabCtrlEvent) event.Skip() @@ -731,12 +799,8 @@ new_values["creationDateTime"] = old_values["creationDateTime"] if new_values != old_values: self.Controler.SetProjectProperties(None, new_values) - self.RefreshTitle() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshDisplayMenu() - self.RefreshTypesTree() - self.RefreshScaling() + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, + TYPESTREE, INSTANCESTREE, SCALING) dialog.Destroy() def OnCloseFrame(self, event): @@ -744,83 +808,106 @@ self.AUIManager.UnInit() self._onclose() event.Skip() - elif not self.Controler.ProjectIsSaved(): - dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.SaveProject() - event.Skip() - elif answer == wx.ID_NO: - self.Controler.Reset() - self.AUIManager.UnInit() - event.Skip() + elif self.CheckSaveBeforeClosing(): + event.Skip() + else: + event.Veto() + +#------------------------------------------------------------------------------- +# Notebook Unified Functions +#------------------------------------------------------------------------------- + + ## Function that generate bitmap for + # for wx.aui.AUINotebook. + # @param window Panel to display in tab. + # @param text title for the tab ctrl. + def GenerateBitmap(self, icon1_name, icon2_name = None): + # Find index of bitmap if already created + index = self.TabsImageListIndexes.get((icon1_name, icon2_name), None) + # Return index or bitmap if found + if index is not None: + if USE_AUI: + return self.TabsImageList.GetBitmap(index) else: - event.Veto() - else: - event.Skip() - -#------------------------------------------------------------------------------- -# Notebook Unified Functions -#------------------------------------------------------------------------------- - - def GenerateBitmap(self, icon1_name, icon2_name = None): - if not USE_AUI: - index = self.TabsImageListIndexes.get((icon1_name, icon2_name), None) - if index is not None: return index if icon2_name is None: + # Bitmap with only one icon tmp_bitmap = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name)) else: + # Bitmap with two icon icon1 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name)) icon2 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon2_name)) + # Calculate bitmap size width = icon1.GetWidth() + icon2.GetWidth() - 1 height = max(icon1.GetHeight(), icon2.GetHeight()) + # Create bitmap with both icons tmp_bitmap = wx.EmptyBitmap(width, height) dc = wx.MemoryDC() dc.SelectObject(tmp_bitmap) dc.Clear() dc.DrawBitmap(icon1, 0, 0) dc.DrawBitmap(icon2, icon1.GetWidth() - 1, 0) + # Store bitmap in ImageList + index = self.TabsImageList.Add(tmp_bitmap) + # Save bitmap index in ImageList in dictionary + self.TabsImageListIndexes[(icon1_name, icon2_name)] = index if USE_AUI: return tmp_bitmap else: - index = self.TabsImageList.Add(tmp_bitmap) - self.TabsImageListIndexes[(icon1_name, icon2_name)] = index return index + ## Function that add a tab in Notebook, calling refresh for tab DClick event + # for wx.aui.AUINotebook. + # @param window Panel to display in tab. + # @param text title for the tab ctrl. def AddPage(self, window, text): self.TabsOpened.AddPage(window, text) self.RefreshTabCtrlEvent() - def RefreshTabCtrlEvent(self): - if USE_AUI: - auitabctrl = [] - for child in self.TabsOpened.GetChildren(): - if isinstance(child, wx.aui.AuiTabCtrl): - auitabctrl.append(child) - if child not in self.AuiTabCtrl: - child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child)) - self.AuiTabCtrl = auitabctrl - if self.TabsOpened.GetPageCount() == 0: - pane = self.AUIManager.GetPane(self.TabsOpened) - if pane.IsMaximized(): - self.AUIManager.RestorePane(pane) - self.AUIManager.Update() - + ## Function that fix difference in deleting all tabs between + # wx.Notebook and wx.aui.AUINotebook. def DeleteAllPages(self): if USE_AUI: for idx in xrange(self.TabsOpened.GetPageCount()): self.TabsOpened.DeletePage(0) else: self.TabsOpened.DeleteAllPages() - + self.RefreshTabCtrlEvent() + + ## Function that fix difference in setting picture on tab between + # wx.Notebook and wx.aui.AUINotebook. + # @param idx Tab index. + # @param bitmap wx.Bitmap to define on tab. + # @return True if operation succeeded def SetPageBitmap(self, idx, bitmap): if USE_AUI: return self.TabsOpened.SetPageBitmap(idx, bitmap) else: return self.TabsOpened.SetPageImage(idx, bitmap) +#------------------------------------------------------------------------------- +# Dialog Message Functions +#------------------------------------------------------------------------------- + + ## Function displaying an Error dialog in PLCOpenEditor. + # @param message The message to display. + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, "Error", wx.OK|wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + + ## Function displaying an Error dialog in PLCOpenEditor. + # @return False if closing cancelled. + def CheckSaveBeforeClosing(self): + if not self.Controler.ProjectIsSaved(): + dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + answer = dialog.ShowModal() + dialog.Destroy() + if answer == wx.ID_YES: + self.SaveProject() + elif answer == wx.ID_CANCEL: + return False + return True #------------------------------------------------------------------------------- # File Menu Functions @@ -871,23 +958,13 @@ if dialog.ShowModal() == wx.ID_OK: properties = dialog.GetValues() self.Controler.CreateNewProject(properties) - self.RefreshTitle() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshTypesTree() - self.RefreshInstancesTree() - self.RefreshLibraryTree() + self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE, + LIBRARYTREE) event.Skip() def OnOpenProjectMenu(self, event): - if not self.Controler.ProjectIsSaved(): - dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.SaveProject() - elif answer == wx.ID_CANCEL: - return + if not self.CheckSaveBeforeClosing(): + return filepath = self.Controler.GetFilePath() if filepath != "": directory = os.path.dirname(filepath) @@ -900,13 +977,8 @@ self.DeleteAllPages() self.VariablePanelIndexer.RemoveAllPanels() self.Controler.OpenXMLFile(filepath) - self.RefreshTypesTree() - self.RefreshInstancesTree() - self.RefreshLibraryTree() - self.RefreshTitle() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshToolBar() + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) dialog.Destroy() event.Skip() @@ -923,29 +995,18 @@ if not self.Debug: tagname = self.TabsOpened.GetPage(new_index).GetTagName() self.VariablePanelIndexer.ChangeVariablePanel(tagname) - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshToolBar() + self._Refresh(TOOLBAR, FILEMENU, EDITMENU) event.Skip() def OnCloseProjectMenu(self, event): - if not self.Controler.ProjectIsSaved(): - dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.SaveProject() - elif answer == wx.ID_CANCEL: - return + if not self.CheckSaveBeforeClosing(): + return self.DeleteAllPages() self.VariablePanelIndexer.RemoveAllPanels() self.TypesTree.DeleteAllItems() self.InstancesTree.DeleteAllItems() self.Controler.Reset() - self.RefreshTitle() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshToolBar() + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) event.Skip() def OnSaveProjectMenu(self, event): @@ -1002,13 +1063,9 @@ if os.path.isdir(os.path.dirname(filepath)): result = self.Controler.SaveXMLFile(filepath) if not result: - message = wx.MessageDialog(self, "Can't save project to file %s!"%filepath, "Error", wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() + self.ShowErrorMessage("Can't save project to file %s!"%filepath) else: - message = wx.MessageDialog(self, "%s is not a valid folder!"%os.path.dirname(filepath), "Error", wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() + self.ShowErrorMessage("\"%s\" is not a valid folder!"%os.path.dirname(filepath)) self.RefreshTitle() dialog.Destroy() @@ -1051,7 +1108,7 @@ printout = GraphicPrintout(self.TabsOpened.GetPage(selected), page_size, margins) if not printer.Print(self, printout, True): - wx.MessageBox("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", wx.OK) + self.ShowErrorMessage("There was a problem printing.\nPerhaps your current printer is not set correctly?") printout.Destroy() event.Skip() @@ -1101,8 +1158,7 @@ self.EditMenu.Enable(wx.ID_ADD, False) self.EditMenu.Enable(wx.ID_DELETE, False) - def OnUndoMenu(self, event): - self.Controler.LoadPrevious() + def CloseTabsWithoutModel(self): idxs = range(self.TabsOpened.GetPageCount()) idxs.reverse() for idx in idxs: @@ -1110,70 +1166,32 @@ if self.Controler.GetEditedElement(tagname, self.Debug) is None: self.VariablePanelIndexer.RemoveVariablePanel(tagname) self.TabsOpened.DeletePage(idx) - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.RefreshView() - self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName()) - self.RefreshTitle() - self.RefreshEditMenu() - self.RefreshTypesTree() - self.RefreshInstancesTree() - self.RefreshLibraryTree() - self.RefreshScaling() + + def OnUndoMenu(self, event): + self.Controler.LoadPrevious() + self.CloseTabsWithoutModel() + self.RefreshEditor() + self._Refresh(TITLE, EDITMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, + SCALING) event.Skip() def OnRedoMenu(self, event): self.Controler.LoadNext() - idxs = range(self.TabsOpened.GetPageCount()) - idxs.reverse() - for idx in idxs: - tagname = self.TabsOpened.GetPage(idx).GetTagName() - if self.Controler.GetEditedElement(tagname, self.Debug) is None: - self.VariablePanelIndexer.RemoveVariablePanel(tagname) - self.TabsOpened.DeletePage(idx) - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.RefreshView() - self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName()) - self.RefreshTitle() - self.RefreshEditMenu() - self.RefreshTypesTree() - self.RefreshInstancesTree() - self.RefreshLibraryTree() - self.RefreshScaling() - event.Skip() - + self.CloseTabsWithoutModel() + self.RefreshEditor() + self._Refresh(TITLE, EDITMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, + SCALING) + event.Skip() + def OnEnableUndoRedoMenu(self, event): self.Controler.EnableProjectBuffer(event.IsChecked()) self.RefreshEditMenu() event.Skip() - def OnCutMenu(self, event): - control = self.FindFocus() - if isinstance(control, (Viewer, TextViewer)): - control.Cut() - elif isinstance(control, wx.TextCtrl): - control.ProcessEvent(event) - event.Skip() - - def OnCopyMenu(self, event): - control = self.FindFocus() - if isinstance(control, (Viewer, TextViewer)): - control.Copy() - elif isinstance(control, wx.TextCtrl): - control.ProcessEvent(event) - event.Skip() - - def OnPasteMenu(self, event): - control = self.FindFocus() - if isinstance(control, (Viewer, TextViewer)): - control.Paste() - elif isinstance(control, wx.TextCtrl): - control.ProcessEvent(event) - event.Skip() - + OnCutMenu = GetShortcutKeyCallbackFunction("Cut") + OnCopyMenu = GetShortcutKeyCallbackFunction("Copy") + OnPasteMenu = GetShortcutKeyCallbackFunction("Paste") + def OnSelectAllMenu(self, event): control = self.FindFocus() if isinstance(control, (Viewer, TextViewer)): @@ -1184,70 +1202,27 @@ control.SetMark(0, control.GetLastPosition() + 1) event.Skip() + DeleteFunctions = { + ITEM_DATATYPE: GetDeleteElementFunction(PLCControler.ProjectRemoveDataType, check_function=PLCControler.DataTypeIsUsed), + ITEM_POU: GetDeleteElementFunction(PLCControler.ProjectRemovePou, check_function=PLCControler.PouIsUsed), + ITEM_TRANSITION: GetDeleteElementFunction(PLCControler.ProjectRemovePouTransition, ITEM_POU), + ITEM_ACTION: GetDeleteElementFunction(PLCControler.ProjectRemovePouAction, ITEM_POU), + ITEM_CONFIGURATION: GetDeleteElementFunction(PLCControler.ProjectRemoveConfiguration), + ITEM_RESOURCE: GetDeleteElementFunction(PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION) + } + def OnDeleteMenu(self, event): window = self.FindFocus() - if window == self.TypesTree: + if window == self.TypesTree or window is None: selected = self.TypesTree.GetSelection() if selected.IsOk(): type = self.TypesTree.GetPyData(selected) - tagname = "" - if type == ITEM_DATATYPE: - name = self.TypesTree.GetItemText(selected) - if not self.Controler.DataTypeIsUsed(name, self.Debug): - self.Controler.ProjectRemoveDataType(name) - tagname = self.Controler.ComputeDataTypeName(name) - else: - message = wx.MessageDialog(self, "\"%s\" is used by one or more POUs. It can't be removed!"%name, "Error", wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - elif type == ITEM_POU: - name = self.TypesTree.GetItemText(selected) - if not self.Controler.PouIsUsed(name, self.Debug): - self.Controler.ProjectRemovePou(name) - tagname = self.Controler.ComputePouName(name) - else: - message = wx.MessageDialog(self, "\"%s\" is used by one or more POUs. It can't be removed!"%name, "Error", wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - elif type in [ITEM_TRANSITION, ITEM_ACTION]: - item = self.TypesTree.GetItemParent(selected) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_POU: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - pou_name = self.TypesTree.GetItemText(item) - if type == ITEM_TRANSITION: - transition = self.TypesTree.GetItemText(selected) - self.Controler.ProjectRemovePouTransition(pou_name, transition) - tagname = self.Controler.ComputePouTransitionName(pou_name, transition) - elif type == ITEM_ACTION: - action = self.TypesTree.GetItemText(selected) - self.Controler.ProjectRemovePouAction(pou_name, action) - tagname = self.Controler.ComputePouActionName(pou_name, action) - elif type == ITEM_CONFIGURATION: - name = self.TypesTree.GetItemText(selected) - self.Controler.ProjectRemoveConfiguration(name) - tagname = self.Controler.ComputeConfigurationName(name) - elif type == ITEM_RESOURCE: - resource = self.TypesTree.GetItemText(selected) - item = self.TypesTree.GetItemParent(selected) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_CONFIGURATION: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - config_name = self.TypesTree.GetItemText(item) - self.Controler.ProjectRemoveConfigurationResource(config_name, resource) - tagname = self.Controler.ComputeConfigurationResourceName(config_name, selected) - 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() - self.RefreshLibraryTree() - self.RefreshToolBar() + function = self.DeleteFunctions.get(type, None) + if function is not None: + function(self, selected) + self.CloseTabsWithoutModel() + self._Refresh(TITLE, TOOLBAR, EDITMENU, TYPESTREE, + INSTANCESTREE, LIBRARYTREE) elif isinstance(window, (Viewer, TextViewer)): event = wx.KeyEvent(wx.EVT_CHAR._getEvtType()) event.m_keyCode = wx.WXK_DELETE @@ -1287,12 +1262,7 @@ self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) def OnRefreshMenu(self, event): - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.RefreshView() - if not self.Debug: - self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName()) + self.RefreshEditor(not self.Debug) event.Skip() def OnClearErrorsMenu(self, event): @@ -1315,6 +1285,25 @@ # Project Editor Panels Management Functions #------------------------------------------------------------------------------- + def OnPageDragged(self, event): + wx.CallAfter(self.RefreshTabCtrlEvent) + event.Skip() + + def RefreshTabCtrlEvent(self): + if USE_AUI: + auitabctrl = [] + for child in self.TabsOpened.GetChildren(): + if isinstance(child, wx.aui.AuiTabCtrl): + auitabctrl.append(child) + if child not in self.AuiTabCtrl: + child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child)) + self.AuiTabCtrl = auitabctrl + if self.TabsOpened.GetPageCount() == 0: + pane = self.AUIManager.GetPane(self.TabsOpened) + if pane.IsMaximized(): + self.AUIManager.RestorePane(pane) + self.AUIManager.Update() + def OnPouSelectedChanged(self, event): old_selected = self.TabsOpened.GetSelection() if old_selected >= 0: @@ -1332,10 +1321,7 @@ window.RefreshView() if not self.Debug: self.VariablePanelIndexer.ChangeVariablePanel(window.GetTagName()) - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshDisplayMenu() - self.RefreshToolBar() + self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, TOOLBAR) event.Skip() def RefreshEditor(self, variablepanel = True): @@ -1412,6 +1398,9 @@ self.GenerateTypesTreeBranch(root, infos) self.TypesTree.Expand(root) + def ResetSelectedItem(self): + self.SelectedItem = None + def GenerateTypesTreeBranch(self, root, infos, topology=False): to_delete = [] self.TypesTree.SetItemText(root, infos["name"]) @@ -1423,9 +1412,9 @@ self.TypesTree.SetItemBackgroundColour(root, wx.WHITE) self.TypesTree.SetItemTextColour(root, wx.BLACK) if infos["type"] == ITEM_POU: - self.TypesTree.SetItemImage(root, self.TypesTreeImageDict[self.Controler.GetPouBodyType(infos["name"], self.Debug)]) - else: - self.TypesTree.SetItemImage(root, self.TypesTreeImageDict[infos["type"]]) + self.TypesTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"], self.Debug)]) + else: + self.TypesTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) if wx.VERSION >= (2, 6, 0): item, root_cookie = self.TypesTree.GetFirstChild(root) @@ -1543,12 +1532,7 @@ self.RefreshLibraryTree() self.RefreshPageTitles() elif itemtype == ITEM_TRANSITION: - parent = self.TypesTree.GetItemParent(item) - parent_type = self.TypesTree.GetPyData(parent) - while parent_type != ITEM_POU: - parent = self.TypesTree.GetItemParent(parent) - parent_type = self.TypesTree.GetPyData(parent) - pou_name = self.TypesTree.GetItemText(parent) + pou_name = GetParentName(self.TypesTree, item, ITEM_POU) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: message = "A pou with \"%s\" as name exists!"%new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name, self.Debug) if name != old_name]: @@ -1559,12 +1543,7 @@ self.Controler.ComputePouTransitionName(pou_name, new_name)) self.RefreshPageTitles() elif itemtype == ITEM_ACTION: - parent = self.TypesTree.GetItemParent(item) - parent_type = self.TypesTree.GetPyData(parent) - while parent_type != ITEM_POU: - parent = self.TypesTree.GetItemParent(parent) - parent_type = self.TypesTree.GetPyData(parent) - pou_name = self.TypesTree.GetItemText(parent) + pou_name = GetParentName(self.TypesTree, item, ITEM_POU) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: message = "A pou with \"%s\" as name exists!"%new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name, self.Debug) if name != old_name]: @@ -1594,12 +1573,7 @@ self.Controler.ComputeConfigurationName(new_name)) self.RefreshPageTitles() elif itemtype == ITEM_RESOURCE: - parent = self.TypesTree.GetItemParent(item) - parent_type = self.TypesTree.GetPyData(parent) - while parent_type != ITEM_CONFIGURATION: - parent = self.TypesTree.GetItemParent(parent) - parent_type = self.TypesTree.GetPyData(parent) - config_name = self.TypesTree.GetItemText(parent) + config_name = GetParentName(self.TypesTree, item, ITEM_CONFIGURATION) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames(self.Debug)]: message = "\"%s\" config already exists!"%new_name abort = True @@ -1620,18 +1594,14 @@ self.RefreshPageTitles() if message or abort: if message: - messageDialog = wx.MessageDialog(self, message, "Error", wx.OK|wx.ICON_ERROR) - messageDialog.ShowModal() - messageDialog.Destroy() + self.ShowErrorMessage(message) item = event.GetItem() wx.CallAfter(self.TypesTree.EditLabel, item) event.Veto() else: wx.CallAfter(self.RefreshTypesTree) self.RefreshEditor() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshTitle() + self._Refresh(TITLE, FILEMENU, EDITMENU) event.Skip() def OnTypesTreeItemActivated(self, event): @@ -1647,20 +1617,10 @@ elif data == ITEM_CONFIGURATION: self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name)) elif data == ITEM_RESOURCE: - item = self.TypesTree.GetItemParent(selected) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_CONFIGURATION: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - config_name = self.TypesTree.GetItemText(item) + config_name = GetParentName(self.TypesTree, selected, ITEM_CONFIGURATION) self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name)) elif data in [ITEM_TRANSITION, ITEM_ACTION]: - item = self.TypesTree.GetItemParent(selected) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_POU: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - pou_name = self.TypesTree.GetItemText(item) + pou_name = GetParentName(self.TypesTree, selected, ITEM_POU) if data == ITEM_TRANSITION: tagname = self.Controler.ComputePouTransitionName(pou_name, name) elif data == ITEM_ACTION: @@ -1678,20 +1638,10 @@ elif data == ITEM_CONFIGURATION: self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name), True) elif data == ITEM_RESOURCE: - item = self.TypesTree.GetItemParent(select_item) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_CONFIGURATION: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - config_name = self.TypesTree.GetItemText(item) + config_name = GetParentName(self.TypesTree, select_item, ITEM_CONFIGURATION) self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name), True) elif data in [ITEM_TRANSITION, ITEM_ACTION]: - item = self.TypesTree.GetItemParent(select_item) - item_type = self.TypesTree.GetPyData(item) - while item_type != ITEM_POU: - item = self.TypesTree.GetItemParent(item) - item_type = self.TypesTree.GetPyData(item) - pou_name = self.TypesTree.GetItemText(item) + pou_name = GetParentName(self.TypesTree, select_item, ITEM_POU) if data == ITEM_TRANSITION: tagname = self.Controler.ComputePouTransitionName(pou_name, name) elif data == ITEM_ACTION: @@ -1724,12 +1674,9 @@ if old_selected >= 0: self.TabsOpened.GetPage(old_selected).ResetBuffer() self.TabsOpened.SetSelection(openedidx) - self.TabsOpened.GetPage(openedidx).RefreshView() self.VariablePanelIndexer.ChangeVariablePanel(tagname) self.RefreshPageTitles() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshToolBar() + self._Refresh(FILEMENU, EDITMENU, TOOLBAR) elif not onlyopened: if elementtype == ITEM_CONFIGURATION: new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler) @@ -1775,9 +1722,7 @@ self.TabsOpened.SetSelection(i) window.SetFocus() self.RefreshPageTitles() - self.RefreshFileMenu() - self.RefreshEditMenu() - self.RefreshToolBar() + self._Refresh(FILEMENU, EDITMENU, TOOLBAR) def OnTypesTreeRightUp(self, event): if wx.Platform == '__WXMSW__': @@ -1815,7 +1760,7 @@ self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id) new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete") - self.Bind(wx.EVT_MENU, self.OnRemovePouMenu, id=new_id) + self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id) self.PopupMenu(menu) elif type == ITEM_CONFIGURATION: menu = wx.Menu(title='') @@ -1824,20 +1769,13 @@ self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id) new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete") - self.Bind(wx.EVT_MENU, self.OnRemoveConfigurationMenu, id=new_id) + self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id) self.PopupMenu(menu) elif type in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]: menu = wx.Menu(title='') new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete") - if type == ITEM_DATATYPE: - self.Bind(wx.EVT_MENU, self.OnRemoveDataTypeMenu, id=new_id) - elif type == ITEM_TRANSITION: - self.Bind(wx.EVT_MENU, self.OnRemoveTransitionMenu, id=new_id) - elif type == ITEM_ACTION: - self.Bind(wx.EVT_MENU, self.OnRemoveActionMenu, id=new_id) - elif type == ITEM_RESOURCE: - self.Bind(wx.EVT_MENU, self.OnRemoveResourceMenu, id=new_id) + self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id) self.PopupMenu(menu) elif type in ITEMS_UNEDITABLE: if name == "Data Types": @@ -1913,7 +1851,7 @@ else: self.InstancesTree.SetItemText(root, infos["name"]) self.InstancesTree.SetPyData(root, (infos["type"], infos.get("tagname", None))) - self.InstancesTree.SetItemImage(root, self.InstancesTreeImageDict[infos["type"]]) + self.InstancesTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) if wx.VERSION >= (2, 6, 0): item, root_cookie = self.InstancesTree.GetFirstChild(root) @@ -2642,7 +2580,18 @@ for i in xrange(self.TabsOpened.GetPageCount()): viewer = self.TabsOpened.GetPage(i) viewer.ClearErrors() - + + RefreshFunctions = { + TITLE : RefreshTitle, + TOOLBAR : RefreshToolBar, + FILEMENU : RefreshFileMenu, + EDITMENU : RefreshEditMenu, + DISPLAYMENU : RefreshDisplayMenu, + TYPESTREE : RefreshTypesTree, + INSTANCESTREE : RefreshInstancesTree, + LIBRARYTREE : RefreshLibraryTree, + SCALING : RefreshScaling} + current_num = 0 def GetNewNum(): global current_num @@ -4543,7 +4492,7 @@ self.Table.ResetView(self.VariablesGrid) #------------------------------------------------------------------------------- -# Variables Editor Panel +# Debug Variables Panel #------------------------------------------------------------------------------- class VariableTableItem(DebugDataConsumer):