Skip to content

Custom braille tables (#3304)#10172

Closed
JulienCochuyt wants to merge 32 commits intonvaccess:masterfrom
accessolutions:i3304-customBrailleTables
Closed

Custom braille tables (#3304)#10172
JulienCochuyt wants to merge 32 commits intonvaccess:masterfrom
accessolutions:i3304-customBrailleTables

Conversation

@JulienCochuyt
Copy link
Copy Markdown
Contributor

@JulienCochuyt JulienCochuyt commented Sep 8, 2019

Link to issue number:

Fixes #3304.
Fixes #9863.
Supersedes PR #9864.
Addresses #505 (comment)
Provides a workaround for #3470

Summary of the issue:

In NVDA, there is no easy and reliable way for an add-on to provide a new braille table.
For an experienced users wishing to do so there are two options:

  • Alter manually an existing table in louis/tables.
    The new table still has its original name in the settings GUI.
    This change is lost upon NVDA updates.
  • Set the absolute path to the table file in the configuration in lieu of the usual file name.
    The settings GUI shows an empty entry for this one.
    Forces to manually alter nvda.ini.
    Forces to copy in the same directory the whole dependency chain of the new table plus braille-patterns.cti. This is because liblouis default table resolver only looks for tables in a single directory, See Make it easier for add-ons to supply custom braille tables #5489 (comment)

Description of how this pull request fixes the issue:

  • Add a new brailleTables optional directory in both the user scratchpad directory and the add-on directory structure.
  • Implement a custom liblouis table resolver which looks up first in these directories, then in louis/tables.
  • Support reading tables metadata from an optional brailleTables section of the add-on manifest or from a .ini file with the same format found in the brailleTables subdirectory of the scratchpad directory, allowing a user to provide a display names and set output/input/contracted capabilities with no code.
  • Enforce the existing fallback mechanism to ensure there still is braille output if the configured table cannot be found eg. because an add-on or the scratchpad directory was disabled. This now applies both to the main configuration and individual profiles and also covers braille input.

As is already the case in the default liblouis table resolver, table file names are treated as an identifier.
That is, if a user eg. provides in its scratchpad/brailleTables directory a new version of a table which is imported by other tables, all of those are impacted.
Only one version of a given table file name can be resolved. In that concern, the user scratchpad directory takes precedence over add-ons, which take precedence over the tables provided with NVDA.
It also mean that the manifest file can alter metadata of a table file provided elsewhere.
Eg. adding a manifest to the scratchpad/brailleTables directory is enough to workaround #3470.

Manifest file example as found in the developer guide:

[brailleTables]
[[fr-bfu-tabmod-comp8.utb]]
displayName = French (unified) 8 dot computer braille - Tab mod
contracted = False
output = True
input = True
[[no-no-generic.ctb]]
displayName = Norwegian unofficial computer braille table
contracted = False
output = True
input = True

Example add-on demonstrating the new functionality (please rename .zip to .nvda-addon):
pr10712-customBrailleTables-demo-2021.05.08.zip

Testing strategy:

From a source copy:

  • added a new table via an add-on
  • added a new table via the scratchpad directory
  • replaced an existing table via an add-on
  • replaced an existing table via the scratchpad directory
  • altered existing table meta-data via an add-on
  • altered existing table meta-data via the scratchpad directory
  • ensured proper fallback when the user selected a new table which was provided by a later disabled add-on
  • ensured proper fallback when the user selected a new table which was provided by a later removed add-on
  • ensured proper fallback when the user selected a new table which was provided by the later disabled scratchpad directory

Known issues with pull request:

The current automatic translation of older tables names to newer tables names as of a liblouis update can lead to unexpected behavior if there is collision, but I do not think I am the one to take the decision when to drop this backward-compatibility piece.

Change log entry:

Section: Changes for developers
Add-on can now provide custom braille tables.

Code Review Checklist:

  • Pull Request description is up to date.
  • Unit tests.
  • System (end to end) tests.
  • Manual tests.
  • User Documentation.
  • Change log entry.
  • Context sensitive help for GUI changes.

Copy link
Copy Markdown
Collaborator

@LeonarddeR LeonarddeR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this huge and nice work! I think I like the switch from json to configobj.

Now I wonder, have you considered storing table data in the add-ons manifest itself? I think this has a major benefit, namely that the add-on manifest parser has support for language inheritance out of the box. This means that coming up with the right translated name for the table is the responsibility of the potential add-on translator, not of the initial author.

Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
Comment thread source/brailleTables.py Outdated
for addon in addonHandler.getRunningAddons():
yield os.path.join(addon.path, "brailleTables")

return [dir_ for dir_ in candidates() if os.path.isdir(dir_)]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this style. Do you think it makes sense to log if the isdir check doesn't pass?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would guess no.
Having a file named "brailleTables" is damn unlikely, and no one would reasonably expect its content to be interpreted.
The check could even be removed here and such a file would still not break the resolver.

Comment thread source/brailleTables.py Outdated
)


def _getCustomTableDisplayName(fileName: str, tableCfg: dict) -> str:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered making this a property on CustomTablesManifest?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, no.
The manifest is only one way to supply the config.
Another is to pass a dictionary to brailleTables.loadCustomTablesConfig (made public as of b87f5b2, see #10172 (comment))

Comment thread source/gui/settingsDialogs.py Outdated
@JulienCochuyt
Copy link
Copy Markdown
Contributor Author

JulienCochuyt commented Sep 9, 2019

Thanks for this huge and nice work! I think I like the switch from json to configobj.

You are very welcome. Thanks for you interest.

Now I wonder, have you considered storing table data in the add-ons manifest itself? I think this has a major benefit, namely that the add-on manifest parser has support for language inheritance out of the box. This means that coming up with the right translated name for the table is the responsibility of the potential add-on translator, not of the initial author.

The add-on manifest could indeed safely contain a "brailleTables" section with the needed configuration. brailleTables._getCustomTableDisplayName even handles the case where "displayName" is a key rather than a section - which fits this use case just fine.

My concern, though, is that ConfigObj does not support formally optional sections.
See DiffSK/configobj#131
This means that the "brailleTables" section could not be specified in the add-on manifest specification unless every single add-on manifest provides it. I guess neither of these alternatives is fine.

Anyway, an add-on can safely add a custom table during its initialization and have its display name translated as usual:
Eg: brailleTables.addTable("somefile.utb", , _("Some fancy name"), output=True, input=True, contracted=False)

Writing this I realize that brailleTables._loadCustomTablesConfig should have its leading underscore removed as the following is also a valid use for an add-on:

brailleTables.loadCustomTablesConfig({
    "file1.utb": {
        "displayName": _("Name of first table"),
        "output": True,
        "input": True,
        "contracted": True
    },
    "file2.utb: {
        "displayName": _("Name of second table"),
        "output": True,
        "input": True,
        "contracted": True
    }
})

EDIT:
Renamed _loadCustomTablesConfig to loadCustomTablesConfig as of b87f5b2.

@LeonarddeR
Copy link
Copy Markdown
Collaborator

@feerrenrut, could your share your thoughts about what you think would be best to specify metadata for custom braille tables?

@DrSooom
Copy link
Copy Markdown

DrSooom commented Sep 20, 2019

@LeonarddeR
Copy link
Copy Markdown
Collaborator

@JulienCochuyt wrote:

This means that the "brailleTables" section could not be specified in the add-on manifest specification unless every single add-on manifest provides it.

Is this true? Is there anything holding you back from providing a [brailleTables] section and a [[many]] section in there and providing a manifest that doesn't have this section? I gess the validator would just add the empty section just as it adds defaults for optional fields, such as readme.

@JulienCochuyt
Copy link
Copy Markdown
Contributor Author

@JulienCochuyt wrote:

This means that the "brailleTables" section could not be specified in the add-on manifest specification unless every single add-on manifest provides it.

Is this true? Is there anything holding you back from providing a [brailleTables] section and a [[many]] section in there and providing a manifest that doesn't have this section? I gess the validator would just add the empty section just as it adds defaults for optional fields, such as readme.

It at least is how I understood DiffSK/configobj#131 back then: You can't have a specified optional section. Unspecified sections are supported, but once they are specified, they become mandatory.
Nevertheless, I might try to play a bit more with ConfigObj to try ensure that point.

But anyhow, I doubt bundling a hundreds of tables in an add-on would ever be a reasonable use case, and keep in mind that registering the tables via simple code already allows translators to follow their usual workflow.

@LeonarddeR
Copy link
Copy Markdown
Collaborator

... keep in mind that registering the tables via simple code already allows translators to follow their usual workflow.

Problem with this is that there are multiple pathways to do exactly the same. I'd rather have only one public method of bundling tables in add-ons.

@LeonarddeR LeonarddeR closed this Sep 30, 2019
@LeonarddeR LeonarddeR reopened this Sep 30, 2019
@JulienCochuyt
Copy link
Copy Markdown
Contributor Author

... keep in mind that registering the tables via simple code already allows translators to follow their usual workflow.

Problem with this is that there are multiple pathways to do exactly the same. I'd rather have only one public method of bundling tables in add-ons.

Actually, the support for metadata .ini files was initially merely added to easily add custom tables in the scratchpad directory with no code - extending my personal use case of wanting my own modified fr-bfu-comp8 available when disabling all add-ons. Felt then like providing non-developer end users with as much icing on the cake as possible.
Thought it also provided an easy way to allow building of a minimal add-on with no code that would eg. add the requested no-no-comp table. I never considered an add-on author would really want to provide dozens of tables with localized names - but still made sure to support it.

We're in Python, there isn't a single way to do things, even if a compatriot of yours is told to be the one able to distinguish which is the right one :)

If I understand your point of view, it's hard to advertise the metadata .ini files way as it does not fit with the standard translation process. Though, I can remove it altogether if you are more comfortable with it.

@LeonarddeR
Copy link
Copy Markdown
Collaborator

Actually, the support for metadata .ini files was initially merely added to easily add custom tables in the scratchpad directory with no code - extending my personal use case of wanting my own modified fr-bfu-comp8 available when disabling all add-ons. Felt then like providing non-developer end users with as much icing on the cake as possible.

Mm yes, this is an interesting one. This requires a method that doesn't involve add-on manifests.

We're in Python, there isn't a single way to do things, even if a compatriot of yours is told to be the one able to distinguish which is the right one :)

This is true. However, I'd rather advertise one public method to do it, leaving it up to an add-on author to use another method that might be perfectly valid, but isn't the advised one. For example, if we decide that manifests is the way to go, add-on authors can still use the registration method, but they are encouraged to use the manifest method.

If I understand your point of view, it's hard to advertise the metadata .ini files way as it does not fit with the standard translation process.

Yes, this is true. However, I think it is up to either @feerrenrut or @michaelDCurran to crack the nut here.

Though, I can remove it altogether if you are more comfortable with it.

I'd say lets leave it as is until we have some more opinions about this.

@LeonarddeR
Copy link
Copy Markdown
Collaborator

Could either @michaelDCurran and @feerrenrut elaborate on what NV access' standpoint is with regard to what implementation to use to define custom tables? See #10172 (comment)

@feerrenrut feerrenrut added Addon/API Changes to NVDA's API for addons to support addon development. component/braille labels Apr 8, 2020
@JulienCochuyt
Copy link
Copy Markdown
Contributor Author

@seanbudd wrote:

There's some minor merge conflicts in the docs that need to be fixed as well.

Done as of f3be0f9.

@seanbudd seanbudd added the blocked/merge-conflicts Merge conflicts exist on this PR label Sep 22, 2021
# Conflicts:
#	source/addonHandler/__init__.py
#	source/braille.py
#	source/brailleInput.py
#	source/brailleTables.py
#	source/gui/settingsDialogs.py
Comment thread source/braille.py
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when resolving this merge conflict, I kept this line where it currently is in master. However, it might need to get moved to handlePostConfigProfileSwitch?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think it should be there? I don't think it needs to re-initialize after every config update?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only non-trivial change when resolving merge conflicts.
I thought was worth raising, in the case that we need to reload detections when changing braille tables.

@feerrenrut feerrenrut removed the blocked/merge-conflicts Merge conflicts exist on this PR label Feb 17, 2022
Copy link
Copy Markdown
Contributor

@feerrenrut feerrenrut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to need to come back to this next week.

[brailleTables]
# The key is the table file name (not the full path)
[[__many__]]
displayName = string()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is display name the name of the braille display, or the name of the braille table to be displayed in the GUI, or file name?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the name displayed in the GUI. The [[many]] map key is the file name.

This is based on my understanding of the user docs: https://github.com/nvaccess/nvda/pull/10172/files#diff-c9a23ef91eea3598e1128c9564a3a9b511d4941456ca31a24837e81c18b8b5caR853-R868, and the code a little further down this file https://github.com/nvaccess/nvda/pull/10172/files#diff-2fafc40e2d44037b654b987d85ce3c62746cc62261eb9c8dd59268a2d48a9494R768-R771

Comment thread source/braille.py
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think it should be there? I don't think it needs to re-initialize after every config update?

@feerrenrut feerrenrut added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Mar 18, 2022
@burmancomp
Copy link
Copy Markdown
Contributor

How is it with this pull request? I think this would be useful feature.

@seanbudd seanbudd added the stale PR is stale/old - NV Access has missed reviewing this or this is an abandoned draft label Apr 28, 2023
@seanbudd seanbudd self-requested a review June 20, 2023 01:51
@seanbudd seanbudd added merge-early Merge Early in a developer cycle blocked/merge-conflicts Merge conflicts exist on this PR labels Jun 21, 2023
@LeonarddeR
Copy link
Copy Markdown
Collaborator

@JulienCochuyt any chance you can bring this forward? If not, I think you did some very valuable work here and I'm happy to take this one. I'll do that if I'm not hearing back from you within a month or so.

@amirmahdifard

This comment has been minimized.

@Adriani90
Copy link
Copy Markdown
Collaborator

@LeonarddeR I think @JulienCochuyt is no longer that active on Github, at least I didn't see any updates from him since many years. Are you still thinking about taking this up?

@Adriani90
Copy link
Copy Markdown
Collaborator

Just a note, in my view unlike #3304 suggests, I would rather prefer an approach similar to speech dictionaries where user's dictionaries are stored in the user config and where users can edit the dictionaries in a GUI. . But I don't know if this is technically feasible. Nevertheless, this PR definitely would point things in the right direction at least.

@seanbudd
Copy link
Copy Markdown
Member

Hi - I'm going to mark this as a draft. I know that the reason this has been abandoned due to a delayed review from NV Access, however I do not see it in our near capacity to fix up the merge conflicts and prepare this for another review.
Given this is now the only item in our ready, unblocked, PR queue, I think we can be confident that this will be reviewed in a timely manner in future if further work is done.

@seanbudd seanbudd marked this pull request as draft January 30, 2024 03:19
@seanbudd seanbudd added the Abandoned requested reports or updates are missing since more than 1 year, author or users are not available. label Jan 30, 2024
@LeonarddeR
Copy link
Copy Markdown
Collaborator

I'm definitely taking this, since I might have a use case for myself in the near future. That's a pretty good stick behind the door if I may say so😉

@LeonarddeR LeonarddeR mentioned this pull request Feb 21, 2024
14 tasks
@LeonarddeR
Copy link
Copy Markdown
Collaborator

Superseeded by #16208

@LeonarddeR LeonarddeR closed this Feb 21, 2024
seanbudd pushed a commit that referenced this pull request May 7, 2024
Fixes #3304.
Fixes #9863.
Supersedes PR #9864, #10172.
Addresses #505 (comment)

Summary of the issue:
In NVDA, there is no easy and reliable way for an add-on to provide a new braille table. For an experienced users wishing to do so there are two options:

Alter manually an existing table in louis/tables.
The new table still has its original name in the settings GUI.
This change is lost upon NVDA updates.

Set the absolute path to the table file in the configuration in lieu of the usual file name.
The settings GUI shows an empty entry for this one.
Forces to manually alter nvda.ini.
Forces to copy in the same directory the whole dependency chain of the new table plus braille-patterns.cti. This is because liblouis default table resolver only looks for tables in a single directory, See Make it easier for add-ons to supply custom braille tables #5489 (comment)

Description of how this pull request fixes the issue:
Add a new brailleTables optional directory in both the user scratchpad directory and the add-on directory structure.

Support reading tables metadata from an optional brailleTables section of the add-on manifest or from a manifest.ini file with the same format found in the root of the scratchpad directory, allowing a user to provide a display name and set output/input/contracted capabilities with no code.

Implement a custom liblouis table resolver that resolves tables based on what is registered in the brailleTables module:

When liblouis calls the resolver without a base file specified, the table is looked up from the brailleTables module and either resolved from the add-ons brailleTables directory or the built-in tables directory
When liblouis calls the resolver with a base file specified (e.g. when processing includes in tables), the table is looked up from the folder of the base table and/or the built-in tables directory
Enforce the existing fallback mechanism to ensure there still is braille output if the configured table cannot be found e.g. because an add-on or the scratchpad directory was disabled. This now applies both to the main configuration and individual profiles and also covers braille input.

Note that if an add-on author wants a table to be listed in the GUI, he/she should always define the table in the manifest. Contrary to earlier incarnations of this pr, replacing a table in an add-on (i.e. when it has the same filename as a built-in table) without defining it in the manifest is no longer possible. Therefore it is also not possible to replace unlisted tables that are included by listed tables. For example, if you want to replace spaces.uti as included in nl-comp8.utb, you weel need to both define and bundle a replacement of nl-comp8.utb and spaces.uti in your add-on.
@CyrilleB79 CyrilleB79 removed the Abandoned requested reports or updates are missing since more than 1 year, author or users are not available. label Jun 18, 2024
@CyrilleB79
Copy link
Copy Markdown
Contributor

Since it has been superseded, I remove the "Abandoned" label.

@seanbudd I hope NV Access approves such triage actions; it should allow to filter Abandoned PRs for somewone wanting to take it over.

@seanbudd
Copy link
Copy Markdown
Member

Thanks @CyrilleB79

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Addon/API Changes to NVDA's API for addons to support addon development. blocked/merge-conflicts Merge conflicts exist on this PR blocked/needs-code-review component/braille conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. feature merge-early Merge Early in a developer cycle stale PR is stale/old - NV Access has missed reviewing this or this is an abandoned draft

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Braille: The fall-back input table does not match the default input table. Custom braille tables

10 participants