아티클

NVDA 추가 기능 개발 가이드라인 4부 - 글로벌 플러그인 제작

2019-05-25 00:39:05

안녕하세요. 엔비전스입니다.

지난 NVDA 추가 기능 개발 가이드라인 3부에서는 NVDA 추가 기능 개발 시 알아야 할 이벤트, 음성을 출력하는 방법 등 추가 기능 개발의 기본 구성 요소에 대해 살펴보았습니다. 이번 아티클에서는 NVDA 스크린 리더를 위한 글로벌 플러그인 제작 방법에 대해 다룹니다. 아울러, 글로벌 플러그인을 추가 기능 패키지(*.nvda-addon)으로 패키징하는 방법에 대해 살펴보겠습니다.

 

들어가기 전에

  1. 필요시 NVDA의 사용자 환경 폴더를 열어 잡업을 진행해야 할 수도 있습니다. NVDA 사용자 환경 폴더에는 추가 기능을 포함한 NVDA의 각종 설정값이 저장되어 있는 폴더입니다. NVDA 사용자 환경 폴더를 여는 방법은 NVDA 추가 기능 개발 가이드라인 1부 <추가 기능이 저장된 폴더 열기> 섹션을 참고합니다.
  2. NVDA는 Python 언어로 추가 기능을 제작할 수 있습니다. 이번 아티클에서 사용한 NVDA에 내장된 클래스, 메소드 왜 Python 언어의 문법, 튜토리얼 등의 자세한 내용은 <Python 공식 문서 웹사이트>를 참고하세요.
  3. NVDA에 기본으로 포함된 추가 기능을 위한 모듈에 대해서는 NVDA 추가 기능 모듈 문서를 통해 내장 모듈, 클래스 및 메서드에 대한 설명을 확인할 수 있습니다.

용어 설명

이번 아티클에서 사용된 주요 개념 및 용어는 다음과 같습니다:

  • Python의 list 자료형: 여러 자료를 하나의 변수로 관리하기 위해 사용되는 자료형이며, 리스트에 속한 값을 변경하거나 삭제할 수 있습니다. list를 만들려면, listName = [1, "a"]과 같이 만듭니다. 초점 객체 및 탐색 객체의 객체 정보를 관리하기 위해 사용되었습니다.
  • Python의 집합(set) 자료형: 여러 자료를 하나의 변수로 관리하기 위해 사용되는 자료형이며, list 자료형과 달리 중복된 데이터를 가질 수 없고, 순서를 가질 수 없습니다. 초점 객체와 탐색 객체의 객체 상태는 집합 형태로 저장되므로, 상태 값을 관리하기 위해 list로 변환되어 사용되었습니다.

글로벌 플러그인이란?

글로벌 플러그인은 NVDA 추가 기능 개발 가이드라인의 앞선 아티클에서 설명드렸듯 NVDA가 실행되는 동안 프로그램에 구애받지 않고 실행되는 추가 기능의 한 형태입니다. 이미지를 인식하여 이미지에 대한 설명을 스크린 리더 사용자에게 알려주도록 하는 기능을 추가하려면, 글로벌 플러그인 형태로 추가 기능을 작성해야 합니다. 이 기능은 NVDA가 실행되는 동안 사용자가 어디에서나 사용할 수 있기 때문입니다. Chrome 웹 브라우저와 같은 특정 프로그램에서만 작동하는 추가 기능을 작성하려면, Chrome 프로그램을 위한 앱 모듈을 제작해야 합니다.

글로벌 플러그인은 NVDA가 텍스트와 같은 요소 읽기 방식의 변경, 웹페이지 탐색 시 도움을 줄 수 있는 role(역할) 변경과 같은 NVDA의 기능을 변경할 수도 있기 때문에, 추가 기능 제작 시 세심한 주의가 필요합니다. 이번 아티클을 통해 단축키를 눌렀을 때 초점이 위치한 객체를 포함하여 탐색 객체(navigatorObject)의 요소의 이름(name), 부가 설명(description), 현재 포커스 상태와 같은 객체 정보를 출력하는 추가 기능을 제작하는 과정을 살펴보겠습니다.

 

글로벌 플러그인을 제작하기 전에

이번 아티클에서 구현할 글로벌 플러그인은 다음과 같은 기능을 가지고 있습니다:

  1. 이 글로벌 플러그인은 현재 초점이 위치한 객체 및 탐색 객체에 대한 객체의 이름(name), 부가 설명(description), 포커스 상태(True 또는 False) 등의 여러 객체 정보를 출력하는 기능을 가집니다.
  2. 단축키는 NVDA 메뉴(Insert+N) -> 설정 -> 제스처 설정 메뉴의 instantObjectViewer 카테고리에서 변경할 수 있도록 구현하며, 기본 단축키는 NVDA+Shift+A로 정의합니다.

참고: 요소의 이름, 부가 설명(description) 및 요소의 포커스 가능 상태 등의 객체 정보는 NVDA 로그 뷰어(Insert+F1)를 눌러 확인할 수 있습니다. 다만, 로그 뷰어의 경우 많은 정보를 포함하기에 객체 정보를 빠르게 확인할 목적으로 글로벌 플러그인을 작성하는 것입니다.

 

글로벌 플러그인의 폴더 구조

NVDA 추가 기능 개발 가이드라인 1부에서 추가 기능 폴더의 구조 및 각 파일의 역할에 대해 간단히 언급하였습니다. 이번 아티클에서 제작할 추가 기능의 파일 및 폴더가 어떻게 구성되는지 자세히 살펴보겠습니다.

  • ini: 추가 기능의 이름, 설명, 제작자 및 버전 정보를 포함합니다. 추가 기능을 설치하거나, NVDA의 추가 기능 관리 대화상자(NVDA 메뉴 -> 도구 -> 추가 기능 관리)에 표시됩니다.
  • globalPlugins 폴더: 이번 아티클에서는 글로벌 플러그인 형식으로 추가 기능을 제작하므로 이 폴더에 추가 기능의 핵심 파일들이 위치합니다.
  • globalPluginsinstantObjectViewer\__init__.py: 추가 기능의 모든 클래스, 함수가 포함된 파일입니다.

manifest.ini 작성

추가 기능을 작성하기 전 -       manifest.INI가 어떠한 요소로 구성되어 있는지 알아보고, manifest.ini를 작성해보겠습니다. -       manifest.ini는 NVDA의 추가 기능 관리 대화상자의 추가 기능 목록이나, 추가 기능의 설치 등에 사용되므로 중요하다 할 수 있습니다. manifest.ini는 필수인 항목과 필수가 아닌 항목으로 구분할 수 있으며, 필수로 포함돼야 할 값을 입력하지 않으면, 추가 기능이 설치되지 않을 수 있습니다. manifest.ini에서 자주 사용되는 필드 목록은 다음과 같습니다:

  • name: 추가 기능의 짧은 이름입니다. 이 필드는 NVDA가 내부적으로 추가 기능을 처리하는 데 사용되며, 사용자에게 표시되지 않습니다. 이 값은 필수입니다.
  • summary: 사용자에게 표시되는 추가 기능의 이름입니다. 이 값은 필수입니다.
  • description: 추가 기능이 수행하는 설명을 입력합니다. NVDA 메뉴 -> 도구 -> 추가 기능 메뉴에 추가 기능 별로 제공된 <정보> 버튼을 클릭했을 때 설명 정보가 표시됩니다. 이 값은 필수입니다.
  • author: 추가 기능 제작자 정보를 입력합니다. 이 값은 필수입니다.
  • version: 2.0.0, 2019.5.0과 같은 추가 기능의 버전 번호를 입력합니다. 이 값은 필수입니다.
  • url: 추가 기능의 업그레이드 및 추가 정보가 제공된다면 url 주소를 입력합니다.
  • docFileName: 추가 기능 문서 파일 이름을 지정합니다.
  • minimumNVDAVersion:: 추가 기능에서 요구하는 NVDA의 최소 버전을 입력합니다. lastTestedNVDAVersion 보다 작거나 같아야 합니다. 2018.4.0과 같이 값을 지정합니다.
  • lastTestedNVDAVersion: 추가 기능이 테스트된 NVDA의 최신 버전을 입력합니다. minimumNVDAVersion 보다 크거나 같아야 합니다. 2019.1.0과 같이 값을 지정합니다. 이 값이 지정되지 않으면, NVDA는 사용자에게 추가 기능을 설치하지 말 것을 경고합니다.

manifest.ini 예제

다음은 이번 추가 기능에서 사용된 manifest.ini 예제입니다. manifest.ini의 각 필드에 대한 설명은 이전 섹션을 참고합니다.

# manifest.ini start

name = instantObjectViewer

summary = "instantObjectViewer"

description = """단축키를 눌러 객체의 이름(name), 설명(description), 상태(state)와 같은 객체 정보를 표시합니다."""

author = "NVISIONS"

url = None

version = 1.0

minimumNVDAVersion = 2018.4

lastTestedNVDAVersion = 2019.1.1

docFileName = None

# manifest.ini end

 

플러그인의 주요 소스 코드 설명

지난 아티클에 걸쳐 플러그인의 주석, 기본적인 모듈 import에 대해 살펴보았으므로, 이번 아티클에서는 플러그인에서 사용된 주요 함수를 중심으로 소스 코드를 살펴보겠습니다.

  • 1~18줄: 이 추가 기능의 이름 및 저작권 정보와 추가 기능에서 사용된 주요 모듈을 가져오는 두 부분으로 나뉠 수 있으며, 1번째 라인은 Python에 utf-8로 인코딩된 파일임을 알리기 위한 용도입니다. controlTypes 모듈은 체크 상자, 버튼 등의 객체의 역할, 상태 정보 등을 제어할 수 있습니다. Ui 모듈은 음성이나 NVDA의 가상 커서 탐색이 가능한 창에서 메시지를 출력하기 위한 모듈입니다.
  • 21줄: 글로벌 플러그인을 제작하기 위해 globalPlugin 클래스를 정의합니다.
  • 23~26줄: Python의 데코리에터를 사용하여 추가 기능이 속할 카테고리를 만듭니다. 추가 기능 카테고리에는 추가 기능에 대한 설명 정보 및 단축키 등을 정의하여 사용자가 추가 기능의 용도를 파악하고 단축키를 변경할 수 있도록 합니다. NVDA 메뉴 -> 설정 -> 제스처 설정의 <instantObjectViewer> 카테고리가 표시됩니다.

다음은 추가 기능의 핵심 함수인 script_instantObjectViewer 함수의 주요 코드에 대한 설명입니다.

함수는 크게 초점 객체(FocusObject)와 탐색 객체(NavigatorObject)로 나뉠 수 있으며, 30, 35번 라인을 통해 초점 객체와 탐색 개체를 가져옵니다. focusObj, navObj는 초점 객체와 탐색 객체를 쉽게 가져오기 위해 사용됩니다. 두 부분의 코드 모두 초점 객체와 탐색 객체의 차이만 있을 뿐 동일하므로, 초점 객체를 가져와 정보를 출력하는 부분을 설명합니다.

  • 31, 32줄: 선택 가능, 객체에 초점을 위치한 경우와 같은 객체 상태 정보를 가져오기 위해 사용됩니다. focusStr은 객체 상태 정보의 출력을 용이하기 위해 문자열로 변환하여 저장되는 변수입니다.
  • 33, 34줄: 뷰어를 실행했을 때 초점 객체임을 명확히 강조하기 위해 헤딩으로 초점 객체임을 지정하였습니다. focusObjContent에는 헤더 메시지와 초점 객체에 대한 객체 정보를 저장하기 위한 변수입니다.
  • 40~41줄: 초점 객체의 객체 상태 정보를 가져오기 위한 반복문으로, 객체의 상태 정보는 여러 개가 존재하므로 한 줄에 한 개의 상태 정보가 출력되도록 하였습니다.
  • 44~45줄: 초점 객체의 필수적인 객체 정보를 출력을 위한 리스트로, 초점 객체의 객체 이름(name), 설명(description), 객체 역할(체크 상자, 편집창과 같은), 초점을 받을 수 있는 객체인지 출력하는 isFocus, 객체에 현재 초점이 위치했는지의 여부(hasFocus)를 출력하며 Python에의 list 자료형을 사용하였습니다. focusList는 초점 객체의 선택적 정보 또한 다음에 소개할 조건에 의해 목록에 추가됩니다.
  • 46~47줄: 초점 객체의 선택적인 객체 정보를 출력합니다. 선택적 정보는 객체에 필수적으로 포함된 정보가 아니며, 웹 페이지에서 객체 확인 시 객체의 언어(한국어 또는 영어와 같은)나 슬라이드 컨트롤에서의 현재 값(focusObj.value) 등이 있습니다. 선택적 정보를 출력하기 위해 필수적인 정보를 출력할 때 사용되었던 list 자료형을 사용하였습니다.
  • 48~51줄: 초점 객체의 선택적인 객체 정보를 초점 객체의 정보를 담고 있는 focusList에 저장하며, focusList가 none(없음)가 아닐 때 조건이 실행됩니다.
  • 56~60줄 초점 객체에 단축키 정보(Alt+Shift+A) 등이 존재할 때 focusList에 해당 정보를 삽입합니다.
  • 70~71줄: 초점 객체의 필수 정보와 선택 정보가 담긴 focusList의 항목을 focusObjContent 변수에 저장하기 위한 코드로, focusList를 list 자료형을 문자열로 변환하여 저장하며, NVDA에서 객체 정보를 사용자가 인지할 수 있도록 출력하기 위해 unicode로 변환하여 저장합니다.
  • 74줄: o 변수에는 초점 객체 및 탐색 객체의 정보가 담기며, 다음 라인에서 출력에 사용됩니다.
  • 75줄: ui 모듈의 browseableMessage 메서드를 통해 objectViewer 창에 객체 정보가 출력됩니다. 이 창은 NVDA의 가상 커서 기능(빠른 탐색 키 등)을 사용할 수 있습니다. objectViewer 창에 HTML을 허용하기 위해 isHtml을 True로 설정합니다.

추가 기능 패키징 하기

추가 기능을 패키징하는 방법은 다음과 같습니다.

  1. 추가 기능 폴더(예: test) 폴더로 이동합니다. 추가 기능 파일(manifest.ini, 라이선스 파일 및 Python 소스코드 등 추가 기능 관련 파일이 존재해야 추가 기능이 올바로 설치되고 사용자가 최종적으로 사용하는 데 문제가 없습니다.
  2. 폴더 내 모든 파일 및 하위 폴더를 전체 선택하고, 7-Zip과 같은 프로그램으로 .zip 확장자로 압축합니다.
  3. 압축된 파일(add-onName.zip)의 파일 이름을 add-onName.nvda-addon으로 변경합니다. NVDA에서 추가 기능 파일임을 인식하고, 추가 기능 설치 화면을 표시합니다.
  4. add-onName.nvda-addon 파일을 더블클릭하여 추가 기능 설치 화면이 표시되는지 확인합니다.

 

이번 아티클에서 사용된 NVDA의 모듈 문서는 다음 링크에서 확인하실 수 있습니다:

  • ui 모듈: 객체 정보를 화면에 출력하기 위해 사용되었으며, 음성으로 메시지를 출력할 수 있는 메서드가 있습니다.
  • API 모듈: 초점, 탐색 객체 정보를 얻어오기 위해 getFocusObject, getNavigatorObject 메서드가 사용되었으며, 마우스, 리뷰 위치, 클립보드에 텍스트 복사 등의 메서드가 포함되어 있습니다.
  • controlTypes 모듈: Python의 사전형으로 객체의 역할, 상태 정보를 담고 있으며, stateLabels, roleLabels을 사용하여 객체 역할, 상태 정보를 <체크 상자>, <선택됨>과 같은 문자열로 변환하는 메서드를 포함합니다.
  • scriptHandler: objectViewer 함수에 제스처(단축키)를 할당하기 위해 사용되었으며, 제스처의 횟수를 체크하여 기능을 다르게 할당할 수 있는 등 script 작업에 유용한 메서드를 포함합니다.
  • globalPluginHandler: 글로벌 플러그인을 제작하려면 필수로 사용해야 하는 모듈로, 아티클에서 모듈의 내부 메서드를 사용하진 않았지만, NVDA가 내부적으로 글로벌 플러그인을 핸들링할 때 사용됩니다.

 

이번 아티클에서 사용된 추가 기능 파일(.zip) 및 Python 소스 코드는 다음 링크에서 확인하실 수 있습니다:

추가 기능(.zip) 파일

추가 기능 Python 소스코드

 

댓글 0
댓글을 작성하려면 해주세요.