NVDA 插件开发文档

欢迎来到 NVDA 插件开发文档,此文档将一步一步的叙述如何进行插件开发,也将选出某些开发插件时常用的 NVDA 核心代码进行分析讲解。

更多有关于 NVDA 开发的信息,请访问 NvDA Community Development 页。请确认您对 [NVDA Developer Guide][2]有所熟悉,以了解关键术语及插件开发的基本知识。

读者

此指南除了面向的是初步了解 Python 或对 NVDA 的一般开发熟悉的新手,也包括专家、超级用户、熟悉其他语言甚至是了解 NVDA 源码结构的程序员。

如果您了解 NVDA 插件或了解 NVDA 的核心开发,我们建议您先去了解 Python,它将让您明白文档的剩余部分的必须背景。如果您是一个 Python 开发者而且了解 NVDA 开发,请查看 NVDA development Guide 和 Design Overview document两篇文档,这两个文档都可以在 NVDA community 网站找到。

作者, 贡献和版权

此文档起初由 Joseph Lee 编写,他因 NVDA 用户及开发社区而成长,我们欢迎您的反馈及贡献。

NV Access 版权所有 2006-2016 Microsoft Windows, Microsoft Office, Win32 API 和其他微软产品归微软公司所有。 IAccessible 包归 IBM 和 Linux 基金会所有。 Python 归 python 基金会所有, 其他提及的产品归该产品的作者所有。

系统要求

要想 为 NVDA 创建插件,请确保下述系统要求已经做好:

  • 在您的电脑上存有一个可用的 NVDA 安装版本或绿色版本(但是我们强烈建议您安装一个安装版本的 NVDA 到您的开发电脑上)。
  • Python 2.7 系列, 版本 2.7.5 32-bit for Windows.
  • SCons 2,版本2.3.0,用来生成插件包。
  • Markdown 2.0.1 或更高,用来生成插件文档。
  • GNU Gettext package for Windows,用来支持消息本地化。
  • Git 1.7.9 或更高,如果您想上传您的插件代码到仓库,如 Bitbucket (可选,请看下文)。您可以使用各种各样的 Git 客户端,如 Git Bash、 Cygwin's Git、 Tortoise Git 等等。
  • NVDA 社区插件的简易模板(用来打包和管理文件和目录)(可选),[点这里][3]下载插件模板。
  • 其他您的插件所需的 python 模块依赖。

什么是“插件”?

“插件”是一种扩展 NVDA 功能或程序支持的增强包。这可能包括增加全局特性、增强软件支持、对新的点显器或语音合成器的支持。

一个插件可能包括下面的一个或多个部分:

  • Global plugin:全局插件为 NVDA 增加全局可用的新特性, 如 OCR 识别。
  • App module: 程序模块可以对程序做增强支持,如某些程序的特定窗口或控件,如音频编辑器。
  • Driver: 驱动可让程序直接与硬件沟通,目前可谓点显器或语音合成器开发驱动。

每个 NVDA 插件都是由上述的一个或多个部分组成,它被打包成 zip 文件且扩展名被改为 nvda-addon 。NVDA2012.2版本之后可使用插件管理器进行安装,或者如果您在您的电脑上安装了 NVDA2012.3或其后的版本,也可以直接使用文件管理器进行安装。

在文档全文,我们参考了来自“NVDA 核心”的标准 NVDA Python 模块,从而对插件模块进行剖析。

安装插件开发环境

跟随下面的步骤为编写插件对您的电脑进行准备。

安装依赖

  1. 如果您没有 NVDA, 您可以从 NV access 网站或 NVDA 中文站下载 NVDA。
  2. 在您的电脑上安装 Python 2.7.x 32-bit (如果使用 Windows, 请使用 32-bit Windows 安装包)。
  3. 使用他们的 Windows 安装包 安装 Markdown 和 SCons
  4. 如果您计划和其他人分享您的插件代码,请安装 Git 客户端。
  5. 粘贴您的 GetText 可执行文件到您的开发目录(请查看下一张“插件开发目录”)。
  6. 如果您正在为某个程序、点显器或语音合成器进行支持,请安装所需软件和或硬件设备。

插件开发目录

编写插件的时候,建议把每一个插件的代码存放在不同的目录,一个目录一个插件。如果您选择下载插件魔板,那么目录结构将会被自动创建。

当您安装了所需的依赖(请看上文),粘贴 GetText 可执行文件到插件目录。

插件目录的结构

每一个插件目录,最少包含下面的文件和目录:

  • manifest.ini 用来存放插件的 manifest 信息,如插件名称、作者等。
  • 一个放在“addon”子文件夹且带有 “AppModules”“globalPlugins”“synthDrivers”“BrailleDisplay”子文件夹的模块插件子文件夹(译者注:addon/模块目录/brailleDisplay等,最后一级的目录请看原文),也可定制更多的模块文件夹。

如果您使用了插件魔板,插件的目录结构将被自动创建, 所以您只需创建“addon”子文件夹和包含插件本身的模块代码。请阅读放 template 的 readme 文件来获取使用魔板文件如何自定义插件的 manifest。

打包插件

有两种打包插件的方法:

  1. 手动打包您的插件,把您的插件目录压缩为zip文件并改变其扩展名为 .nvda-addon。
  2. 要想在 SCons 使用插件魔板,请使用管理员模式(Windows Vista 或以上)的命令提示符定位到您的插件目录然后输入“SCons”。

更多有关于插件管理的详情,请查看本指南的“管理”章节。

入门:动手事例

如此,您准备好开始编写插件冒险了,但不知道该如何让它“活起来”?请通读本章节,它将告诉您如何开始的一些基本信息,还将为编写代码给出一些提示。

请注意,本章节将不会使用真实的插件包,取而代之的是使用NVDA用户配置目录下包含着若干子目录的“plugin”目录(如果NVDA已经安装在电脑上,使用开始菜单或开始屏幕可访问该目录)来存放我们的例子 python 文件。

要想编辑一个 .py 文件,您必须拥有一个可以处理 .py 文件的文字处理程序,我们推荐的最好编辑器是 notepad++。

如何组织插件代码

您的插件代码将存放在一个或多个 python 文件(.py为后缀的文件)中,尽管一些不同种类的插件不是这样的,但他们都有非常相似的布局。

首先,您可以从编写一段可选的插件抬头开始,如您的名字 、一个简单的用来描述插件用途的句子或两个关键字等等。虽然它是可选的,但我们仍然建议您这么做,因为这样可以让我们知道您的插件的用途。

接下来, 您需要告诉 NVDA 您的插件所需的模块。您可以写成 “import 模块名称”的形式,如您想在您的插件运行过程中可以使用提示音,您可以通过“import tones”导入该模块。您可能还需要导入多个模块,,请参照上面的格式,用逗号分隔每个文件。通过查看下方的模块列表,您将知道您编写的插件需要什么类型的模块。

在声明了您需要货导入的插件模块之后,您可以开始编写您的插件了(如编写类、变量、方法等等)。最重要的是“插件类”的代码,他将决定您的插件锁归属的插件类型。

举例来说,如果您想添加一个程序的支持,您可以导入 appModuleHandler 和其他所需模块,之后您就可以开始编写了。

class appModule(appModuleHandler.AppModule):

在那以后,您编写的全部都是 python 代码(请查看 python 文档的“如何编写 python 程序”部分。)。

在此事例张杰运行您的插件

要想运行来自此张杰的您的插件,您可以打开您的 NVDA 用户配置目录(在开始菜单/屏幕查找 “浏览 NVDA 配置目录”项目),把您的 .py 文件放到恰当的目录:appModules 目录存放程序模块插件事例,globalPlugins 目录存放全局插件事例。

例1:按下“NVDA+a”的时候播放提示音

让我们从查看一个简单例子开始。如果您在任何程序按下“NVDA+A”,您可以听到一个长度为一秒的提示音。我们希望在任何地方都可以使用该插件,所以我们把代码放在 GlobalPlugins 目录下。

首先,打开您的用户配置目录,并进入 globalPlugins 目录,创建一个文件并赋予一个描述性名称(如 example1.py)(强烈建议您在给全局插件命名时,为您的文件添加一个短的、带有描述性质的文件名),然后用文字处理器打开这个新创建的 .py 文件。

下面的代码将实现我们的例子,请原封不动的把它放到 .py 的文件中去。

# Add-on development first example

import globalPluginHandler
import tones # We want to hear beeps.

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

    def script_doBeep(self, gesture):
        tones.beep(440, 1000) # Beep a standard middle A for 1 second.

    __gestures={
        "kb:NVDA+A":"doBeep"
    }

在 python 中,请把“#(警号)”放在评论行的开始处。

例 1代码解析

我们的第一个例子是按下“NVDA+A”时发出长度为1秒的提示音,您可能想知道代码的用途。接下来,让我们一段一段的进行讲解。

  1. 在文件顶部,我们写了一个标题,它告诉我们这是一个例子插件。
  2. 因为这是一个全局插件,我们需要导入最重要的模块“globalPluginHandler”,所以我们写成“import globalPluginHandler”。
  3. 然后我们写下“import tones”导入(表示加载货包含)这个 NVDA 的内建模块 tones,每当您决定使用某个模块,必须先将其导入。
  4. 接下来,我们定义了一个类 globalPlugin,括号中定义了这个类继承自哪里。编程中用到的类用来描述一个对象,如一个人、一张桌子、一个程序或其他。
  5. 在这个类里,我们定义了一个方法(或称函数)“script_doBeep”。这是一个脚本例子,我们按下某个快捷键后它就会被执行。在这个脚本里,我们写成“tones.beep(440, 1000)”,这句代码告诉 NVDA 发出一个长度为1秒的中音A。在边城中,一个函数可以用来放变量、设置、参数,它的用途取决于您给该方法赋予的值(接下来我们还会见到他们)。您会经常编写包括我们所说的“doBeep ”在内的、包含更多变量的许多方法,更多的脚本江在指南的旅行过程中提供。
  6. 最后,我们写了一个简单字典(一个集合)来存储与 doBeep 方法绑定的命令。这里,我们告诉 NVDA 使用“NVDA+A”来执行 doBeep 脚本。

保存这个文件并重启 NVDA,不论您在什么时候按下“NVDA+A”都可以听到一个长度为1秒的提示音。如果您认为您的插件代码和布局已经很合适,您可以把最近创建的 .py 文件删除。

我无法理解上面的术语

对于某些人,类和方法等术语可能是全新的事物。让我们来看看他们的意义,因为在插件的开发过程中非常重要:

  • 类:一个类可以描述任何事物,例如一个人、一张桌子、一个插件或其他。类对于 NVDA 和其他程序来说都是非常重要的—大量的程序员正在提高使用类方面的技巧。
  • 方法:方法是一段短小的程序或在软件执行过程中一个用来执行例行任务的程序。如发出提示音,计算人数,加载插件等等。有的人也把他们称为“函数”。
  • 脚本:脚本是一个当用户执行特定动作(如在键盘上按下某个特定的按键)就会执行的方法。如,当您按下“NVDA+F12”后 , NVDA 将执行来自 NVDA 核心的模块 Global Commands 的 dateTime 脚本。这个脚本有两个变量:在哪里执行(通常是“self”,详情稍后讲解)和使用什么首饰后执行(请看下方)(译者注:所谓的首饰大体包括按键和触摸首饰两类)。
  • 变量:变量是一种可以修改的事物,如一个人的名字、您正在运行的NVDA插件的名称或您正在使用的 NVDA 版本等等。一个插件可能定义很多的变量,如用来存储共用常量(字串)等。
  • 模块:模块把方法和变量集合在一个文件里。我们现在在编写的插件,其实就是一个 NVDA 运行时可用的附加模块。

这里还有一些术语我们需要简单了解。

例2: 切换到记事本后发出提示音

下面的大多数代码都来自 NvDA Developer Guide。

NVDA 不仅允许您编写全局插件,他还允许您通过编写程序模块来提高对某个软件的支持。模块名称除了表示着可执行的 python 文件之外,还代表着某个可执行程序。如,为“notepad”开发的模块就被命名为“notepad.py”。

下面的代码(来自 NVDA developer Guide)给出了一个典型程序模块的小例子:当切换到记事本的时候发出一段很短的提示音。为了使它运行,请把代码放到“notepad.py”里,并把文件放在用户配置的 appModules 目录下。

# An example app module.

import appModuleHandler
import tones

class AppModule(appModuleHandler.AppModule):

    def event_gainFocus(self, obj, nextHandler):
        tones.beep(256, 200)
        nextHandler()

例2代码解析

我们在这里看到了更多的新代码。让我们再一次逐段进行分析:

  1. 不像第一个例子,这次我们所需的最重要模块是 appModuleHandler。
  2. 我们使用的类是 AppModule。
  3. 不同于上次,我们这次使用的是“事件”,极只在某种事件发生后(如控件名称的更改)才执行该方法。“事件”把一个“对象”作为变量,“对象”极是每个“事件”都需要处理的事物,或是某些人所称的“击发(fired)”。
  4. 在 event 方法里,我们还看到了被称为“nextHandler”的方法。放在 event 的这个方法用来告诉 NVDA 监视这个事件的发生,如在发出提示音之后读出控件的名称。

更多新的术语

您可能看到的其他术语包括:

  • 事件:事件是一种仅当某个事件发生候才会运行的方法。如一个程序拥有焦点或控件名称发生变动等。
  • 调用:我们说一个方法(方法2)使用其他的方法(方法1)就被称为“调用”。如,在我们的第一个例子里,我们就在 script 方法里调用了 tones.beep 方法。
  • 对象(object):对象就是类的实例,极当程序运行时“类”拥有了生命。当您编写类病运行您的插件的时候,您的类就被具象化为“对象”(通常简写为“obj”)。对于 NVDA 来说,对象可能用来访问控件或程序的一部分。

  • self:对于 python 来说,“self”表示的是当前类(如您在写插件的时候定义的类,还表示着这个方法属于哪个类)。如,我们创建了一个“numbers”类,而这个类的方法“add”的第一个参数就是“self”。这样告诉我们:这个方法“add”是类“numbers”的一部分。在 NVDA 的开发世界里,“self”通常指的是 NVDA 的当前“对象(请看下面)”,对于插件开发来说,self指的是这个插件的实例。您的许多方法将会把“self”作为您方法的第一个参数。(译者注:这里可能显得有些乱,下面的两个方面,是从宏观和微观两个角度去解释self的含义:self既是对象(宏观),也是插件实例(针对插件开发的微观),希望这段解释没有添乱)。

和例1相同,您可能希望在代码已经符合需求的情况下把他移除,除非您希望在切换到记事本的时候听到一个提示音。随着我们在本指南中对他们的进一步探讨,更多有关于全局插件和模块插件的实际差异将会变得更加清晰。

一些给初学者的提示

这里有一些给插件开发者的实用提示:

  • 从编写一个简单的插件开始,例如输出一条消息,发出提示音等等。
  • 编写方法和测试方法这两个过程在同时进行。
  • 如果您正在编写程序模块或驱动,而且对于编程更加熟悉了,可以尝试使用语音合成器或点显器进行输出(如阅读文档等)。
  • 在定义一个命令以前(特别是全局插件),请首先对 NVDA 和其他插件所使用的命令进行考察,以避免发生命令键冲突。

来自 NVDA 核心的实用模块

贯穿整个插件开发周期,您可能会碰到一些来自 NVDA 核心的、对于您的插件代码非常有帮助的有用模块。这个章节介绍他们和他们的一些实用函数。

实用的 NVDA 核心模块和方法列表

下面罗列了可用的 NVDA 核心模块和一些从他们那儿找到的实用方法。

  • 插件控制器 (addonHandler.py): 这个模块使得插件子系统可以正常工作。方法“addonHandler.initTranslation()”可以为您的插件进行国际化支持的初始化。
  • NVDA 基础 API (api.py): 整个 NVDA 都在使用的核心方法的集合,如获取焦点和可浏览对象,设置焦点等等。请查看下一个列表中属于该模块的实用方法。
  • 程序模块子系统(appModuleHandler.py和程序模块们):子系统负责控制程序模块(更多详情,请查看章节“程序模块”获取)。
  • ARIA 支持 (aria.py): 掌控访问富互联网应用(ARIA)的支持。
  • 基本对象集合(baseObject.py): 包含实用的对象,如可编程对象(请查看 NVDA 对象和覆盖对象章节获取更多信息)。
  • 点字输入输出子系统(braille.py, brailleInput.py):控制点显器盲文的输入输出,需要点显器驱动插件的支持。
  • 内建模块(builtin.py):允许在插件工作时访问内建模块。
  • 配置(config): 管理配置和配置文件(配置文件在 2013.3 后可用)。
  • 控件和状态集合(controltypes.py): 包含控件类型的字典(角色)和可能被插件套用的状态。
  • 事件(eventHandler.py): 控制各种各样的事件,如正在获取焦点。
  • 全局命令集合 (globalCommands.py): 使用 NVDA 时可用的全局命令的列表(请查看脚本作用域章节获取更多信息)。
  • 全局插件子系统(globalPluginHandler.py):控制全局插件必备的模块。
  • NVDA 图形用户界面(gui): 一个 NVDA 用来以图形方式展示消息的类的集合。 包括 NVDA 使用的菜单、插件管理器和其他界面。
  • 硬件端口工具(hwPortUtils.py): 一套用来与串行端口或其他硬件端口进行通讯的工具。它对驱动插件的开发非常有用。
  • IAccessible 支持 (IAccessibleHandler.py和可访问对象): 用于支持 IAccesible 控件。
  • 输入管理(inputCore.py):管理用户输入。
  • Java Access Bridge 支持 (JABHandler.py):一套为 Java 程序支持 JAB 子系统的函数集合。
  • 键盘输入 (keyboardHandler.py): 对键盘命令输入进行支持。
  • 日志工具(logHandler.py): 一个用来输出日志的模块,允许开发者直接查看,用户也可以使用“日志查看器”进行查看。
  • 鼠标支持(mouseHandler.py): 支持鼠标命令。
  • NvDA 对象集合 (NVDAObjects): 一个 NVDA 对象的集合,包含许多程序常用的控件和标准(如UIA(自动用户界面))。
  • 脚本支持 (scriptHandler.py): 控制脚本、方法因应用户的键盘或其他输入的执行。
  • 语音输出 (speech.py): 控制语音输出
  • 合成器驱动支持(synthDriverHandler.py): 这是合成器插件必备的核心模块。
  • 小部件文本访问(textInfos): 允许访问小部件和文档里的文本。
  • 触摸屏支持(touchHandler.py): 提供对触摸屏输入的支持(仅安装版可用)。
  • 提示音输出(tones.py): 可允许用户听到提示音。
  • 用户界面消息(ui.py): 包含 用语音或点字输出的方法“ui.message”。
  • 虚拟缓冲区(virtualBuffers): 控制虚拟缓冲区的文档(如网页)。

没有扩展名“.py”的都是目录,包含的是专用模块。

实用的方法

这里列举了插件常用的方法。有关于如何调用的更多信息,请查看 NVDA 源码文档。编写的例子,请查看“插件组成”章节。

来自 addonHandler:

  • addonHandler.initTranslation(): 在您的插件上安装翻译子系统并使用 Gettext。

来自 api.py:

  • api.getFocusObject(): 检索有焦点的控件(返回有焦点的对象)。
  • api.getNavigatorObject(): 补货当前的浏览对象。 如果 NVDA 设置成“跟随系统焦点移动”,那么焦点和导航对象的返回相同,否则将返回不同的对象。
  • api.getForegroundObject(): 返回当前程序的前景窗口(对象的 parent 就是程序本身)。
  • 还有相应的方法设置当前对象为焦点对象或导航对象。

来自 logHandler:

  • logHandler.Log:一个控制日志输出的类。

来自 tones:

  • tones.beep(表示音高的赫兹数,持续的毫秒数,左通道音量,右通道音量): 按照给定的音高和时常播放提示音。前面的两个参数是必须的,而另外的两个是可选参数。

来自 ui:

  • ui.message(需要语音或点字输出的消息): 用语音或点字输出该消息,此参数必须是带有引号的字串。

除此之外,还有其他有用的方法,但是上面提到的就是最常用的。查看 NVDA 源码文档可以获知其他方法的信息,或者查看下方的例子学习如何在插件的开发生命周期使用方法和其他东西。

插件模块的组成和开发提示

一个插件由很多成分组成。包括控制输入输出、在不同的 NVDA 对象下工作、响应事件、存储配置或更多。

这一章节介绍了插件开发过程中的关键组成和概念。如 NVDA 对象、脚本、事件控制及其他主题,并付上了对应的例子。

请注意: NVDA 核心开发指南中介绍了下面的概念。这一章节只是那个文档的扩充。查阅 NVDA development guide 可获得概述。

在屏幕上的控件工作

一个对象就是一个“类”的“实例”,每当程序运行的时候,类就有了生命。如定义了一个“按钮”类,屏幕上的按钮就是这个“按钮”类的对象。

在 NVDA,一个“对象”表示了一个控件货程序的一部分。这包括按钮、复选框、编辑框、工具栏、滑块甚至是程序窗口。按照控件的父子关系--有些对象可能包含其他紫对象--,他们备分层组织。如 Windows 资源管理器的列表对象可能包含一个货多个列表项目对象。而这个列表的父对象可能就是 Windows 资源管理器窗口。您现在锁了解的对象就是“导航对象”。

NVDA 对象(货简单的称为对象)带有大量的实用特性和属性。包括对象名称、它的值(已选择、编辑窗口的文本等)、角色(复选框、窗口、嵌入式对象等)、位置(屏幕坐标)货更多。NVDA 对象还包含一些操作他们的方法,如修改对象的值、响应对象的事件(获取焦点、值的更新等)诸多动作。

在许多场景下,NVDA 的对象可能从属于相关对象的类。对于每个对象类, NVDA 都提供了控制方法。这些类包括 IAccessible、 JAB、 UIA 等等。这些类及对象类的行为都备定义在 NVDA 源码的 NVDAObjects 目录中。要想在您的插件中使用,只要导入您锁使用的对象的对象类控制器即可。(如您正工作在一个 IAccessible 对象,那么可以导入 NVDAObjects.IAccessible)。

这些中的两个对象类应该特别提到:虚拟缓冲区和树拦截器。树拦截器允许 NVDA 工作在“树”对象中,树对象看起来就像只有一个对象。虚拟缓冲区就是一个特别的树对象,它允许 NVDA 在复杂的文档(如 PDF 文档)工作。

检查对象层级

这里有许多方法可以查看给定程序对象的所属层级:

  1. 在简易浏览模式关闭的前提下使用对象导航命令(NvDA+数字键盘2/4/5/6/8)。
  2. 在 python 控制台,使用 obj.next/previous/parent/firstChild/lastChild 属性。如果您想查看所有可用的属性,在 Python 控制台输入“dir(obj)”。

如果您想查看浏览对象的更详细描述,且导航对象已经定位到您锁感兴趣的对象,请按“NVDA+F1)运行日志查看器查看。所有对象的根就是桌面货外壳对象(译者注:所谓“根”通常指的是第一层)。

焦点 vs. 导航对象

在您的插件里,您可能会工作在并控制着各种各样的对象。这可能包括改变聚焦对象、同步导航对象和焦点对象、修改一个对象的角色等等。

焦点对象就是当前获得焦点的对象。控件连接着键盘焦点--就是说,焦点跟随着高亮的控件。与此不同,导航对象就是您感兴趣的对象。因为导航对象可以移动到任何地方。您可以同时查看聚焦对象和浏览对象这两个对象。 例如,您可以在焦点处于一个编辑框的同时使用导航对象查看标题栏。

在您的插件里,要想获取有焦点的对象,编写 someObj = api.getFocusObject()。 someObj 可以使用不同的名字——习惯上使用名称 “Obj”。要想获取导航对象(它可能雨焦点对象不同),可使用obj = api.getNavigatorObject()

其他有用的对象——相关的优点

这里有一些 NVDA 对象工作时可用的其他实用方法,他们都备放置在模块“api.py”:

  • 如果您想获取前景对象(如您想查看前景窗口的子对象,这是非常有用的),使用 obj = api.getForegroundObject()
  • 在 Python 控制台查看某对象包含的子对象(如紫成员货前景窗口的小部件等)的数目,输入obj.childCount。值为0说明已经没有更多的紫对象了。
  • 要想设置某些对象为新的有焦点对象货浏览对象,可使用 api.setFocusObject(obj)api.setNavigatorObject(obj)
  • 您可以通过 obj.property 获得对象的各种属性,property就是您想查看的属性(如 obj.values)。

例1:查找程序滑块的值

假设您的用户要求您使用程序模块查看一个程序滑块的值,在查看对象的层级和其他属性之后,您发现这个工具栏还是前景对象的最后一个子成员。

这里是提供此特性的代码:

# Object example 1

import api
import appModuleHandler

class AppModule(appModuleHandler.AppModule):

    sliderChildIndex = -1 # The variable to store the child index.

    def getSliderValue(self):
        fg = api.getForegroundObject()
        sliderVal = fg.children[self.sliderChildIndex].value
        return sliderVal

在此代码中,方法fg.children[index]用来取回给出索引的子成员(这里,我们设定工具栏是最后一个子成员,因而索引号是 -1,货对于非常后面的成员,我们可以使用fg.lastChild)。

可能,这份代码有一个问题:万一如果滑块的值实际上包含在滑块的第一个子成员控件怎么办?解决这个问题的其中一个办法就是检测对象角色。修改的代码看起来像这样:

def getSliderValue(self):
    from controltypes import ROLE_SLIDER # It is possible to import from within a method.
    fg = api.getForegroundObject()
    slider = fg.lastChild
    if slider.role == ROLE_SLIDER: return slider.firstChild.value

就此,我们知道并确定经过我们的处理,函数可以返回滑块的第一个子对象的值(如果存在这种情况的话)。请注意:两个等号表示相等,只有一个等于号表示分配。

这里还有其他的例子可以帮助您熟悉导航对象和操作:

  • 获取不再这个程序而在其他地方的对象的名称。
  • 移动导航器到前景对象。
  • 设置焦点到其他程序。

关于 NVDA 对象的更多现实例子,可以查阅 NVDA 源代码货其他社区插件。

运行时的专门对象货覆盖对象的属性

有些时候,只在控件用默认动作工作是不够的。如某些程序的部分可能需要自定义的手势, 或者需要修改某个控件的窗口角色为按钮等。

NVDA 提供两个方法来创建专门对象或覆盖对象(也可能是类),每一个都是为了不同的需求而配备的:

  • event_NvDAObject_init(self, 您正在处理的对象): 如果您希望替换某个当前控件的属性,如角色货标签(名称),您可以使用这个方法在 NVDA 第一次遇到这个控件货初始化的时候把您的“输入”放入“account”。例如,如果一个控件拥有窗口类TForm (曾经在许多Delphi 程序见过), 您可以通过 bj.role = ROLE_WINDOW 要求 NVDA 把它视为窗口。(查看控件类型词典获取可用角色的列表)。
  • chooseNVDAObjectOverlayClasses(self, 对象, 类表):这个方法允许 NVDA 用您的逻辑处理某些控件。例如,您如果想在您的程序模块中为程序的某部分设计特殊的手势,这是非常有用的(事实上,很多程序模块为了处理程序的特定部分而创建对象,然后在遇到某些状况的时候使用 chooseNvDAObjectOverlayClasses 选择正确的对象)。我们必须基于固有的对象来处理这些对象(大多数时候, Accessible 已经足够,因为大多数的覆盖对象都继承自此或是 IAccessible 的子成员货专门的类)。

请注意,对于第二个方法,给定的类名必须放置在文件内。他们备继承字一个众所周知的基本对象(在 Python, 继承的语法是 子类(基类), 通常也可以说成“这个子类继承自这个基类”。我们将在后面看到这样的代码)。

覆盖类和修改角色的例子

下面的例子说明了我们在上面讨论的覆盖和更改属性这两个方法的用法:

第一个例子:修改属性。

# Reassign some Delphi forms as window.
    def event_NvDAObject_init(self, obj):
        if obj.windowClassName == "TForm": obj.role = ROLE_WINDOW

这样的意思是,每当我们遇到的窗口类名为“TForm”的窗口时, NvDA 会把他当作普通窗口对待。

例子2:使用带有两个对象的程序模块处理程序的特定部分,然后使用 chooseNVDAObjectOverlayClasses 来自定义每个控件的逻辑。

#An example of overlay classes

class enhancedEdit(IAccessible):
    # Some code to be run when window class name is MyEdit.

class MainWindow(IAccessible):
    # Another code, this time adding custom gestures for main window of the program.

# In the app module:

def chooseNVDAObjectOverlayClasses(self, obj, clsList):
    if obj.windowClassName == "myEdit": clsList.insert(0, enhancedEdit)
    elif obj.windowClassName == "TWindow": clsList.insert(0, mainWindow)

对于这两个函数,我们希望检查的对象必须插入到“clsList”的第一项。 这样做的好处是在为对象查找手势和代码(行为)时这些自定义对象可以获得优先权。且在开发信息中,显示导航对象的MRO(方法解决顺序)时江首先显示这些自定义控件。

输入和输出:脚本和 UI 消息

插件的另外一个组成部分就是获取用户命令和显示插件正在做什么。这些可以通过脚本(输入)和 UI 消息(输出)完成。

脚本是一个当用户执行某个动作而执行的方法。例如,如果您按下“NVDA+T”,NVDA 会执行存放在全局命令模块的脚本“SayTitle”。在 Poedit 中,译者按下“Control+Shift+A”时, NVDA 会朗读油程序员添加的注视以帮助理清可翻译字串的用意。这不是原本的 NVDA 命令,它备添加在 Poedit 的程序模块当中以执行此功能。

通常来说,可接收脚本的插件都会拥有一个命令列表:它就是放置在某个地方的功能表。最简单的就是手势(命令)词典, 极 python 词典(通常备命名为 __gestures),它可以容纳命令(作为 keys)和脚本(作为 values)(如果命令货 key 多余一个,则可以捆绑为脚本)。 这些词典江在 插件加载时载入,将在 NVDA 退出货程序模块所属程序丢失焦点时清空(就是说,用户切换到了另外一个程序)。

另外一种捆绑脚本的方法就是基于运行时的插入。可以通过创建与 __gestures 分离的其他手势词典完成,它可以用来存储对上下文敏感的手势。如操作一个单独的控件。接下来开发者会使用 inputCore.bindGesture (如果定义了多个脚本货手势则使用 inputCore.bindGestures )来进行定义。最后在 inputCore.bindGestures 后使用 inputCore.clearGestures 来移除已添加的手势。更加优雅的方法涉及到特定对象的脚本,江在我们讲述覆盖类时讲到。

NvDA 支持从键盘、点显器货点显器键盘和触摸屏输入。

例子2:基础脚本字典

在这个例子中,我们设计了“sayHello”和“sayGoodBye”两个脚本并绑定到单独的手势中去。

# An example fragment for script assignment.
import ui

def script_sayHello(self, gesture):
    ui.message"Hello!")

def script_sayGoodBye(self, gesture):
    ui.message("Good Bye!")

__gestures={
    "kb:control+NVDA+1":"sayHello",
    "kb:Control+NVDA+2":"sayGoodBye"
}

现在,当您按下“Control+NvDA+1”,NVDA 会说“Hello”,当您按下“Control+NvDA+2”,NVDA 会说“Good bye”。

例子3:脚本基于运行时的插入和移除(译者注:内容暂时就到这儿为止了,期待日后官方的更新吧。另外有几个地方网址没能正常显示也是原文档的bug。翻译有何不妥,欢迎随时与我联系,请访问 NVDA 中文站获取更多 NVDA 的中文资讯和插件。电邮:vgjh2005@gmail.com,非常感谢您的阅读)。

# Future sections #

Please delete this notice when appropriate sections are done.

Add-on components and development tips

Includes introductions to input and scripts, output systems, objects, events, configuration, add-on settings and reloading plug-ins. Also includes some tips on add-on development such as debugging. It concludes with some useful examples and do's and don'ts.

Planned sections (please feel free to contribute your knowledge in this section):

  • Introduction to NVDA objects.
  • Examining object attributes with Python Console.
  • Fetching and setting objects.
  • Object hierarchy and differences between regular and simple review modes.
  • The event_NVDAObject_init and chooseNVDAObjectOverlayClasses methods.
  • Events and list of available events.
  • Next handlers.
  • Input from keyboard, braille displays, mouse and touchscreen via scripts.
  • Script lookup process and conflicts.
  • Static and dynamic script bindings, gesture dictionaries and script categories.
  • Braille, speech and tone output.
  • Debugging add-ons.
  • If something goes wrong (common errors and exceptions).
  • Few working and non-working examples for each topic.
  • These plan sections may change.

Global Plugins

A chapter devoted to global plugins.

Planned sections:

  • What exactly is global plugin.
  • Importance of consulting NvDA and add-on commands to minimize command conflicts.
  • When not to use global plugins.
  • A few worked out examples.
  • These sections may change.

App Modules

A chapter devoted to app modules.

Planned sections:

  • What is an app module.
  • App module scripts.
  • Defining objects representing different parts of a program.
  • Silencing NVDA for a program.
  • How app developers can help NVDA users through accessible app designs.
  • A few worked out examples and examples from existing app modules from NVDA core and from community.
  • These topics may change.

Drivers

A chapter devoted to driver development.

Misc items

Includes Bitbucket repo, add-ons lisst, other topics and contact information.

https://community.nvda-project.org/documentation/developerGuide.html