diff --git a/addon/globalPlugins/MathCAT/MathCAT.py b/addon/globalPlugins/MathCAT/MathCAT.py index 93983bb..e8dbd46 100644 --- a/addon/globalPlugins/MathCAT/MathCAT.py +++ b/addon/globalPlugins/MathCAT/MathCAT.py @@ -92,7 +92,11 @@ def getLanguageToUse(mathMl: str = "") -> str: - """Get the language specified in a math tag if the language pref is Auto, else the language preference.""" + """Get the language specified in a math tag if the language pref is Auto, else the language preference. + + :param mathMl: The MathML string to examine for language. Defaults to an empty string. + :returns: The language string to use. + """ mathCATLanguageSetting: str = "Auto" try: # ignore regional differences if the MathCAT language setting doesn't have it. @@ -117,8 +121,13 @@ def getLanguageToUse(mathMl: str = "") -> str: def convertSSMLTextForNVDA(text: str) -> list[str | SpeechCommand]: - """Change the SSML in the text into NVDA's command structure. - The environment is examined to determine whether a language switch is needed""" + """ + Change the SSML in the text into NVDA's command structure. + The environment is examined to determine whether a language switch is needed. + + :param text: The SSML text to convert. + :returns: A list of strings and SpeechCommand objects. + """ # MathCAT's default rate is 180 wpm. # Assume that 0% is 80 wpm and 100% is 450 wpm and scale accordingly. # log.info(f"\nSpeech str: '{text}'") @@ -212,6 +221,8 @@ def convertSSMLTextForNVDA(text: str) -> list[str | SpeechCommand]: class MathCATInteraction(mathPres.MathInteractionNVDAObject): + """An NVDA object used to interact with MathML.""" + # Put MathML or other formats on the clipboard. # MathML is put on the clipboard using the two formats below (defined by MathML spec) # We use both formats because some apps may only use one or the other @@ -225,9 +236,14 @@ class MathCATInteraction(mathPres.MathInteractionNVDAObject): def __init__( self, - provider: mathPres.MathPresentationProvider = None, + provider: mathPres.MathPresentationProvider | None = None, mathMl: str | None = None, ): + """Initialize the MathCATInteraction object. + + :param provider: Optional presentation provider. + :param mathMl: Optional initial MathML string. + """ super(MathCATInteraction, self).__init__(provider=provider, mathMl=mathMl) if mathMl is None: self.initMathML = "" @@ -235,6 +251,7 @@ def __init__( self.initMathML = mathMl def reportFocus(self) -> None: + """Calls MathCAT's ZoomIn command and speaks the resulting text.""" super(MathCATInteraction, self).reportFocus() # try to get around espeak bug where voice slows down if _synthesizerRate and getSynth().name == "espeak": @@ -256,6 +273,7 @@ def getBrailleRegions( self, review: bool = False, ) -> Generator[braille.Region, None, None]: + """Yields braille.Region objects for this MathCATInteraction object.""" # log.info("***MathCAT start getBrailleRegions") yield braille.NVDAObjectRegion(self, appendText=" ") region: braille.Region = braille.Region() @@ -276,7 +294,12 @@ def getScript( self, gesture: KeyboardInputGesture, ) -> Callable[KeyboardInputGesture, None] | None: - # Pass most keys to MathCAT. Pretty ugly. + """ + Returns the script function bound to the given gesture. + + :param gesture: A KeyboardInputGesture sent to this object. + :returns: The script bound to that gesture. + """ if ( isinstance(gesture, KeyboardInputGesture) and "NVDA" not in gesture.modifierNames @@ -309,6 +332,10 @@ def getScript( return super().getScript(gesture) def script_navigate(self, gesture: KeyboardInputGesture) -> None: + """Performs the specified navigation command. + + :param gesture: They keyboard command which specified the navigation command to perform. + """ try: # try to get around espeak bug where voice slows down if _synthesizerRate and getSynth().name == "espeak": @@ -365,6 +392,10 @@ def script_navigate(self, gesture: KeyboardInputGesture) -> None: gesture="kb:control+c", ) def script_rawdataToClip(self, gesture: KeyboardInputGesture) -> None: + """Copies the raw data to the clipboard, either as MathML, ASCII math, or LaTeX, depending on user preferences. + + :param gesture: The gesture which activated this script. + """ try: copyAs: str = "mathml" # value used even if "CopyAs" pref is invalid textToCopy: str = "" @@ -416,7 +447,7 @@ def script_rawdataToClip(self, gesture: KeyboardInputGesture) -> None: _hasDataAttr: re.Pattern = re.compile(" data-[^=]+='[^']*'") def _wrapMathMLForClipBoard(self, text: str) -> str: - # cleanup the MathML a little + """Cleanup the MathML a little.""" text = re.sub(self._hasAddedId, "", text) mathMLWithNS: str = re.sub(self._hasDataAttr, "", text) if not re.match(self._mathTagHasNameSpace, mathMLWithNS): @@ -434,9 +465,10 @@ def _copyToClipAsMathML( notify: bool | None = False, ) -> bool: """Copies the given text to the windows clipboard. - @returns: True if it succeeds, False otherwise. - @param text: the text which will be copied to the clipboard - @param notify: whether to emit a confirmation message + + :param text: the text which will be copied to the clipboard. + :param notify: whether to emit a confirmation message. + :returns: True if it succeeds, False otherwise. """ # copied from api.py and modified to use CF_MathML_Presentation if not isinstance(text, str) or len(text) == 0: @@ -463,6 +495,12 @@ def _copyToClipAsMathML( return False def _setClipboardData(self, format: int, data: str) -> None: + """Sets the clipboard data to the given data with the specified format. + + :param format: The format for the clipboard data. + This is an integer format code returned by windll.user32.RegisterClipboardFormatW. + :param data: The data to set on the clipboard. + """ # Need to support MathML Presentation, so this copied from winUser.py and the first two lines are commented out # For now only unicode is a supported format # if format!=CF_UNICODETEXT: @@ -485,6 +523,7 @@ def _setClipboardData(self, format: int, data: str) -> None: class MathCAT(mathPres.MathPresentationProvider): def __init__(self): + """Initializes MathCAT, loading the rules specified in the rules directory.""" # super(MathCAT, self).__init__(*args, **kwargs) try: @@ -502,6 +541,11 @@ def getSpeechForMathMl( self, mathml: str, ) -> list[str | SpeechCommand]: + """Outputs a MathML string as speech. + + :param mathml: A MathML string. + :returns: A list of speech commands and strings representing the given MathML. + """ global _synthesizerRate synth: SynthDriver = getSynth() synthConfig = config.conf["speech"][synth.name] @@ -557,6 +601,10 @@ def getSpeechForMathMl( getSynth()._set_rate(_synthesizerRate) def _addSounds(self) -> bool: + """Queries the user preferences to determine whether or not sounds should be added. + + :returns: True if MathCAT's `SpeechSound` preference is set, and False otherwise. + """ try: return libmathcat.GetPreference("SpeechSound") != "None" except Exception as e: @@ -564,6 +612,11 @@ def _addSounds(self) -> bool: return False def getBrailleForMathMl(self, mathml: str) -> str: + """Gets the braille representation of a given MathML string by calling MathCAT's GetBraille function. + + :param mathml: A MathML string. + :returns: A braille string representing the input MathML. + """ # log.info("***MathCAT getBrailleForMathMl") try: libmathcat.SetMathML(mathml) @@ -582,6 +635,10 @@ def getBrailleForMathMl(self, mathml: str) -> str: return "" def interactWithMathMl(self, mathml: str) -> None: + """Interact with a MathML string, creating a MathCATInteraction object. + + :param mathml: The MathML representing the math to interact with. + """ MathCATInteraction(provider=self, mathMl=mathml).setFocus() MathCATInteraction(provider=self, mathMl=mathml).script_navigate(None) @@ -590,6 +647,7 @@ def interactWithMathMl(self, mathml: str) -> None: def _monkeyPatchESpeak() -> None: + """Patches an eSpeak bug where the voice slows down.""" global CACHED_SYNTH currentSynth: SynthDriver = getSynth() if currentSynth.name != "espeak" or CACHED_SYNTH == currentSynth: diff --git a/addon/globalPlugins/MathCAT/MathCATPreferences.py b/addon/globalPlugins/MathCAT/MathCATPreferences.py index 8f2ed1e..8424707 100644 --- a/addon/globalPlugins/MathCAT/MathCATPreferences.py +++ b/addon/globalPlugins/MathCAT/MathCATPreferences.py @@ -42,7 +42,20 @@ class UserInterface(MathCATgui.MathCATPreferencesDialog): - def __init__(self, parent): + """UI class for the MathCAT Preferences Dialog. + + Initializes and manages user preferences, including language, speech, braille, + and navigation settings. Extends MathCATgui.MathCATPreferencesDialog. + """ + + def __init__(self, parent: wx.Window | None): + """Initialize the preferences dialog. + + Sets up the UI, loads preferences, applies defaults and saved settings, + and restores the previous UI state. + + :param parent: The parent window for the dialog. + """ # initialize parent class MathCATgui.MathCATPreferencesDialog.__init__(self, parent) @@ -79,16 +92,36 @@ def __init__(self, parent): @staticmethod def pathToLanguagesFolder() -> str: - # the user preferences file is stored at: MathCAT\Rules\Languages + r"""Returns the full path to the Languages rules folder. + + The language rules are stored in: + MathCAT\Rules\Languages, relative to the location of this file. + + :return: Absolute path to the Languages folder as a string. + """ return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "Languages") @staticmethod def pathToBrailleFolder() -> str: - # the user preferences file is stored at: MathCAT\Rules\Languages + r"""Returns the full path to the Braille rules folder. + + The Braille rules are stored in: + MathCAT\Rules\Braille, relative to the location of this file. + + :return: Absolute path to the Braille folder as a string. + """ return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "Braille") @staticmethod def languagesDict() -> dict[str, str]: + """Returns a dictionary mapping language codes to their corresponding language names. + + This dictionary includes standard language codes, as well as regional variants such as + 'en-GB', 'zh-HANT', and others. + + :return: A dictionary where the key is the language code (e.g., 'en', 'fr', 'zh-HANS') + and the value is the language name (e.g. 'English', 'Français', 'Chinese, Simplified'). + """ languages = { "aa": "Afar", "ab": "Аҧсуа", @@ -270,6 +303,18 @@ def getRulesFiles( pathToDir: str, processSubDirs: Callable[[str, str], list[str]] | None, ) -> list[str]: + """ + Get the rule files from a directory, optionally processing subdirectories. + + Searches for files ending with '_Rules.yaml' in the specified directory. + If no rule files are found, attempts to find them inside a corresponding ZIP archive, + including checking any subdirectories inside the ZIP. + + :param pathToDir: Path to the directory to search for rule files. + :param processSubDirs: Optional callable to process subdirectories. It should take the subdirectory name + and the language code as arguments, returning a list of rule filenames found in that subdirectory. + :return: A list of rule file names found either directly in the directory or inside the ZIP archive. + """ language: str = os.path.basename(pathToDir) ruleFiles: list[str] = [ os.path.basename(file) for file in glob.glob(os.path.join(pathToDir, "*_Rules.yaml")) @@ -294,6 +339,14 @@ def getRulesFiles( return ruleFiles def getLanguages(self) -> None: + """Populate the language choice dropdown with available languages and their regional variants. + + This method scans the language folders and adds entries for each language and its + regional dialects. Language folders use ISO 639-1 codes and regional variants use ISO 3166-1 alpha-2 codes. + + It also adds a special "Use Voice's Language (Auto)" option at the top. + """ + def addRegionalLanguages(subDir: str, language: str) -> list[str]: # the language variants are in folders named using ISO 3166-1 alpha-2 # codes https://en.wikipedia.org/wiki/ISO_3166-2 @@ -334,19 +387,34 @@ def addRegionalLanguages(subDir: str, language: str) -> list[str]: self._choiceLanguage.Append(language + " (" + language + ")") def getLanguageCode(self) -> str: + """Extract the language code from the selected language string in the UI. + + The selected language string is expected to contain the language code in parentheses, + for example: "English (en)". + + :return: The language code extracted from the selection. + """ langSelection: str = self._choiceLanguage.GetStringSelection() langCode: str = langSelection[langSelection.find("(") + 1 : langSelection.find(")")] return langCode - def getSpeechStyles(self, thisSpeechStyle: str): + def getSpeechStyles(self, thisSpeechStyle: str) -> None: """Get all the speech styles for the current language. - This sets the SpeechStyles dialog entry""" + This sets the SpeechStyles dialog entry. + + :param thisSpeechStyle: The speech style to set or highlight in the dialog. + """ from speech import getCurrentLanguage def getSpeechStyleFromDirectory(dir: str, lang: str) -> list[str]: r"""Get the speech styles from any regional dialog, from the main language, dir and if there isn't from the zip file. The 'lang', if it has a region dialect, is of the form 'en\uk' - The returned list is sorted alphabetically""" + The returned list is sorted alphabetically + + :param dir: The directory path to search for speech styles. + :param lang: Language code which may include a regional dialect (e.g., 'en\uk'). + :return: A list of speech styles sorted alphabetically. + """ # start with the regional dialect, then add on any (unique) styles in the main dir mainLang: str = lang.split("\\")[0] # does the right thing even if there is no regional directory allStyleFiles: list[str] = [] @@ -407,6 +475,11 @@ def getSpeechStyleFromDirectory(dir: str, lang: str) -> list[str]: self._choiceSpeechStyle.SetSelection(0) def getBrailleCodes(self) -> None: + """Initializes and populates the braille code choice control with available braille codes. + + Scans the braille codes folder for valid directories containing rules files, and adds them + to the braille code dropdown in the dialog. + """ # initialise the braille code list self._choiceBrailleMathCode.Clear() # populate the available braille codes in the dialog @@ -419,7 +492,11 @@ def getBrailleCodes(self) -> None: self._choiceBrailleMathCode.Append(brailleCode) def setUIValues(self) -> None: - # set the UI elements to the ones read from the preference file(s) + """Sets the UI elements based on the values read from the user preferences. + + Attempts to match preference values to UI controls; falls back to defaults if values are invalid + or missing. + """ try: self._choiceImpairment.SetSelection( Speech_Impairment.index(userPreferences["Speech"]["Impairment"]), @@ -512,6 +589,7 @@ def setUIValues(self) -> None: print("Key not found", err) def getUIValues(self) -> None: + """Reads the current values from the UI controls and updates the user preferences accordingly.""" global userPreferences # read the values from the UI and update the user preferences dictionary userPreferences["Speech"]["Impairment"] = Speech_Impairment[self._choiceImpairment.GetSelection()] @@ -556,20 +634,24 @@ def getUIValues(self) -> None: @staticmethod def pathToDefaultPreferences() -> str: + """Returns the full path to the default preferences file.""" return os.path.join(os.path.dirname(os.path.abspath(__file__)), "Rules", "prefs.yaml") @staticmethod def pathToUserPreferencesFolder() -> str: + """Returns the path to the folder where user preferences are stored.""" # the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml return os.path.join(os.path.expandvars("%APPDATA%"), "MathCAT") @staticmethod def pathToUserPreferences() -> str: + """Returns the full path to the user preferences file.""" # the user preferences file is stored at: C:\Users\AppData\Roaming\MathCAT\prefs.yaml return os.path.join(UserInterface.pathToUserPreferencesFolder(), "prefs.yaml") @staticmethod def loadDefaultPreferences() -> None: + """Loads the default preferences, overwriting any existing user preferences.""" global userPreferences # load default preferences into the user preferences data structure (overwrites existing) if os.path.exists(UserInterface.pathToDefaultPreferences()): @@ -581,6 +663,10 @@ def loadDefaultPreferences() -> None: @staticmethod def loadUserPreferences() -> None: + """Loads user preferences from a file and merges them into the current preferences. + + If the user preferences file exists, its values overwrite the defaults. + """ global userPreferences # merge user file values into the user preferences data structure if os.path.exists(UserInterface.pathToUserPreferences()): @@ -595,6 +681,15 @@ def validate( validValues: list[str | bool], defaultValue: str | bool, ) -> None: + """Validates that a preference value is in a list of valid options or non-empty if no list is given. + + If the value is missing or invalid, sets it to the default. + + :param key1: The first-level key in the preferences dictionary. + :param key2: The second-level key in the preferences dictionary. + :param validValues: A list of valid values; if empty, any non-empty value is valid. + :param defaultValue: The default value to set if validation fails. + """ global userPreferences try: if validValues == []: @@ -621,6 +716,15 @@ def validateInt( validValues: list[int], defaultValue: int, ) -> None: + """Validates that an integer preference is within a specified range. + + If the value is missing or out of bounds, sets it to the default. + + :param key1: The first-level key in the preferences dictionary. + :param key2: The second-level key in the preferences dictionary. + :param validValues: A list with two integers [min, max] representing valid bounds. + :param defaultValue: The default value to set if validation fails. + """ global userPreferences try: # any value between lower and upper bounds is valid @@ -639,7 +743,11 @@ def validateInt( @staticmethod def validateUserPreferences(): - # check each user preference value to ensure it is present and valid, set default value if not + """Validates all user preferences, ensuring each is present and valid. + + If a preference is missing or invalid, it is reset to its default value. + Validation covers speech, navigation, and braille settings. + """ # Speech: # Impairment: Blindness # LearningDisability, LowVision, Blindness UserInterface.validate( @@ -693,6 +801,11 @@ def validateUserPreferences(): @staticmethod def writeUserPreferences() -> None: + """Writes the current user preferences to a file and updates special settings. + + Sets the language preference through the native library, ensures the preferences + folder exists, and saves the preferences to disk. + """ # Language is special because it is set elsewhere by SetPreference which overrides the user_prefs -- so set it here from . import libmathcat_py as libmathcat @@ -710,6 +823,13 @@ def writeUserPreferences() -> None: yaml.dump(userPreferences, stream=f, allow_unicode=True) def onRelativeSpeedChanged(self, event: wx.ScrollEvent) -> None: + """Handles changes to the relative speed slider and updates speech output. + + Adjusts the speech rate based on the slider value and speaks a test phrase + with the updated rate. + + :param event: The scroll event triggered by adjusting the relative speed slider. + """ rate: int = self._sliderRelativeSpeed.GetValue() # Translators: this is a test string that is spoken. Only translate "the square root of x squared plus y squared" text: str = _("the square root of x squared plus y squared").replace( @@ -720,6 +840,13 @@ def onRelativeSpeedChanged(self, event: wx.ScrollEvent) -> None: speak(convertSSMLTextForNVDA(text)) def onPauseFactorChanged(self, event: wx.ScrollEvent) -> None: + """Handles changes to the pause factor slider and updates speech output accordingly. + + Calculates the pause durations based on the slider value, constructs an SSML string + with adjusted prosody and breaks, and sends it for speech synthesis. + + :param event: The scroll event triggered by adjusting the pause factor slider. + """ rate: int = self._sliderRelativeSpeed.GetValue() pfSlider = self._sliderPauseFactor.GetValue() pauseFactor = ( @@ -747,35 +874,91 @@ def onPauseFactorChanged(self, event: wx.ScrollEvent) -> None: speak(convertSSMLTextForNVDA(text)) def onClickOK(self, event: wx.CommandEvent) -> None: + """Saves current preferences and closes the dialog. + + Retrieves values from the UI, writes them to the preferences, and then closes the window. + + :param event: The event triggered by clicking the OK button. + """ UserInterface.getUIValues(self) UserInterface.writeUserPreferences() self.Destroy() def onClickCancel(self, event: wx.CommandEvent) -> None: + """Closes the preferences dialog without saving changes. + + :param event: The event triggered by clicking the Cancel button. + """ self.Destroy() def onClickApply(self, event: wx.CommandEvent) -> None: + """Applies the current UI settings to the user preferences. + + Retrieves values from the UI and writes them to the preferences configuration. + + :param event: The event triggered by clicking the Apply button. + """ UserInterface.getUIValues(self) UserInterface.writeUserPreferences() def onClickReset(self, event: wx.CommandEvent) -> None: + """Resets preferences to their default values. + + Loads the default preferences, validates them, and updates the UI accordingly. + + :param event: The event triggered by clicking the Reset button. + """ UserInterface.loadDefaultPreferences() UserInterface.validateUserPreferences() UserInterface.setUIValues(self) def onClickHelp(self, event: wx.CommandEvent) -> None: + """Opens the MathCAT user guide in the default web browser. + + Triggered when the Help button is clicked. + + :param event: The event triggered by clicking the Help button. + """ webbrowser.open("https://nsoiffer.github.io/MathCAT/users.html") def onListBoxCategories(self, event: wx.CommandEvent) -> None: - # the category changed, now show the appropriate dialogue page + """Handles category selection changes in the preferences list box. + + Updates the displayed panel in the dialog to match the newly selected category. + + :param event: The event triggered by selecting a different category. + """ self._simplebookPanelsCategories.SetSelection(self._listBoxPreferencesTopic.GetSelection()) def onLanguage(self, event: wx.CommandEvent) -> None: - # the language changed, get the SpeechStyles for the new language + """Handles the event when the user changes the selected language. + + Retrieves and updates the available speech styles for the newly selected language + in the preferences dialog. + + :param event: The event triggered by changing the language selection. + """ UserInterface.getSpeechStyles(self, self._choiceSpeechStyle.GetStringSelection()) def mathCATPreferencesDialogOnCharHook(self, event: wx.KeyEvent) -> None: - # designed choice is that Enter is the same as clicking OK, and Escape is the same as clicking Cancel + """Handles character key events within the MathCAT Preferences dialog. + + This method interprets specific key presses to mimic button clicks or + navigate within the preferences dialog: + + - escape: Triggers the Cancel button functionality. + - enter: Triggers the OK button functionality. + - ctrl+tab: Cycles forward through the preference categories. + - ctrl+shift+tab: Cycles backward through the preference categories. + - tab: Moves focus to the first control in the currently selected category, + if the category list has focus. + - shift+tab: Moves focus to the second row of controls, + if the OK button has focus. + + If none of these keys are matched, the event is skipped to allow default processing. + + :param event: The keyboard event to handle. + """ keyCode: int = event.GetKeyCode() if keyCode == wx.WXK_ESCAPE: UserInterface.onClickCancel(self, event) diff --git a/addon/globalPlugins/MathCAT/MathCATgui.py b/addon/globalPlugins/MathCAT/MathCATgui.py index e0bacf3..fdf06b8 100644 --- a/addon/globalPlugins/MathCAT/MathCATgui.py +++ b/addon/globalPlugins/MathCAT/MathCATgui.py @@ -13,7 +13,16 @@ class MathCATPreferencesDialog(wx.Dialog): - def __init__(self, parent): + """Main dialog window for configuring MathCAT preferences. + + This base class sets up the layout and controls. + """ + + def __init__(self, parent: wx.Window | None): + """Initialize the preferences dialog. + + :param parent: The parent window for this dialog. + """ wx.Dialog.__init__( self, parent, @@ -825,38 +834,50 @@ def __init__(self, parent): self._buttonHelp.Bind(wx.EVT_BUTTON, self.onClickHelp) def __del__(self): + """Destructor placeholder; override if cleanup is needed.""" pass # Virtual event handlers, override them in your derived class def mathCATPreferencesDialogOnCharHook(self, event: wx.KeyEvent) -> None: + """Handle character input events; override in subclass as needed.""" event.Skip() def mathCATPreferencesDialogOnKeyUp(self, event: wx.KeyEvent) -> None: + """Handle key release events; override in subclass as needed.""" event.Skip() def onListBoxCategories(self, event: wx.CommandEvent) -> None: + """Handle selection events in the categories list box; override in subclass as needed.""" event.Skip() def onLanguage(self, event: wx.CommandEvent) -> None: + """Handle language selection; override in subclass as needed.""" event.Skip() def onRelativeSpeedChanged(self, event: wx.ScrollEvent) -> None: + """Handle change in relative speed; override in subclass as needed.""" event.Skip() def onPauseFactorChanged(self, event: wx.ScrollEvent) -> None: + """Handle change in pause factor; override in subclass as needed.""" event.Skip() def onClickOK(self, event: wx.CommandEvent) -> None: + """Handle OK button click; override in subclass as needed.""" event.Skip() def onClickCancel(self, event: wx.CommandEvent) -> None: + """Handle Cancel button click; override in subclass as needed.""" event.Skip() def onClickApply(self, event: wx.CommandEvent) -> None: + """Handle Apply button click; override in subclass as needed.""" event.Skip() def onClickReset(self, event: wx.CommandEvent) -> None: + """Handle Reset button click; override in subclass as needed.""" event.Skip() def onClickHelp(self, event: wx.CommandEvent) -> None: + """Handle Help button click; override in subclass as needed.""" event.Skip() diff --git a/addon/globalPlugins/MathCAT/__init__.py b/addon/globalPlugins/MathCAT/__init__.py index cfbae9f..4bc4da4 100644 --- a/addon/globalPlugins/MathCAT/__init__.py +++ b/addon/globalPlugins/MathCAT/__init__.py @@ -1,14 +1,15 @@ # -*- coding: UTF-8 -*- -# MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML -# The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued. -# Author: Neil Soiffer -# Copyright: this file is copyright GPL2 -# The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license -# and also (obviously) requires external speech engines and braille drivers. -# The plugin also requires the use of a small python dll: python3.dll -# python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved" - +""" +MathCAT add-on: generates speech, braille, and allows exploration of expressions written in MathML. +The goal of this add-on is to replicate/improve upon the functionality of MathPlayer which has been discontinued. +Author: Neil Soiffer +Copyright: this file is copyright GPL2 + The code additionally makes use of the MathCAT library (written in Rust) which is covered by the MIT license + and also (obviously) requires external speech engines and braille drivers. + The plugin also requires the use of a small python dll: python3.dll + python3.dll has "Copyright © 2001-2022 Python Software Foundation; All Rights Reserved +""" import globalPluginHandler # we are a global plugin import globalVars @@ -26,12 +27,25 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin): + """ + Global plugin for the MathCAT add-on. + """ + def __init__(self, *args, **kwargs): + """ + Initialize the Global Plugin and add the MathCAT menu. + + :param args: Additional positional arguments. + :param kwargs: Additional keyword arguments. + """ super().__init__(*args, **kwargs) # MathCAT.__init__(self) self.addMathCATMenu() def addMathCATMenu(self) -> None: + """ + Adds the MathCAT settings menu to the NVDA preferences. + """ if not globalVars.appArgs.secure: self.preferencesMenu = mainFrame.sysTrayIcon.preferencesMenu # Translators: this show up in the NVDA preferences dialog. It opens the MathCAT preferences dialog @@ -39,9 +53,17 @@ def addMathCATMenu(self) -> None: mainFrame.sysTrayIcon.Bind(wx.EVT_MENU, self.onSettings, self.settings) def onSettings(self, evt: wx.CommandEvent) -> None: + """ + Opens the MathCAT preferences dialog. + + :param evt: The event that triggered this action. + """ mainFrame.popupSettingsDialog(UserInterface) def terminate(self) -> None: + """ + Cleans up by removing the MathCAT menu item upon termination. + """ try: if not globalVars.appArgs.secure: self.preferencesMenu.Remove(self.settings)