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