Closed11

NVDAと格闘する

mehm8128mehm8128

appModulesの中には、それぞれのアプリケーションごとの処理が入ってそう
explorer.pyevent_UIA_elementSelectedだったら、エクスプローラーでタブが切り替わったときに読み上げる処理とか
braille.なんとかで点字用の処理も行っていそう

mehm8128mehm8128

なんかaria-errormessageがChromeとFirefoxでサポートされてるっぽいので、読んでみる
https://github.com/nvaccess/nvda/pull/16411
a11ysupportにはまだ反映されていなさそう
https://a11ysupport.io/tech/aria/aria-errormessage_attribute

_get_errorMessage
IAccessibleHandler.RelationType.ERRORがaria-errormessageに対応する
ref: https://www.w3.org/TR/core-aam-1.2/#ariaErrorMessage

Relation: IA2_RELATION_ERROR points to accessible nodes matching IDREFs, if the referenced objects are in the accessibility tree

_getIA2RelationFirstTargetで、最初に見つかったそれを取ってきてる。どこからかは知らない

https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/NVDAObjects/IAccessible/__init__.py#L2001

NVDAHelper.localLib.nvdaInProcUtils_getTextFromIAccessibleでcppのコードを呼び出し、getRelationElementsOfTypeで取り出したerrorTargetからgetTextFromIAccessibleでIAから情報をもらっていそう
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/NVDAObjects/IAccessible/__init__.py#L2231

https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/nvdaHelper/remote/IA2Support.cpp#L363

https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/nvdaHelper/remote/textFromIAccessible.cpp#L65

mehm8128mehm8128

source/speech/speech.pyの方を見てみる
一番上の変更差分であるgetObjectPropertiesSpeechから
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L641

Objectはおそらく、引数にもらってるobjであるNVDAObjectを示している。こいつが持ってるプロパティを一通りスピーチする関数なのだろう

NVDAObjectはここらへんで定義されてるやつ。PRの説明文にもあるように、今回これにerrorMessageというプロパティが新しく追加されたので、これにさっきの_get_errorMessageしたやつを入れて、読み上げさせる的な感じなのだと考えた
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/NVDAObjects/__init__.py#L205

が、_get_errorMessageが他のところで出てこないので、謎

getObjectPropertiesSpeechはすぐ上のspeakObjectPropertiesで使われて返り値がspeechSequenceに入り、speak(speakObjectPropertiesされる
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L1068

speak関数を追っていくと、SpeechManagergetSynth().speak(seq)という箇所にたどり着く。SynthDriverというものを使って音を流しているらしい

mehm8128mehm8128

もう少しgetObjectPropertiesSpeechを見ていく

name == "errorMessage" and value and State.INVALID_ENTRY not in obj.states

で、後半はまあコメントに書いてあるように、NVDAObjectaria-validというstateを持っていないときみたいな感じで、nameがなんなのかを見る。
nameallowedProperties.items()forで回されていて、allowedPropetiesは引数で渡されている
あとで出てくるので一応書いておくと、ここでnewPropertyValues["errorMessage"] = Noneを設定している

**allowedProperties**ってなんだっけ?ダブルポインタ?と思いつつ、そういえばPythonのspread構文みたいな感じのやつだったことを思い出す

speakObjectPropertiesの引数がそのまま渡されてるっぽいので、speakObjectPropertiesで検索をかけると、例えばsource/browseMode.pyだと以下のような使われ方

speech.speakObjectProperties(
    self.rootNVDAObject,
    name=True,
    states=True,
    reason=OutputReason.FOCUS,
)

name=Truenameってstringじゃないの?と思いながらちょっと考えてみた結果、「objで渡されたものの中からkey=Trueであるkeyのみ取得する」という理解になったけど、理屈はよく分からなかった
name=True, states=Trueで渡したらallowedProperties{name: True, states: True}になっちゃうのでは

mehm8128mehm8128

次にgetPropertiesSpeechの↓を見ていく

errorMessage: str | None = propertyValues.get("errorMessage", None)

getObjectPropertiesSpeechでさっき作ったnewPropertyValuesgetPropertiesSpeechに渡して、speechSequenceを作っている
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L765-L766

さっきNoneを渡してたんだからNoneのような気もするが、getPropertiesSpeechは色んなところで使われているので、今回関係あるところだとここ↓でも使われている
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L2309

ちょっと上でattrsからerrorMessageを取っているので、ここではちゃんとエラーメッセージが入る可能性がある
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L2244-L2246

attrsgetControlFieldSpeechが複数使われているgetTextInfoSpeechで定義されていそう

info.getTextWithFieldsから取ったtextWithFieldsの中身であるfieldfield.fieldをinitialFieldsに入れ、その中身のfieldnewControlFieldStackに入れ、その中身のfieldattrsとして使っていそう
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L1524
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/speech/speech.py#L1613-L1614

mehm8128mehm8128

そういえば、ファイル検索するときはincludeディレクトリとuser_docsディレクトリをexcludeするといい感じになった

mehm8128mehm8128

_get_errorMessageがどこで実行されてどのタイミングでerrorMessageプロパティに値が入るのかが分からなかった

mehm8128mehm8128

なんか多分これ以上追ってもしょうがないけど、一応attrsについてもう少し調べたのでまとめ

attrsの型であるControlFieldは、ここ↓に書かれてる通り、table, button, formなど、テキストを包含するcontrolのaccessible nameやroleなどの情報を保有するclassらしい
https://github.com/nvaccess/nvda/blob/13cb733684960127c58c33a013abbb2d1b88bb8c/source/textInfos/__init__.py#L51-L55
Dictを継承しているので、先に出てきたようなerrorMessage = attrs.get("errorMessage", None)みたいな処理が可能になっている

newControlFieldStackList[ControlField]
textWithFieldsの中身であるfieldFieldCommandで、そのfield.fieldinitialFieldsになり、それとnewControlFieldStackの型が同じ

多分どこかでいい感じにNVDAObjectを取ってきて、attrsにしてる

このスクラップは2025/06/13にクローズされました