Wed, 01 Jan 2014 19:47:09 +0100
Made the code more robust.
# -*- coding: utf-8 -*- # Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the split, merge or convert camel case plug-in. """ import os import re from PyQt4.QtCore import QObject, QTranslator from PyQt4.QtGui import QMenu from E5Gui.E5Application import e5App # Start-Of-Header name = "Camel Case Handling Plug-in" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = "0.5.0" className = "SplitMergeCamelCasePlugin" packageName = "SplitMergeCamelCase" shortDescription = "Split, merge or convert camel case text" longDescription = \ """This plug-in implements a tool to split, merge or convert""" \ """ camel case text. It works with the text of the current""" \ """ editor. The menu entries will only be selectable, if the""" \ """ current editor has some selected text.""" needsRestart = False pyqtApi = 2 # End-Of-Header error = "" class SplitMergeCamelCasePlugin(QObject): """ Class implementing the split, merge or convert camel case plug-in. """ def __init__(self, ui): """ Constructor @param ui reference to the user interface object (UI.UserInterface) """ QObject.__init__(self, ui) self.__ui = ui self.__translator = None self.__loadTranslator() self.__initMenu() self.__editors = {} def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status (boolean) """ global error error = "" # clear previous error self.__ui.showMenu.connect(self.__populateMenu) e5App().getObject("ViewManager").editorOpenedEd.connect( self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.connect( self.__editorClosed) for editor in e5App().getObject("ViewManager").getOpenEditors(): self.__editorOpened(editor) return None, True def deactivate(self): """ Public method to deactivate this plugin. """ self.__ui.showMenu.disconnect(self.__populateMenu) e5App().getObject("ViewManager").editorOpenedEd.disconnect( self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.disconnect( self.__editorClosed) for editor, acts in self.__editors.items(): editor.showMenu.disconnect(self.__editorShowMenu) menu = editor.getMenu("Tools") if menu is not None: for act in acts: menu.removeAction(act) self.__editors = {} def __loadTranslator(self): """ Private method to load the translation file. """ if self.__ui is not None: loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = os.path.join( os.path.dirname(__file__), "SplitMergeCamelCase", "i18n") translation = "splitmergecamelcase_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator e5App().installTranslator(self.__translator) else: print("Warning: translation file '{0}' could not be" " loaded.".format(translation)) print("Using default.") def __initMenu(self): """ Private method to initialize the camel case handling menu. """ self.__menu = QMenu(self.tr("CamelCase Handling")) self.__menu.addAction(self.tr("Split from CamelCase"), self.__splitCamelCase) self.__menu.addAction(self.tr("Merge to CamelCase"), self.__mergeCamelCase) self.__menu.addAction(self.tr("CamelCase to under_score"), self.__camelCaseToUnderscore) self.__menu.addAction(self.tr("under_score to CamelCase"), self.__underscoreToCamelCase) self.__menu.addAction( self.tr("under_score to CamelCase (upper case first)"), self.__underscoreToCamelCaseUppercaseFirst) def __populateMenu(self, name, menu): """ Private slot to populate the tools menu with our entries. @param name name of the menu (string) @param menu reference to the menu to be populated (QMenu) """ if name != "Tools": return editor = e5App().getObject("ViewManager").activeWindow() if not menu.isEmpty(): menu.addSeparator() act = menu.addMenu(self.__menu) act.setEnabled(editor is not None and editor.selectedText() != '') def __editorOpened(self, editor): """ Private slot called, when a new editor was opened. @param editor reference to the new editor (QScintilla.Editor) """ menu = editor.getMenu("Tools") if menu is not None: self.__editors[editor] = [] if not menu.isEmpty(): act = menu.addSeparator() self.__editors[editor].append(act) act = menu.addMenu(self.__menu) self.__editors[editor].append(act) editor.showMenu.connect(self.__editorShowMenu) def __editorClosed(self, editor): """ Private slot called, when an editor was closed. @param editor reference to the editor (QScintilla.Editor) """ try: del self.__editors[editor] except KeyError: pass def __editorShowMenu(self, menuName, menu, editor): """ Private slot called, when the the editor context menu or a submenu is about to be shown. @param menuName name of the menu to be shown (string) @param menu reference to the menu (QMenu) @param editor reference to the editor """ if menuName == "Tools": self.__menu.setEnabled(editor.selectedText() != '') def __applyChange(self, newText, editor): """ Private method to change the selected text. @param newText new (converted) text (string) @param editor reference to the editor to apply the text change to (Editor) """ editor.beginUndoAction() editor.replaceSelectedText(newText) editor.endUndoAction() def __splitCamelCase(self): """ Private slot to split the selected camel case text. """ editor = e5App().getObject("ViewManager").activeWindow() if editor is None: return text = editor.selectedText() if text: newText = re.sub(r"([A-Z])", r" \1", text) if newText.startswith(" "): newText = newText[1:] if newText != text: self.__applyChange(newText, editor) def __mergeCamelCase(self): """ Private slot to merge the selected text to camel case. """ editor = e5App().getObject("ViewManager").activeWindow() if editor is None: return text = editor.selectedText() if text: newText = "".join(text.split()) if newText != text: self.__applyChange(newText, editor) def __camelCaseToUnderscore(self): """ Private slot to convert camel case text to underscore separated text. """ editor = e5App().getObject("ViewManager").activeWindow() if editor is None: return text = editor.selectedText() if text: newText = re.sub(r"([A-Z])", r"_\1", text).lower() if newText.startswith("_"): newText = newText[1:] if newText != text: self.__applyChange(newText, editor) def __underscoreToCamelCase(self, uppercaseFirst=False): """ Private slot to convert underscore separated text to camel case. @param uppercaseFirst flag indicating to upper case the first character (boolean) """ editor = e5App().getObject("ViewManager").activeWindow() if editor is None: return text = editor.selectedText() if text: newText = "".join([s.capitalize() for s in text.split("_")]) if not uppercaseFirst: newText = newText[0].lower() + newText[1:] if newText != text: self.__applyChange(newText, editor) def __underscoreToCamelCaseUppercaseFirst(self): """ Private slot to convert underscore separated text to camel case upper casing the first character. """ self.__underscoreToCamelCase(True)