Fri, 28 May 2021 18:35:49 +0200
Ported the plug-in to PyQt6 for eric7.
# -*- coding: utf-8 -*- # Copyright (c) 2014 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the 'Color String' tool plug-in. """ import contextlib import os from PyQt6.QtCore import QObject, QTranslator from PyQt6.QtGui import QColor from PyQt6.QtWidgets import QColorDialog, QMenu, QDialog from EricWidgets.EricApplication import ericApp from EricWidgets import EricMessageBox # Start-Of-Header name = "Color String Plug-in" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = "1.0.0" className = "ColorStringPlugin" packageName = "ColorString" shortDescription = "Insert color as string" longDescription = ( """This plug-in implements a tool to select a color via a""" """ color selection dialog and insert it as a hex string at the""" """ current cursor position. Selected text is used to initialize""" """ the dialog and is replaced with the new color.""" ) needsRestart = False pyqtApi = 2 # End-Of-Header error = "" class ColorStringPlugin(QObject): """ Class implementing the 'Color String' tool plug-in. """ def __init__(self, ui): """ Constructor @param ui reference to the user interface object @type UserInterface """ super().__init__(ui) self.__ui = ui self.__translator = None self.__loadTranslator() self.__initMenu() self.__editors = {} self.__mainActions = [] def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status @rtype tuple of (None, bool) """ global error error = "" # clear previous error self.__ui.showMenu.connect(self.__populateMenu) menu = self.__ui.getMenu("plugin_tools") if menu is not None: if not menu.isEmpty(): act = menu.addSeparator() self.__mainActions.append(act) act = menu.addMenu(self.__menu) self.__mainActions.append(act) ericApp().getObject("ViewManager").editorOpenedEd.connect( self.__editorOpened) ericApp().getObject("ViewManager").editorClosedEd.connect( self.__editorClosed) for editor in ericApp().getObject("ViewManager").getOpenEditors(): self.__editorOpened(editor) return None, True def deactivate(self): """ Public method to deactivate this plugin. """ self.__ui.showMenu.disconnect(self.__populateMenu) menu = self.__ui.getMenu("plugin_tools") if menu is not None: for act in self.__mainActions: menu.removeAction(act) self.__mainActions = [] ericApp().getObject("ViewManager").editorOpenedEd.disconnect( self.__editorOpened) ericApp().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__), "ColorString", "i18n") translation = "colorstring_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator ericApp().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 menu. """ self.__menu = QMenu(self.tr("Color String")) self.__menu.addAction(self.tr("Hex Color"), self.__selectHexColor) self.__menu.addAction(self.tr("Color Name"), self.__selectColorName) self.__menu.addAction(self.tr("RGBA Color"), self.__selectRgbaColor) self.__menu.setEnabled(False) def __populateMenu(self, name, menu): """ Private slot to populate the tools menu with our entry. @param name name of the menu @type str @param menu reference to the menu to be populated @type QMenu """ if name not in ["Tools", "PluginTools"]: return editor = ericApp().getObject("ViewManager").activeWindow() if name == "Tools": if not menu.isEmpty(): menu.addSeparator() act = menu.addMenu(self.__menu) act.setEnabled(editor is not None) elif name == "PluginTools" and self.__mainActions: self.__mainActions[-1].setEnabled(editor is not None) def __editorOpened(self, editor): """ Private slot called, when a new editor was opened. @param editor reference to the new editor @type 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.__menu.setEnabled(True) 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 @type Editor """ with contextlib.suppress(KeyError): del self.__editors[editor] if not self.__editors: self.__menu.setEnabled(False) 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 @type str @param menu reference to the menu @type QMenu @param editor reference to the editor @type Editor """ if ( menuName == "Tools" and self.__menu.menuAction() not in menu.actions() ): # Re-add our menu 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) def __isHexString(self, text): """ Private method to check, if a given text is a hex string. @param text text to check @type str @return flag indicating a hex string @rtype bool """ return all(c in "0123456789abcdefABCDEF" for c in text) def __isValidColor(self, name): """ Private method to check for a valid color name. @param name color name to check @type str @return flag indicating a valid color name @rtype bool """ if self.__isHexString(name) and len(name) in (3, 6, 8, 9, 12): return True return QColor.isValidColor(name) def __selectHexColor(self): """ Private slot implementing the hex color string selection. """ editor = ericApp().getObject("ViewManager").activeWindow() if editor is None: return if editor.hasSelectedText(): currColor = editor.selectedText() if not self.__isValidColor(currColor): EricMessageBox.critical( self.__ui, self.tr("Color String"), self.tr( """<p>The selected string <b>{0}</b> is not a""" """ valid color string. Aborting!</p>""") .format(currColor)) return if currColor.startswith("#"): withHash = True elif self.__isHexString(currColor): withHash = False currColor = "#" + currColor else: withHash = True initColor = QColor(currColor) else: withHash = True currColor = "" initColor = QColor() color = QColorDialog.getColor( initColor, self.__ui, self.tr("Color String"), QColorDialog.ColorDialogOption.ShowAlphaChannel) if color.isValid(): if color.alpha() == 255: nameFormat = QColor.NameFormat.HexRgb else: nameFormat = QColor.NameFormat.HexArgb colorStr = color.name(nameFormat) if not withHash: colorStr = colorStr[1:] editor.beginUndoAction() if editor.hasSelectedText(): editor.replaceSelectedText(colorStr) else: line, index = editor.getCursorPosition() editor.insert(colorStr) editor.setCursorPosition(line, index + len(colorStr)) editor.endUndoAction() def __selectColorName(self): """ Private slot implementing the named color string selection. """ editor = ericApp().getObject("ViewManager").activeWindow() if editor is None: return if editor.hasSelectedText(): currColor = editor.selectedText() if currColor not in QColor.colorNames(): EricMessageBox.critical( self.__ui, self.tr("Color String"), self.tr( """<p>The selected string <b>{0}</b> is not a""" """ valid color name. Aborting!</p>""") .format(currColor)) return else: currColor = "" from ColorString.ColorSelectionDialog import ColorSelectionDialog dlg = ColorSelectionDialog(currColor, self.__ui) if dlg.exec() == QDialog.DialogCode.Accepted: colorStr = dlg.getColor() editor.beginUndoAction() if editor.hasSelectedText(): editor.replaceSelectedText(colorStr) else: line, index = editor.getCursorPosition() editor.insert(colorStr) editor.setCursorPosition(line, index + len(colorStr)) editor.endUndoAction() def __selectRgbaColor(self): """ Private slot implementing the RGBA color string selection. """ editor = ericApp().getObject("ViewManager").activeWindow() if editor is None: return if editor.hasSelectedText(): currColor = editor.selectedText() valid, rgba = self.__isValidRgbaColor(currColor) if not valid: EricMessageBox.critical( self.__ui, self.tr("Color String"), self.tr( """<p>The selected string <b>{0}</b> is not a""" """ valid RGBA color string. Aborting!</p>""") .format(currColor)) return initColor = QColor(*rgba) else: initColor = QColor() color = QColorDialog.getColor( initColor, self.__ui, self.tr("Color String"), QColorDialog.ColorDialogOption.ShowAlphaChannel) if color.isValid(): rgba = color.getRgb() if rgba[-1] == 255: colorStr = "{0}, {1}, {2}".format(*rgba[:-1]) else: colorStr = "{0}, {1}, {2}, {3}".format(*rgba) editor.beginUndoAction() if editor.hasSelectedText(): editor.replaceSelectedText(colorStr) else: line, index = editor.getCursorPosition() editor.insert(colorStr) editor.setCursorPosition(line, index + len(colorStr)) editor.endUndoAction() def __isValidRgbaColor(self, color): """ Private method to check for a valid RGBA color. @param color color string to check @type str @return flag indicating a valid RGBA color (boolean) and a list with the RGBA components of the color (three or four integers) @rtype tuple of (bool, [int, int, int]) or (bool, [int, int, int, int]) """ rgba = [] parts = color.split(",") if len(parts) not in [3, 4]: return False, [] for part in parts: try: c = int(part) except ValueError: return False, [] if c < 0 or c > 255: return False, [] rgba.append(c) return True, rgba # # eflag: noqa = M801