张赐荣——一位视障程序员。
赐荣小站: www.prc.cx

張賜榮

张赐荣的技术博客

博客园 首页 新随笔 联系 订阅 管理

解读NVDA读屏软件的设计架构和技术体系

NVDA(NonVisual Desktop Access)是一个免费开源的Windows屏幕阅读器,通过语音合成或盲文显示的方式,使视障人士能够使用计算机。它由澳大利亚的非营利组织NV Access和世界各地的贡献者共同开发。本文将尝试从技术角度介绍NVDA的设计框架和技术体系,包括以下几个方面:

编程语言

NVDA主要使用Python开发,这样可以提高开发效率和灵活性。需要注入到其他进程中的代码则使用C++编写,以提高性能。NVDA还使用了一些第三方库和组件,如wxPython(用于图形用户界面)、liblouis(用于盲文转换)、eSpeak(用于语音合成)、IAccessible(用于辅助技术接口)等。

辅助技术接口

为了使图形用户界面中的控件(如按钮、编辑框、列表框等)对屏幕阅读器等辅助技术可访问,操作系统和应用程序可以使用专门的辅助技术接口(Accessibility API)。这些接口提供了控件的信息,如名称、类型/角色、描述、值、状态(如选中、不可用、不可见等)和快捷键。辅助技术接口还提供了事件,以便辅助技术能够监测变化,如焦点变化、对象属性(如名称、描述、值和状态)变化等。丰富的辅助技术接口还提供了更多信息,包括访问和跟踪可编辑文本控件中的光标的详细信息,以及表格信息,如行列坐标。

NVDA在很大程度上依赖于辅助技术接口来收集信息。它使用了多种辅助技术接口,包括Microsoft Active Accessibility (MSAA)(也称为IAccessible)、IAccessible2、Java Access Bridge和UI Automation。其中IAccessible2是由IBM主导的一种扩展了MSAA的辅助技术接口,它提供了更多功能,如表格支持、超链接支持、嵌入对象支持等。

NVDA架构

NVDA是一个基于事件驱动的应用程序,它主要由三个部分组成:核心(core)、NVDA Helper和驱动程序(drivers)。

核心是NVDA的主要部分,它负责处理用户输入、监测系统事件、获取对象信息、处理文本和控制输出。核心是一个单线程的循环,它不断地从一个队列中获取事件或命令,并执行相应的操作。核心还负责管理NVDA对象模型和NVDA脚本系统,这些将在后面介绍。

NVDA Helper是一组C++编写的动态链接库(DLL),它们被注入到其他进程中,以便获取更多信息或执行更多操作。例如,NVDA Helper可以监测键盘输入、鼠标移动、窗口创建或销毁等低级事件,并将它们发送给核心。NVDA Helper还可以通过各种辅助技术接口获取对象信息,并将其转换为统一的NVDA对象模型。另外,NVDA Helper还可以执行一些特殊的任务,如在屏幕上绘制焦点矩形或光标跟随等。

驱动程序是一组Python编写的模块,它们负责与不同类型的设备进行通信,并将核心的输出转换为相应的格式。例如,有语音合成驱动程序、盲文显示驱动程序和手持设备驱动程序等。

NVDA对象模型

NVDA对象模型是NVDA用来表示用户界面中各种控件的抽象层。每个控件都对应一个NVDA对象,它包含了该控件的基本属性和方法,如名称、角色、状态、位置、子对象、父对象、下一个对象、上一个对象等。NVDA对象还可以响应一些命令,如报告自身信息、执行默认动作、移动焦点等。

NVDA对象模型是分层的,每一层都对应一种特定类型或来源的控件。例如,有基类NVDAObject、应用程序类AppModule、窗口类Window等。每一层都可以继承或重写上一层的属性和方法,以适应不同情况下的需求。例如,文本类TextInfo可以重写基类NVDAObject的text属性,以返回文本内容而不是名称。

NVDA对象模型是动态创建的,即当用户访问某个控件时,才会创建相应的NVDA对象,并根据需要缓存或销毁之。创建NVDA对象时,会根据控件来源选择合适的类,并调用其构造函数进行初始化。例如,如果控件来源于MSAA接口,则会选择MSAA类,并传入相应的IAccessible对象作为参数。

NVDA脚本系统

NVDA脚本系统是NVDA用来根据用户输入或系统事件执行不同操作的机制。每个操作都由一个脚本函数(script function)实现,它可以获取或修改当前焦点或导航对象的信息,或者调用核心或驱动程序的功能来输出信息或控制设备。

脚本函数通常与一个或多个按键组合(gesture)关联,当用户按下相应的按键时,就会执行该脚本函数。例如,“报告当前行”脚本函数与“NVDA+L”按键组合关联,在文本控件中按下该按键时,就会朗读当前行的内容。脚本函数也可以与其他类型的手势关联,如鼠标点击、触摸屏点击或盲文显示按键等。

脚本函数可以定义在任何类中,但通常定义在与控件类型相关的类中,以实现特定的功能或覆盖通用的功能。例如,“报告当前行”脚本函数定义在TextInfo类中,它可以根据文本控件的特点获取并朗读当前行的内容。

NVDA插件机制

NVDA插件机制是NVDA用来扩展和定制功能的一种方式。NVDA插件是一些可以下载和安装到NVDA中的额外的包,它们可以增强现有的功能或添加新的功能。这些插件可能包括一些可以在任何地方使用的额外的功能和命令,增强对某个程序的支持,或者添加对新的盲文显示或语音合成器的支持。

NVDA插件分为三种类型:应用模块(appModule)、全局插件(globalPlugin)和驱动程序(driver)。

  • 应用模块是针对特定程序或窗口的插件,可以定义一些特定的NVDA对象、脚本函数和事件处理器,以适应该程序或窗口的特点。

  • 全局插件是针对整个系统的插件,可以定义一些通用的NVDA对象、脚本函数和事件处理器,以提供一些全局性的功能。

  • 驱动程序是针对特定类型的设备的插件,可以定义一些与设备通信和转换输出格式的方法,以支持新的盲文显示或语音合成器等。

NVDA插件使用Python编写,并遵循一定的规范和要求。NVDA插件通常包含一个manifest.ini文件,用于描述插件的基本信息,如名称、版本、作者、兼容性等。NVDA插件还包含一个或多个Python文件,用于定义插件类、NVDA对象类、脚本函数等。NVDA插件还可以包含一些其他文件,如文档、语言文件、声音文件等。

NVDA插件可以通过NVDA的管理插件对话框进行安装、卸载、启用或禁用。安装插件时,只需选择下载好的插件文件,并确认安装即可。卸载或禁用插件时,只需选择已安装的插件,并选择相应的操作即可。安装或卸载插件后,需要重启NVDA才能生效。

结语

本文简要介绍了NVDA屏幕阅读器的技术设计和架构,包括编程语言、辅助技术接口、NVDA架构、NVDA对象模型、NVDA脚本系统和NVDA插件机制等方面。希望能够帮助你更好地理解和使用NVDA,以及参与到NVDA的开发和贡献中。

参考资料:

posted on 2023-05-26 09:59  张赐荣  阅读(113)  评论(0编辑  收藏  举报

感谢访问张赐荣的技术分享博客!
博客地址:https://cnblogs.com/netlog/
知乎主页:https://www.zhihu.com/people/tzujung-chang
个人网站:https://prc.cx/