Applescript de interfaz de usuario portátil para elegir cualquier elemento de menú a través de cualquier jerarquía de submenús

En la parte inferior de esta página , Apple explica cómo usar secuencias de comandos de interfaz de usuario para automatizar la selección de elementos de menú. Hay dos ejemplos codificados en los que me estoy basando en un esfuerzo por escribir un script de biblioteca genérico para usar en varios proyectos que pueden elegir cualquier elemento de menú de cualquier menú a través de cualquier jerarquía de submenús.

La versión TL; DR de esta pregunta es: ¿Cómo hago eso? 😉

Pero para un poco más de detalle, si se desea...


...He hecho algunos progresos y se me han ocurrido algunos enfoques para esto, pero cada uno de ellos tiene un defecto fatal u otro...

Opción 1.

Si todos los parámetros son texto o listas de texto: por ejemplo:

on ChooseMenuItem(theApp, theMenuName, theSubMenusNamesList, theMenuItemName)

que podría llamarse con, por ejemplo:

ChooseMenuItem("TextEdit", "Format", {"Font", "Ligatures"}, "Use Default")

luego puedo usar un montón de análisis y concatenación de texto, etc., incluido un ciclo de repetición a través del theSubMenusNamesListparámetro para construir cuidadosamente un script en una cadena, "tell menu ... click ... tell menu item ... click ... tell menu item ... click ..."etc. y luego:

run script theScript in AppleScript

¡Voila! Y esto funciona Excepto cuando alguno de esos parámetros, particularmente el nombre del menú, no existe. p.ej. tratando de conectar o desconectar un dispositivo bluetooth a través del menú bluetooth extra. Entonces...

Opcion 2.

Si el menú no tiene nombre, cambiemos theMenuName a theMenu y pasémoslo por referencia. p.ej:

tell application "System Events" to tell process "SystemUIServer" to set theMenu to (menu bar item 1 of menu bar 1 whose description contains "bluetooth")
...
ChooseMenuItem("SystemUIServer", theMenu, {"Magic Keyboard"}, "Connect")

Tenga en cuenta que aquí theMenuvariable y parámetro en lugar de theMenuName. Después de realizar la setdeclaración, theMenula variable no es texto, es un objeto AppleScript, algo así como:

menu bar item 6 of menu bar 1 of application process "SystemUIServer"

A partir de aquí, esto falla en una de tres formas.

2a. Si trato de construir un script que incluya: tell theMenu ...me dice, como es lógico, que ...la variable theMenu no está definida.

2b. Si saco theMenude las comillas: "tell " & theMenu & "..."o "tell \"" & theMenu & "\"..."se produce un error con: No se puede hacer «clase mbri» 6 de «clase mbar» 1 de «clase pcap» "SystemUIServer" de la aplicación "Eventos del sistema" en tipo de texto Unicode.

es decir. no puede forzar el elemento 6 de la barra de menú antes mencionado de la barra de menú 1... a texto (a diferencia de la Opción 1, donde podía llamar al menú por su nombre).

2c. Si trato de extraer la instrucción Tell del menú del script de texto construido para que la variable de texto del script sea algo así como tell menu item ... click ... tell menu item ... click(sin la inicial tell menu ... click) y luego:

tell theMenu
    click
    run script theScript in AppleScript
end tell

entonces me dice the variable "click" is not defined. Creo que se refiere al primer "clic" en la variable Script que run scriptintenta... bueno... ejecutar. run script...Parece que no puede captar ningún contexto desde donde se llama, o si puede, no he descubierto cómo.

Opción 3

Traté de pensar cómo podría incluir tell menu item theSubMenuName ...declaraciones reales (es decir, no cadenas de texto de las mismas que luego intento run) dentro del ciclo mencionado anteriormente repeat, pero dado que es una jerarquía de indicaciones, no puedo entenderlo cómo hacer que eso funcione. Tal vez haya una solución recursiva aquí, pero mi cerebro duele demasiado como para descifrarlo.

Conclusión

Entonces, estoy perdido... No puedo resolver esto, pero tampoco puedo creer que no haya una manera de hacerlo. Mi inclinación es que tiene que haber alguna forma de referirse al elemento 6 de la barra de menú de la barra de menú 1 de... en mi theScriptvariable en alguna parte... pero ¿cómo? O si realmente no es eso, ¿entonces alguna otra solución definitiva aquí?

Respuestas (1)

Después de luchar con esto durante días antes de publicar la pregunta anterior, parece que encontré una solución: creo que el acto de describir la pregunta en detalle para publicarla me hizo pensar allí.

Lo comparto para el beneficio de cualquier otra persona que encuentre algo similar...

Mi lucha fue con la idea de que la naturaleza jerárquica significaba aumentar el nivel de tellcada elemento en theSubMenusNamesList. No pude averiguar cómo poner eso en un bucle sin que cada iteración del bucle entrara en ese nivel pero luego tuviera que volver al mismo nivel, por lo que no profundizaría.

Aquí fue donde la Opción 3 fue un error desde el principio, lo que me hizo caer en la madriguera del conejo de intentar construir el script en texto, para luego ejecutarlo con run script.

Sin embargo, la Opción 3 es en realidad la respuesta: codifíquelo directamente sin intentar construirlo en el texto, pero tuve que luchar con eso un poco más para llegar allí.

Eventualmente me di cuenta de que puedo poner el submenú de cada iteración en una variable, establecida en menu item ... ofla iteración anterior de sí mismo . Eso profundiza con éxito en cada nivel de la jerarquía sin retirarse demasiado pronto.

Por supuesto, si eso no tiene sentido, entonces aquí está el código, que puedes robar y adaptar, por supuesto, y que puede o no tener más sentido... ;)

on chooseMenuItem(theApp, theMenu, theSubMenusNamesList, theMenuItemName)
    set theAppName to the name of theApp
    activate theApp
    tell application "System Events" to tell process theAppName
        tell theMenu to click

        repeat with theMenuName in theSubMenusNamesList
            set theMenu to menu item theMenuName of menu 1 of theMenu
            tell theMenu to click
        end repeat

        set theMenuItem to menu item theMenuItemName of menu 1 of theMenu
        tell theMenuItem to click
    end tell
end chooseMenuItem

Esto funciona para mí: probé con varias opciones diferentes. Por ejemplo: como en la pregunta original, llámalo con algo como:

ChooseMenuItem("TextEdit", "Format", {"Font", "Ligatures"}, "Use Default")

O un ejemplo sin submenús, simplemente deje el tercer parámetro como una lista vacía:

ChooseMenuItem("TextEdit", "File", {}, "New")

Espero que esto ayude a alguien.

Acabo de recibir su pregunta hoy. Iba a demostrar cómo hacer esto, pero ha descubierto una solución y habrá aprendido mucho más haciéndolo usted mismo que teniendo a alguien que lo haga por usted. Muy bien hecho. Si mal no recuerdo, puedo publicar un par de métodos alternativos, no muy diferentes a los tuyos, pero que podrían ser más rápidos. Vuelva más tarde.
Hola @CJK. Sí, definitivamente aprendí mucho resolviendo eso, a pesar de la pérdida de cabello en el camino. jajaja. 😉 Pero... las soluciones alternativas definitivamente son bienvenidas - también hay más para aprender allí. Me encantaría que publicaras tus alternativas. ¡Gracias!