Recommendation to deal with NVDA's configurations in add-ons.


Hi there.

Today I developed a small utility to deal with NVDA's settings in our add-ons.
I really hate to write config.conf.['a1']['a2]['option'] each time I
need to access, or set a value in the configuration. But was not a
problem for me until now.
I was merging a pr in one of my add-ons, I was a little asleep, and
was very hard to find the error. Just a capital letter! And the editor
usually don't help with autocompletion if we are writting inside ''.
So, I thought... The config names and path should be declared in one
part only, and access the configurations using that code. Then I
decided to experiment with one of my mantained add-ons.

This consist on a class and a very simple descriptor, to get and
access the config. See this example:

class appConfig:
def __init__(self):
# the path to the config. currently you can't use customized paths
(for example to set it for each synthesizer) but is not hard to
implement it.
self.path = ['speechHistoryExplorer']
# if the following is true, will return the value. False, will
return the configuration name.
# is set to false to help creating the conspec.
self.returnValue = False

# now declaring some configurations, outside the constructor.
# optConfig is the descriptor that does all the ting, but it doesn't
matter to explain it.
# you can use properties too, but is too repetitive.
maxHistoryLength = OptConfig('maxHistoryLength')
trimWhitespaceFromStart = OptConfig('trimWhitespaceFromStart')
trimWhitespaceFromEnd = OptConfig('trimWhitespaceFromEnd')
beepWhenPerformingActions = OptConfig('beepWhenPerformingActions')
beepPanning = OptConfig('beepPanning')

# now instantiate the class.
appConfig = appConfig()

# now, it will create the conspec.
confspec = {
appConfig.maxHistoryLength: 'integer(default=500)',
appConfig.trimWhitespaceFromStart: 'boolean(default=false)',
appConfig.trimWhitespaceFromEnd: 'boolean(default=false)',
appConfig.beepWhenPerformingActions: 'boolean(default=true)',
appConfig.beepPanning: 'boolean(default=true)',
# set the conspec to the config of NVDA.
config.conf.spec[appConfig.path[0]] = confspec
# now change returnValue to True, because usually we want the value of
the config, not the name.
appConfig.returnValue = True

# Now, get or set a setting is so easy! just do the following:
# get a copnfig:
# set a config:
appConfig.maxHistoryLength = 250

and you can use the smart Autocomplete feature of vs code, for
example. So, you will never make a mistake when refering to a config.
And you can avoid to use those tedious ', [, etc each time you need to
access a configuration.

You can see the entire file here:

I'm open to suggestions.



Hi again. I decided to do all the stuff in a configHelper, so, now is
easier to define a conspec. and the options can be recognized by the
ides without issues. I need to do more tests, but I like this way of
declaring configurations specs for add-ons.

Now, to declare a spec you can do the following, if you use this util.

from ._configHelper import *
class AppConfig(BaseConfig):
def __init__(self):

# declaration of the options for the config.
maxHistoryLength = OptConfig('integer(default=500)')
trimWhitespaceFromStart = OptConfig('boolean(default=false)')
trimWhitespaceFromEnd = OptConfig('boolean(default=true)')
beepWhenPerformingActions = OptConfig('boolean(default=true)')
beepPanning = OptConfig('boolean(default=true)')
AF = registerConfig(AppConfig)

And to acces or set a configuration:

AF.beepPanning = True

It can be improved a lot, but is enough for me.

the config helper can be found here:


Hi there!.
Again, I added a small but big change to this utility to help dealing
with the add-ons settings.
Now, all the things can be done with a class decorator. But you can
use the older whay if you want.
see the complete information here:

Let's see how easy is to write a spec now, and help to the ides to
understand the configuration. All things declared in just one place.

# first, import the utility. The decorator and the register config function.
from ._configHelper import configSpec, registerConfig

# now the class definition, with the decorator first.
# this decorator will replace the attributes with a descriptor to
manage accessing and updating values.
class AppConfig:
# the config path. Important to call it __path__ = ...
__path__ = 'beepKeyboard'
# now the definition of the settings. in form of name = 'desc'
beepUpperWithCapsLock = 'boolean(default=True)'
beepCharacterWithShift = 'boolean(default=False)'
beepToggleKeyChanges = 'boolean(default=False)'
announceToggleStatus = 'boolean(default=True)'
disableBeepingOnPasswordFields = 'boolean(default=True)'
ignoredCharactersForShift = "string(default='\\x1b\\t\\b\\r ')"
beepForCharacters = "string(default='')"
shiftedCharactersTone = 'int_list(default=list(6000,10,25))'
customCharactersTone = 'int_list(default=list(6000,10,25))'
capsLockUpperTone = 'int_list(default=list(3000,40,50))'
toggleOffTone = 'int_list(default=list(500,40,50))'
toggleOnTone = 'int_list(default=list(2000, 40, 50))'
AF = registerConfig(AppConfig)

# accessing an option:
print("this should print False", AF.disableBeepingOnPasswordFields)
# changing the value:
AF.disableBeepingOnPasswordFields = True
# let's see the new value.
print("this should print True", AF.disableBeepingOnPasswordFields)

It can't be easier! I wish to use python typings to make the config
description, and that would help to reduce typing errors. But for now,
is enough for me. Although I think that this idea is perfectly

David CM.

2022-07-30 5:26 GMT-06:00, DaVid via <dhf360@...>: