PluginToolGenerateHash.py

Sat, 09 Jul 2016 12:52:19 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 09 Jul 2016 12:52:19 +0200
changeset 35
3ae4a3373978
parent 34
37088185a328
child 37
1a388aaa269a
permissions
-rw-r--r--

Updated Russian translations provided by Alexander Barkoff.

# -*- coding: utf-8 -*-

# Copyright (c) 2013 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the 'Generate Hash' tool plug-in.
"""

from __future__ import unicode_literals

import os
import hashlib

from PyQt5.QtCore import QObject, QTranslator
from PyQt5.QtWidgets import QMenu

from E5Gui.E5Application import e5App
from E5Gui import E5FileDialog, E5MessageBox

# Start-Of-Header
name = "Generate Hash Tool Plug-in"
author = "Detlev Offenbach <detlev@die-offenbachs.de>"
autoactivate = True
deactivateable = True
version = "2.1.1"
className = "ToolGenerateHashPlugin"
packageName = "ToolGenerateHash"
shortDescription = "Generate a hash for a selectable file or directory"
longDescription = \
    """Plug-in to generate a hash for a selectable file or directory. The"""\
    """ hash string will be inserted at the cursor position of the current"""\
    """ editor. The menu will be disabled, if no editor is open."""
needsRestart = False
pyqtApi = 2
python2Compatible = True
# End-Of-Header

error = ""


class ToolGenerateHashPlugin(QObject):
    """
    Class implementing the 'Generate Hash' tool plug-in.
    """
    Hashes = {
        "MD5": hashlib.md5,
        "SHA1": hashlib.sha1,
        "SHA224": hashlib.sha224,
        "SHA256": hashlib.sha256,
        "SHA384": hashlib.sha384,
        "SHA512": hashlib.sha512,
    }
    
    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.__initMenus()
        
        self.__editors = {}
        self.__mainActions = []
    
    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)
        
        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.__fileMenu)
            self.__mainActions.append(act)
            act = menu.addMenu(self.__dirMenu)
            self.__mainActions.append(act)
        
        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)
        
        menu = self.__ui.getMenu("plugin_tools")
        if menu is not None:
            for act in self.__mainActions:
                menu.removeAction(act)
        self.__mainActions = []

        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__), "ToolGenerateHash", "i18n")
                translation = "generatehash_{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 __initMenus(self):
        """
        Private method to initialize the hash generation menus.
        """
        self.__fileMenu = QMenu(self.tr("Generate File Hash"))
        self.__fileMenu.addAction("MD5", self.__hashFile).setData("MD5")
        self.__fileMenu.addAction("SHA1", self.__hashFile).setData("SHA1")
        self.__fileMenu.addAction("SHA224", self.__hashFile).setData("SHA224")
        self.__fileMenu.addAction("SHA256", self.__hashFile).setData("SHA256")
        self.__fileMenu.addAction("SHA384", self.__hashFile).setData("SHA384")
        self.__fileMenu.addAction("SHA512", self.__hashFile).setData("SHA512")
        self.__fileMenu.setEnabled(False)
        
        self.__dirMenu = QMenu(self.tr("Generate Directory Hash"))
        self.__dirMenu.addAction(
            "MD5", self.__hashDirectory).setData("MD5")
        self.__dirMenu.addAction(
            "SHA1", self.__hashDirectory).setData("SHA1")
        self.__dirMenu.addAction(
            "SHA224", self.__hashDirectory).setData("SHA224")
        self.__dirMenu.addAction(
            "SHA256", self.__hashDirectory).setData("SHA256")
        self.__dirMenu.addAction(
            "SHA384", self.__hashDirectory).setData("SHA384")
        self.__dirMenu.addAction(
            "SHA512", self.__hashDirectory).setData("SHA512")
        self.__dirMenu.setEnabled(False)
    
    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 not in ["Tools", "PluginTools"]:
            return
        
        editor = e5App().getObject("ViewManager").activeWindow()
        
        if name == "Tools":
            if not menu.isEmpty():
                menu.addSeparator()
            
            act = menu.addMenu(self.__fileMenu)
            act.setEnabled(editor is not None)
            act = menu.addMenu(self.__dirMenu)
            act.setEnabled(editor is not None)
        elif name == "PluginTools" and self.__mainActions:
            self.__mainActions[-2].setEnabled(editor is not None)
            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 (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.__fileMenu)
            self.__editors[editor].append(act)
            act = menu.addMenu(self.__dirMenu)
            self.__editors[editor].append(act)
            editor.showMenu.connect(self.__editorShowMenu)
            
            self.__fileMenu.setEnabled(True)
            self.__dirMenu.setEnabled(True)
    
    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]
            if not self.__editors:
                self.__fileMenu.setEnabled(False)
                self.__dirMenu.setEnabled(False)
        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":
            if self.__fileMenu.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.__fileMenu)
                self.__editors[editor].append(act)
                act = menu.addMenu(self.__dirMenu)
                self.__editors[editor].append(act)
                
                self.__fileMenu.setEnabled(True)
                self.__dirMenu.setEnabled(True)
    
    def __insertHash(self, hashStr):
        """
        Private method to insert the generated hash string.
        
        @param hashStr hash string (string)
        """
        if hashStr:
            editor = e5App().getObject("ViewManager").activeWindow()
            line, index = editor.getCursorPosition()
            # It should be done on this way to allow undo
            editor.beginUndoAction()
            editor.insertAt(hashStr, line, index)
            editor.endUndoAction()
    
    def __hashFile(self):
        """
        Private slot to generate the hash for a file.
        """
        act = self.sender()
        if act is None:
            return
        
        name = E5FileDialog.getOpenFileName(
            self.__ui,
            self.tr("Generate File Hash"))
        if name:
            try:
                f = open(name, "rb")
                hashStr = self.Hashes[act.data()](f.read()).hexdigest()
                f.close()
            except (IOError, OSError) as err:
                E5MessageBox.critical(
                    self.__ui,
                    self.tr("Generate File Hash"),
                    self.tr("""<p>The hash for <b>{0}</b> could not"""
                            """ be generated.</p><p>Reason: {1}</p>""")
                    .format(name, str(err))
                )
                return
            
            self.__insertHash(hashStr)
    
    def __hashDirectory(self):
        """
        Private slot to generate the hash for a directory.
        """
        act = self.sender()
        if act is None:
            return
        
        folder = E5FileDialog.getExistingDirectory(
            self.__ui,
            self.tr("Generate Directory Hash"),
            "",
            E5FileDialog.Options(E5FileDialog.Option(0)))
        if folder and os.path.isdir(folder):
            fails = 0
            hashes = []
            for name in os.listdir(folder):
                if not name.startswith(".") and \
                        os.path.isfile(os.path.join(folder, name)):
                    try:
                        f = open(os.path.join(folder, name), "rb")
                        hashStr = self.Hashes[act.data()](f.read()).hexdigest()
                        f.close()
                        hashes.append((name, hashStr))
                    except (IOError, OSError):
                        fails += 1
            if fails:
                E5MessageBox.critical(
                    self.__ui,
                    self.tr("Generate Directory Hash"),
                    self.tr("""<p>The hash for some files could not"""
                            """ be generated.</p>""")
                )
            else:
                editor = e5App().getObject("ViewManager").activeWindow()
                if (editor.getLanguage() in ['Python', 'Python2'] and
                        "from __future__ import unicode_literals"
                        not in editor.text()):
                    prefix = 'u'
                else:
                    prefix = ''
                
                line, index = editor.getCursorPosition()
                indLevel = (editor.indentation(line) //
                            editor.indentationWidth())
                if editor.indentationsUseTabs():
                    indString = '\t'
                else:
                    indString = editor.indentationWidth() * ' '
                indent = (indLevel + 1) * indString
                code = ["["]
                for name, hash in hashes:
                    code.append("{0}({1}'{2}', '{3}'),".format(
                        indent, prefix, name, hash))
                code.append("{0}]".format(indLevel * indString))
                
                self.__insertHash(os.linesep.join(code))

eric ide

mercurial