1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing a class representing the shortcuts JSON file. 8""" 9 10import json 11import time 12import typing 13 14from PyQt5.QtCore import QObject 15 16from E5Gui import E5MessageBox 17from E5Gui.E5OverrideCursor import E5OverridenCursor 18from E5Gui.E5Application import e5App 19 20import Preferences 21 22HelpViewer = typing.TypeVar("WebBrowserWindow") 23 24 25class ShortcutsFile(QObject): 26 """ 27 Class representing the shortcuts JSON file. 28 """ 29 def __init__(self: "ShortcutsFile", parent: QObject = None) -> None: 30 """ 31 Constructor 32 33 @param parent reference to the parent object (defaults to None) 34 @type QObject (optional) 35 """ 36 super().__init__(parent) 37 38 def __addActionsToDict(self: "ShortcutsFile", category: str, actions: list, 39 actionsDict: dict) -> None: 40 """ 41 Private method to add a list of actions to the actions dictionary. 42 43 @param category category of the actions 44 @type str 45 @param actions list of actions 46 @type list of QAction 47 @param actionsDict reference to the actions dictionary to be modified 48 @type dict 49 """ 50 if actions: 51 if category not in actionsDict: 52 actionsDict[category] = {} 53 for act in actions: 54 if act.objectName(): 55 # shortcuts are only exported, if their objectName is set 56 actionsDict[category][act.objectName()] = ( 57 act.shortcut().toString(), 58 act.alternateShortcut().toString() 59 ) 60 61 def writeFile(self: "ShortcutsFile", filename: str, 62 helpViewer: HelpViewer = None) -> bool: 63 """ 64 Public method to write the shortcuts data to a shortcuts JSON file. 65 66 @param filename name of the shortcuts file 67 @type str 68 @param helpViewer reference to the help window object 69 @type WebBrowserWindow 70 @return flag indicating a successful write 71 @rtype bool 72 """ 73 actionsDict = {} 74 75 # step 1: collect all the shortcuts 76 if helpViewer is None: 77 self.__addActionsToDict( 78 "Project", 79 e5App().getObject("Project").getActions(), 80 actionsDict 81 ) 82 self.__addActionsToDict( 83 "General", 84 e5App().getObject("UserInterface").getActions('ui'), 85 actionsDict 86 ) 87 self.__addActionsToDict( 88 "Wizards", 89 e5App().getObject("UserInterface").getActions('wizards'), 90 actionsDict 91 ) 92 self.__addActionsToDict( 93 "Debug", 94 e5App().getObject("DebugUI").getActions(), 95 actionsDict 96 ) 97 self.__addActionsToDict( 98 "Edit", 99 e5App().getObject("ViewManager").getActions('edit'), 100 actionsDict 101 ) 102 self.__addActionsToDict( 103 "File", 104 e5App().getObject("ViewManager").getActions('file'), 105 actionsDict 106 ) 107 self.__addActionsToDict( 108 "Search", 109 e5App().getObject("ViewManager").getActions('search'), 110 actionsDict 111 ) 112 self.__addActionsToDict( 113 "View", 114 e5App().getObject("ViewManager").getActions('view'), 115 actionsDict 116 ) 117 self.__addActionsToDict( 118 "Macro", 119 e5App().getObject("ViewManager").getActions('macro'), 120 actionsDict 121 ) 122 self.__addActionsToDict( 123 "Bookmarks", 124 e5App().getObject("ViewManager").getActions('bookmark'), 125 actionsDict 126 ) 127 self.__addActionsToDict( 128 "Spelling", 129 e5App().getObject("ViewManager").getActions('spelling'), 130 actionsDict 131 ) 132 self.__addActionsToDict( 133 "Window", 134 e5App().getObject("ViewManager").getActions('window'), 135 actionsDict 136 ) 137 138 for category, ref in e5App().getPluginObjects(): 139 if hasattr(ref, "getActions"): 140 self.__addActionsToDict( 141 category, ref.getActions(), actionsDict 142 ) 143 144 else: 145 self.__addActionsToDict( 146 helpViewer.getActionsCategory(), 147 helpViewer.getActions(), 148 actionsDict 149 ) 150 151 # step 2: assemble the data structure to be written 152 shortcutsDict = {} 153 # step 2.0: header 154 shortcutsDict["header"] = { 155 "comment": "eric keyboard shortcuts file", 156 "saved": time.strftime('%Y-%m-%d, %H:%M:%S'), 157 "author": Preferences.getUser("Email"), 158 } 159 # step 2.1: keyboard shortcuts 160 shortcutsDict["shortcuts"] = actionsDict 161 162 try: 163 jsonString = json.dumps(shortcutsDict, indent=2) 164 with open(filename, "w") as f: 165 f.write(jsonString) 166 except (TypeError, OSError) as err: 167 with E5OverridenCursor(): 168 E5MessageBox.critical( 169 None, 170 self.tr("Export Keyboard Shortcuts"), 171 self.tr( 172 "<p>The keyboard shortcuts file <b>{0}</b> could not" 173 " be written.</p><p>Reason: {1}</p>" 174 ).format(filename, str(err)) 175 ) 176 return False 177 178 return True 179 180 def readFile(self: "ShortcutsFile", filename: str) -> bool: 181 """ 182 Public method to read the shortcuts data from a shortcuts JSON file. 183 184 @param filename name of the shortcuts file 185 @type str 186 @return Dictionary of dictionaries of shortcuts. The keys of the 187 dictionary are the shortcuts categories, the values are 188 dictionaries. These dictionaries have the shortcut name as their 189 key and a tuple of accelerators as their value. 190 @rtype dict 191 """ 192 try: 193 with open(filename, "r") as f: 194 jsonString = f.read() 195 shortcutsDict = json.loads(jsonString) 196 except (OSError, json.JSONDecodeError) as err: 197 E5MessageBox.critical( 198 None, 199 self.tr("Import Keyboard Shortcuts"), 200 self.tr( 201 "<p>The keyboard shortcuts file <b>{0}</b> could not be" 202 " read.</p><p>Reason: {1}</p>" 203 ).format(filename, str(err)) 204 ) 205 return {} 206 207 return shortcutsDict["shortcuts"] 208