Tue, 10 Dec 2024 15:49:01 +0100
Updated copyright for 2025.
# -*- coding: utf-8 -*- # Copyright (c) 2014 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the Web project plugin. """ import contextlib import os from PyQt6.QtCore import QObject, QTranslator from PyQt6.QtWidgets import QMenu from eric7 import Preferences from eric7.EricWidgets.EricApplication import ericApp try: from bs4 import BeautifulSoup # __IGNORE_EXCEPTION__ BeautifulSoupAvailable = True except ImportError: BeautifulSoup = None BeautifulSoupAvailable = False # Start-Of-Header __header__ = { "name": "Generic Web Project Plug-in", "author": "Detlev Offenbach <detlev@die-offenbachs.de>", "autoactivate": True, "deactivateable": True, "version": "10.2.2", "className": "ProjectWebPlugin", "packageName": "ProjectWeb", "shortDescription": "Support for Web projects and web related tools.", "longDescription": ( """This plug-in provides support for ordinary web projects and some web""" """ related tools.\n\nIt uses BeautifulSoup4 for some of its""" """ functionality.""" ), "needsRestart": False, "hasCompiledForms": True, "pyqtApi": 2, } # End-Of-Header error = "" class ProjectWebPlugin(QObject): """ Class implementing the Web project plugin. """ def __init__(self, ui): """ Constructor @param ui reference to the user interface object @type UserInterface """ super().__init__(ui) self.__ui = ui self.__initialize() self.__translator = None self.__loadTranslator() self.__initMenu() def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__ericProject = ericApp().getObject("Project") 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 # it is not registered for a specific programming language self.__ericProject.registerProjectType( "Web", self.tr("Web"), self.fileTypesCallback ) try: # backward compatibility for eric7 < 22.12 from eric7.Project.ProjectBrowser import ( # noqa: I101 FormsBrowserFlag, OthersBrowserFlag, SourcesBrowserFlag, ) Preferences.setProjectBrowserFlagsDefault( "Web", SourcesBrowserFlag | FormsBrowserFlag | OthersBrowserFlag, ) except ImportError: Preferences.setProjectBrowsersDefault( "Web", ("sources", "forms", "others"), ) 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.__ericProject.unregisterProjectType("Web") 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.__initialize() 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__), "ProjectWeb", "i18n" ) translation = "web_{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 fileTypesCallback(self): """ Public method to get the filetype associations of the Web project type. @return dictionary with file type associations @rtype dict """ if self.__ericProject.getProjectType() == "Web": return { "*.html": "FORMS", "*.htm": "FORMS", "*.js": "SOURCES", } else: return {} def __initMenu(self): """ Private method to initialize the web tools menu. """ self.__menu = QMenu(self.tr("Web")) self.__html5ToCss3Act = self.__menu.addAction( self.tr("HTML5 to CSS3"), self.__html5ToCss3 ) self.__html5ToJsAct = self.__menu.addAction( self.tr("HTML5 to JavaScript"), self.__html5ToJs ) self.__menu.addSeparator() self.__html5PrettifyAct = self.__menu.addAction( self.tr("Prettify HTML"), self.__html5Prettify ) self.__menu.aboutToShow.connect(self.__menuAboutToShow) def __menuAboutToShow(self): """ Private slot to prepare the menu before it is shown. """ editor = ericApp().getObject("ViewManager").activeWindow() selectionAvailable = bool(editor and editor.selectedText() != "") isHtml = bool(editor and editor.getLanguage().lower().startswith("html")) self.__html5ToCss3Act.setEnabled( selectionAvailable and BeautifulSoupAvailable and isHtml ) self.__html5ToJsAct.setEnabled( selectionAvailable and BeautifulSoupAvailable and isHtml ) self.__html5PrettifyAct.setEnabled(BeautifulSoupAvailable and isHtml) def __populateMenu(self, name, menu): """ Private slot to populate the tools menu with our entries. @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 if name == "Tools": if not menu.isEmpty(): menu.addSeparator() menu.addMenu(self.__menu) 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.__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] 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 __html5ToCss3(self): """ Private slot handling the HTML5 to CSS3 conversion. """ from ProjectWeb.Html5ToCss3Converter import Html5ToCss3Converter vm = ericApp().getObject("ViewManager") editor = vm.activeWindow() html = editor.selectedText() converter = Html5ToCss3Converter(html, parent=self.__ui) css3 = converter.getCss3() if css3: vm.newEditor() newEditor = vm.activeWindow() newEditor.setText(css3) newEditor.setLanguage("dummy.css") def __html5ToJs(self): """ Private slot handling the HTML5 to JavaScript conversion. """ from ProjectWeb.Html5ToJsConverter import Html5ToJsConverter vm = ericApp().getObject("ViewManager") editor = vm.activeWindow() html = editor.selectedText() converter = Html5ToJsConverter(html, parent=self.__ui) js = converter.getJavaScript() if js: vm.newEditor() newEditor = vm.activeWindow() newEditor.setText(js) newEditor.setLanguage("dummy.js") def __html5Prettify(self): """ Private slot handling the Prettify HTML action. """ from ProjectWeb.Html5Prettifier import Html5Prettifier editor = ericApp().getObject("ViewManager").activeWindow() html = editor.text() prettifier = Html5Prettifier(html) newHtml = prettifier.getPrettifiedHtml() if newHtml and newHtml != html: cursorLine, cursorIndex = editor.getCursorPosition() editor.beginUndoAction() editor.selectAll() editor.replaceSelectedText(newHtml) editor.endUndoAction() editor.setCursorPosition(cursorLine, cursorIndex) def installDependencies(pipInstall): """ Function to install dependencies of this plug-in. @param pipInstall function to be called with a list of package names. @type function """ try: import bs4 # __IGNORE_WARNING__ except ImportError: pipInstall(["beautifulsoup4"]) # # eflag: noqa = M801