Skip to content

Commit

Permalink
Modernize the example code blocks in the developer's guide (nvaccess#…
Browse files Browse the repository at this point in the history
…16808)

closes nvaccess#10344

Summary of the issue:
In the developer's guide, the code blocks have been reworked by adding "py" tags and by removing indentation spaces and on empty lines. String types of % have also been converted to f-string type.

Description of user facing changes
In the developer guide, the user will see a new appearance of code blocks, with the string characters % replaced with string characters f, indentations by removed spaces, and py tags present at each block.
  • Loading branch information
Nael-Sayegh authored Aug 5, 2024
1 parent 5ecc243 commit ae0228a
Showing 1 changed file with 123 additions and 119 deletions.
242 changes: 123 additions & 119 deletions projectDocs/dev/developerGuide/developerGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,19 +414,22 @@ As explained above, sometimes the default way of associating an App Module with
In such cases you can distribute a small global plugin along with your App Module which maps it to the executable.
For example to map the App Module named "time_app_mod" to the "time" executable the plugin may be written as follows:

import appModuleHandler
import globalPluginHandler
```py
import appModuleHandler
import globalPluginHandler


class GlobalPlugin(globalPluginHandler.GlobalPlugin):
class GlobalPlugin(globalPluginHandler.GlobalPlugin):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
appModuleHandler.registerExecutableWithAppModule("time", "time_app_mod")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
appModuleHandler.registerExecutableWithAppModule("time", "time_app_mod")

def terminate(self, *args, **kwargs):
super().terminate(*args, **kwargs)
appModuleHandler.unregisterExecutable("time")
```

def terminate(self, *args, **kwargs):
super().terminate(*args, **kwargs)
appModuleHandler.unregisterExecutable("time")
### Example 1: An App Module that Beeps on Focus Change Events {#Example1}

The following example App Module makes NVDA beep each time the focus changes within the notepad application.
Expand All @@ -441,20 +444,19 @@ Finally, open Notepad and move the focus around the application; e.g. move along
You should hear beeps each time the focus changes.
Note though that if you move outside of Notepad - for instance, to Windows Explorer - you do not hear beeps.

--- start ---
# Notepad App Module for NVDA
# Developer guide example 1

import appModuleHandler
```py
# Notepad App Module for NVDA
# Developer guide example 1

class AppModule(appModuleHandler.AppModule):
import appModuleHandler

def event_gainFocus(self, obj, nextHandler):
import tones
tones.beep(550, 50)
nextHandler()
class AppModule(appModuleHandler.AppModule):

--- end ---
def event_gainFocus(self, obj, nextHandler):
import tones
tones.beep(550, 50)
nextHandler()
```

This App Module file starts with two comment lines, which describe what the file is for.

Expand Down Expand Up @@ -484,20 +486,19 @@ By default, apps employing Edge WebView2 such as modern Outlook (olk.exe) are di

The following example is same as Notepad app module above except this is for an app hosted by wwahost.exe.

--- start ---
# wwahost/test App Module for NVDA
# Developer guide example 2

from nvdaBuiltin.appModules.wwahost import *
```py
# wwahost/test App Module for NVDA
# Developer guide example 2

class AppModule(AppModule):
from nvdaBuiltin.appModules.wwahost import *

def event_gainFocus(self, obj, nextHandler):
import tones
tones.beep(550, 50)
nextHandler()
class AppModule(AppModule):

--- end ---
def event_gainFocus(self, obj, nextHandler):
import tones
tones.beep(550, 50)
nextHandler()
```

The biggest difference from Notepad app module is where wwahost app module comes from.
As a built-in app module, wwahost can be imported from nvdaBuiltin.appModules.
Expand Down Expand Up @@ -545,22 +546,21 @@ Once saved in the right place, either restart NVDA or choose Reload Plugins foun

From anywhere, you can now press NVDA+shift+v to have NVDA's version spoken and brailled.

--- start ---
# Version announcement plugin for NVDA
# Developer guide example 3

import globalPluginHandler
from scriptHandler import script
import ui
import versionInfo
```py
# Version announcement plugin for NVDA
# Developer guide example 3

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
import globalPluginHandler
from scriptHandler import script
import ui
import versionInfo

@script(gesture="kb:NVDA+shift+v")
def script_announceNVDAVersion(self, gesture):
ui.message(versionInfo.version)

--- end ---
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
@script(gesture="kb:NVDA+shift+v")
def script_announceNVDAVersion(self, gesture):
ui.message(versionInfo.version)
```

This Global Plugin file starts with two comment lines, which describe what the file is for.

Expand Down Expand Up @@ -683,20 +683,21 @@ Furthermore, it ensures that the script is listed with the description you speci

In order for you to use the script decorator, you will have to import it from the scriptHandler module.

from scriptHandler import script
```py
from scriptHandler import script
```

After that, just above your script definition, add the script decorator, providing it the desired arguments.
For example:

--- start ---
@script(
description=_("Speaks the date and time"),
category=inputCore.SCRCAT_MISC,
gestures=["kb:NVDA+shift+t", "kb:NVDA+alt+r"]
)
def script_sayDateTime(self, gesture):

--- end ---
```py
@script(
description=_("Speaks the date and time"),
category=inputCore.SCRCAT_MISC,
gestures=["kb:NVDA+shift+t", "kb:NVDA+alt+r"]
)
def script_sayDateTime(self, gesture):
```

In this example, your script will be listed in the input gestures dialog under the "Miscellaneous" category.
It will have the description "Speaks the date and time", and will be bound to the "NVDA+shift+t" and "NVDA+alt+r" key combinations on the keyboard.
Expand Down Expand Up @@ -747,38 +748,38 @@ Be very careful to keep all tabs and spaces intact.

Once saved in the right place, either restart NVDA or choose Reload Plugins found under Tools in the NVDA menu.

--- start ---
#Window utility scripts for NVDA
#Developer guide example 4

import globalPluginHandler
from scriptHandler import script
import ui
import api

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

@script(
description=_("Announces the window class name of the current focus object"),
gesture="kb:NVDA+leftArrow"
)
def script_announceWindowClassName(self, gesture):
focusObj = api.getFocusObject()
name = focusObj.name
windowClassName = focusObj.windowClassName
ui.message("class for %s window: %s" % (name, windowClassName))

@script(
description=_("Announces the window control ID of the current focus object"),
gesture="kb:NVDA+rightArrow"
)
def script_announceWindowControlID(self, gesture):
focusObj = api.getFocusObject()
name = focusObj.name
windowControlID = focusObj.windowControlID
ui.message("Control ID for %s window: %d" % (name, windowControlID))

--- end ---
```py
#Window utility scripts for NVDA
#Developer guide example 4

import globalPluginHandler
from scriptHandler import script
import ui
import api

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

@script(
description=_("Announces the window class name of the current focus object"),
gesture="kb:NVDA+leftArrow"
)
def script_announceWindowClassName(self, gesture):
focusObj = api.getFocusObject()
name = focusObj.name
windowClassName = focusObj.windowClassName
ui.message(f"class for {name} window: {windowClassName}")

@script(
description=_("Announces the window control ID of the current focus object"),
gesture="kb:NVDA+rightArrow"
)
def script_announceWindowControlID(self, gesture):
focusObj = api.getFocusObject()
name = focusObj.name
windowControlID = focusObj.windowControlID
ui.message(f"Control ID for {name} window: {windowControlID}")
```

### Events {#events}

When NVDA detects particular toolkit, API or Operating System events, it abstracts these and fires its own internal events on plugins and NVDA Objects.
Expand Down Expand Up @@ -836,14 +837,14 @@ This is done by providing an App Module for that application which simply sets s
The following code can be copied and pasted in to a text file, then saved in the appModules directory with the name of the application you wish to enable sleep mode for.
As always, the file must have a .py extension.

--- start ---
import appModuleHandler
```py
import appModuleHandler

class AppModule(appModuleHandler.AppModule):
class AppModule(appModuleHandler.AppModule):

sleepMode = True
sleepMode = True
```

--- end ---
### Providing Custom NVDA Object Classes {#customNVDAObjectClasses}

Providing custom NVDA Object classes is probably the most powerful and useful way to improve the experience of an application in an NVDA plugin.
Expand Down Expand Up @@ -881,26 +882,26 @@ Notice that the command is specific to edit fields; i.e. it only works while you

The following code can be copied and pasted in to a text file, then saved in the appModules directory with the name of notepad.py.

--- start ---
import appModuleHandler
from scriptHandler import script
from NVDAObjects.IAccessible import IAccessible
import controlTypes
import ui
```py
import appModuleHandler
from scriptHandler import script
from NVDAObjects.IAccessible import IAccessible
import controlTypes
import ui

class AppModule(appModuleHandler.AppModule):
class AppModule(appModuleHandler.AppModule):

def chooseNVDAObjectOverlayClasses(self, obj, clsList):
if obj.windowClassName == "Edit" and obj.role == controlTypes.Role.EDITABLETEXT:
clsList.insert(0, EnhancedEditField)
def chooseNVDAObjectOverlayClasses(self, obj, clsList):
if obj.windowClassName == "Edit" and obj.role == controlTypes.Role.EDITABLETEXT:
clsList.insert(0, EnhancedEditField)

class EnhancedEditField(IAccessible):
class EnhancedEditField(IAccessible):

@script(gesture="kb:NVDA+l")
def script_reportLength(self, gesture):
ui.message("%d" % len(self.value))
@script(gesture="kb:NVDA+l")
def script_reportLength(self, gesture):
ui.message(f"{len(self.value)}")
```

--- end ---
### Making Small Changes to an NVDA Object in App Modules {#smallChangesToNVDAObjectInAppModules}

Sometimes, you may wish to make only small changes to an NVDA Object in an application, such as overriding its name or role.
Expand All @@ -921,28 +922,31 @@ That is, when it receives focus, NVDA will say "Content edit".

The following code can be copied and pasted in to a text file, then saved in the appModules directory with the name of notepad.py.

--- start ---
import appModuleHandler
from NVDAObjects.window import Window
```py
import appModuleHandler
from NVDAObjects.window import Window

class AppModule(appModuleHandler.AppModule):
class AppModule(appModuleHandler.AppModule):

def event_NVDAObject_init(self, obj):
if isinstance(obj, Window) and obj.windowClassName == "Edit" and obj.windowControlID == 15:
obj.name = "Content"
```

def event_NVDAObject_init(self, obj):
if isinstance(obj, Window) and obj.windowClassName == "Edit" and obj.windowControlID == 15:
obj.name = "Content"
--- end ---
### Parsing additional command line arguments in your plugin {#PluginCLIArgs}

By default NVDA accepts limited set of command line arguments and shows an error for an unknown ones.
However if you want to use any additional arguments, this is possible by adding a handler to the extension point `addonHandler.isCLIParamKnown`.
Note that since command line arguments are processed just after NVDA starts, your add-on needs to process them in the global plugin, since app modules or other drivers may not be loaded at this stage.
A sample handler can be written as follows:

def processArgs(cliArgument: str) -> bool:
if cliArgument == "--enable-addon-feature":
...
return True # Argument is known to the add-on and should not be flagged by NVDA
return False # unknown argument - NVDA should warn user
```py
def processArgs(cliArgument: str) -> bool:
if cliArgument == "--enable-addon-feature":
...
return True # Argument is known to the add-on and should not be flagged by NVDA
return False # unknown argument - NVDA should warn user
```

Then the handler needs to be registered - preferably in the constructor of your global plugin:

Expand Down

0 comments on commit ae0228a

Please sign in to comment.