python
Programmazione python: uno script di nvda per esplorare l'oggetto col focus
Donato Taddei su uictech, 01\04\2013, h. 14.27.

Sono al mio primo script in questo ambiente e pertanto con l'approssimazione e l'incertezza della 
prima volta.
Intendo servirmene nella mia futura attività di apprendimento e di sviluppo in questo contesto.
Tuttavia esso mostra come implementare alcune semplicissime funzionalità:
1) - intanto il meccanismo di creazione di un plugin globale in nvda, sganciato cioè da una singola 
applicazione;
2) - come emettere beep di determinata altezza e durata;
3) - come inviare frasi alla sintesi
4) - come associare una combinazione di tasti allo script per attivarlo;
5) - come ricavare un certo numero di informazioni e copiarle negli appunti:
ho adottato questa soluzione in attesa di adottare la strategia a me più consona per la creazione e 
gestione di finestre, tra le 3 o 4 possibili in questo ambiente.
6) - come intercettare un evento, nel caso il cambiamento di stato della finestra, segnalandolo con 
un beep.
7) - come minimizzare il fastidio delle indentazioni raggruppando più istruzioni su una sola riga, 
separandole con punto e virgola.

Il codice dello script, come del resto quello postato in precedenza e quelli futuri, sarà sempre 
scaricabile in un unico file all'indirizzo
donatotaddei.altervista.org/pitone.txt
nella sua ultima versione e sarà cura degli interessati tagliare da questo file il codice di 
interesse e salvarlo, in questo caso nella directory userConfig\globalPlugins del loro nvda con una 
estensione .py.
Indi basterà aviviare nvda o ricaricare i plugins dal menu strumenti.

Note:
Il testo di riferimento è stato ovviamente la "developer guide" di nvda:
www.nvda-project.org/documentation/developerGuide.html
versione ufficiale inglese perchè la comunità italiana di nvda non ha ritenuto utile tradurla.
Devo aggiungere che non ci sarei mai riuscito se non avessi sotto mano i sorgenti di nvda:
ma l'open-source è una risorsa immensa perchè se anche non ci ho capito quasi niente ho comunque 
potuto servirmene per esempio per aiutarmi nella sintassi ancora troppo incerta del python.
Sono altresì stati preziosi strumenti a supporto sia la console di python nvda che il 
visualizzatore log:
infatti configurando la visualizzazione del log su debug viene tenuta traccia di ogni cosa:
tasti premuti, testo inviato alla sintesi, errori negli script.
Il lavoro di scrittura vero e proprio, dopo un po, è altrettanto facile quanto scrivere una pagina 
html o php:
la finestra di explorer puntata sul file, il blocco note per modificarlo, indi salvare.
Mentre in php devo aprire il browser su localhost, qui devo premere 4 tasti per ricompilare:
insert+n (menu nvda)
t (tools o strumenti)
r (ricarica plugins) e le modifiche sono attive.

Passiamo alla dimostrazione dell'output per poi infine commentare brevemente il codice.
mentre scrivo questo messaggio in Oe, premo insert+freccia sinistra
odo un beep, poi la sintesi annunciare
classe Internet Explorer_Server, controllo 0
Indi copio dagli appunti con control+v ricavando quanto segue:

Oggetto focalizzato:
, classe Internet Explorer_Server, controllo 0
Genitore:
None, classe ##MimeEdit_Server, controllo 0
Nonno:
None, classe ##MimeEdit_Server, controllo 0
Ruolo: 52
Stati associati: set([16777216, 2, 67108864])
Applicazione: 'msimn' (appName u'winmail', process ID 2128) at address 6d80610
value:
description: None
Coordinate finestra:
Sinistra 284, Sopra 383, Larghezza 797, Altezza 173
Handle finestra: 2688302
Id di processo e thread. (2128, 4884)

Albero genealogico completo:
None, di classe ##MimeEdit_Server, Controllo 0
None, di classe ##MimeEdit_Server, Controllo 0
None, di classe ME_DocHost, Controllo 1000
None, di classe ME_DocHost, Controllo 1000
Programmazione python: uno script per nvda per indagare l'oggetto che ha il focus, di classe 
ATH_Note, Controllo 0
Programmazione python: uno script per nvda per indagare l'oggetto che ha il focus, di classe 
ATH_Note, Controllo 0
Desktop, di classe #32769, Controllo 0

Moduli importati dallo script
[globalPluginHandler, __builtins__, __file__, winUser, __package__, beep, GlobalPlugin, api, ui, 
__name__, __doc__]
Nomi delloggetto NVDA che rappresenta il focus:
[IAccessibleObject, event_childID, _HTMLNodeUniqueNumber, textRepresentationLineLength, 
event_objectID, _windowClassName, HTMLNode, _speakTextInfo_formatFieldAttributesCache, 
_mouseEntered, IAccessibleChildID, _processIDThreadID, event_windowHandle, _oldCaretBookmark, 
HTMLNodeHasAncestorIAccessible, APIClass, _propertyCache, _windowControlID, _appModuleRef, 
_IATableCell, _HTMLNodeName, _speakObjectPropertiesCache, _treeInterceptor, windowHandle, 
_HTMLNodeSupportsTextRanges, _gestureMap, _speakTextInfo_controlFieldStackCache]

Nomi del contesto locale:
[nome, s1, l, focus, p, s, self, gesture]

Comandi di tastiera nvda attivi:
[control+shift+rightarrow, end, control+rightarrow, delete, shift+leftarrow, shift+home, leftarrow, 
backspace, rightarrow, control+home, control+shift+uparrow, control+backspace, 
control+shift+downarrow, shift+rightarrow, shift+uparrow, control+shift+end, control+a, home, 
numpaddelete, control+uparrow, pageup, uparrow, control+shift+home, pagedown, shift+end, 
control+end, downarrow, control+leftarrow, control+shift+leftarrow, shift+pagedown, 
shift+downarrow, control+downarrow, shift+pageup]
Script associati:
[unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByCharacter
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByWord
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_delete
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByCharacter
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_backspaceCharacter
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByCharacter
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_backspaceWord
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByCharacter
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_delete
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByParagraph
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByLine
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByWord
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_moveByParagraph
unbound method 
Dynamic_EditableTextWithoutAutoSelectDetectionBodyMSHTMLIAccessible.script_caret_changeSelection]
Variabili:
dizionario
'IAccessibleObject': POINTER(IAccessible) ptr=0x78b47d0 at 6cbec10
'event_childID': 0
'_HTMLNodeUniqueNumber': 1
'textRepresentationLineLength': None
'event_objectID': 1
'_windowClassName': u'Internet Explorer_Server'
'HTMLNode': comtypes.client.lazybind.Dispatch object at 0x06CB93B0
'_speakTextInfo_formatFieldAttributesCache': dizionario
}
'_mouseEntered': False
'IAccessibleChildID': 0
'_processIDThreadID': (2128
4884)
'event_windowHandle': 2688302

omissis.

Passiamo ora a commentare brevemente il codice:

Un plugin globale in nvda deriva sempre una classe globalPlugin dal modulo
globalPluginHandler,
all'interno della quale possono essere definiti metodi arbitrari.
Vi sono però due categorie di metodi speciali:
quelli che gestiscono eventi come l'apertura di finestre, il focus, cambiamenti di stati e 
proprietà delle finestre, ecc.
e quelli che associano combinazioni di tasti a determinate azioni:
i primi dovranno avere come nome qualcosa che inizi con event_, i secondi con script.
Vi è poi una variabile di tipo dizionario __gestures in cui vanno registrate le combinazioni di 
tasti da associare.
Si rinvia alla guida per ulteriori chiarimenti.

Allora comincio con l'importare un po di moduli:
import globalPluginHandler # da cui derivare la classe
import ui # utilizzato per inviare messaggi alla sintesi
import api # contiene il metodo getFocusObject() per recuperare l'oggetto focalizzato
from tones import beep
import winUser

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
Definizione di un evento: notare la scrittura su una sola riga per poterla all'occorrenza marcre 
con un # iniziale per disattivarlo se i beep danno troppo fastidio.
def event_stateChange(self, obj, nextHandler): beep(550, 50);nextHandler()

E Questo è lo script:
recupera il l'oggetto focalizzatof, emette un beep, indi un messaggio alla sintesi.
poi accumula le varie informazioni in una stringa s.
utilizza un'altra stringa di comodo s1 quando sia necessario spezzare le righe, rimuovere roba 
fastidiosa, ecc.
Dopo le sostituzioni viene accodata all'accumulatore s.
I caratteri di ritorno a capo sono ottenuti con la sequenza "\r\n".la funzione str() serve per 
convertire in stringhe liste, dizionari e quant'altro.
Ogni riga ha l'intestazione in chiaro per cui posso risparmiarmi ulteriori commenti.

def script_vedifocus(self, gesture):
focus = api.getFocusObject()
if focus.name==None: nome="nessuno"
else: nome = focus.name
beep(660,100)
ui.message(nome+" classe: "+focus.windowClassName+" controllo "+str(focus.windowControlID))
s="Oggetto focalizzato:\n"+nome+", classe "+str(focus.windowClassName)+", controllo 
"+str(focus.windowControlID)+"\r\n"
s+="Genitore:\n"+str(focus.parent.name)+", classe "+str(focus.parent.windowClassName)+", controllo 
"+str(focus.parent.windowControlID)+"\r\n"
s+="Nonno:\n"+str(focus.parent.parent.name)+", classe "+str(focus.parent.parent.windowClassName)+", 
controllo "+str(focus.parent.parent.windowControlID)+"\r\n"
s+="Ruolo: "+str(focus.role)+"\r\n"
s+="Stati associati: "+repr(focus.states)+"\r\n"
s+="Applicazione: "+str(focus.appModule)+"\r\n"
s+="value: "+str(focus.value)+"\r\n"
s+="description: "+str(focus.description)+"\r\n"
s+="Coordinate finestra:\nSinistra "+str(focus.location[0])+", Sopra "+str(focus.location[1])+", 
Larghezza "+str(focus.location[2])+", Altezza "+str(focus.location[3])+"\r\n"
s+="Handle finestra: "+str(focus.windowHandle)+"\r\n"+"Id di processo e thread. 
"+str(focus._processIDThreadID)+"\r\n"
# x,y=winUser.getCursorPos();s+="Cursore: "+str(x)+" "+str(y)+"\r\n"
l=api.getFocusAncestors();l.reverse()
s+="\r\nAlbero genealogico completo:\r\n"
for p in (l): s+=str(p.name)+", di classe "+str(p.windowClassName)+", Controllo 
"+str(p.windowControlID)+"\r\n"
s1="\r\nModuli importati dallo script\r\n"+str(globals().keys())+"\r\n";s1.replace("'","")
s1+="Nomi dell'oggetto NVDA che rappresenta il 
focus:\r\n"+str(vars(focus).keys())+"\r\n";s1=s1.replace("'","")
s1+="\nNomi del contesto locale:\n"+str(locals().keys())+"\r\n";s1=s1.replace("'","")
s1+="\r\nComandi di tastiera nvda 
attivi:\n"+str(focus._gestureMap.keys());s1=s1.replace("u'kb:","");s1=s1.replace("'","")
s+=s1;s1="\r\nScript associati:\n"+str(focus._gestureMap.values())

s1+="\r\nVariabili:\n"+str(vars(focus));s1=s1.replace(",","\r\n");s1=s1.replace("{","dizionario\r\n");s1=s1.replace("","")
s+=s1
api.copyToClip(unicode(s))

__gestures = {
"kb:NVDA+leftArrow": "vedifocus",
}
Torna all'indice