Error en la barra de progreso de AppleScript: ¿Cómo evitar que el diálogo de progreso se prolongue?

El bicho:

Cuando el cuadro de diálogo de progreso de AppleScript es reemplazado por un cuadro de diálogo, el cuadro de diálogo de progreso aún persiste, aunque todos los pasos de progreso se hayan completado y la barra de progreso esté llena. Permanecerá hasta que se cancele la secuencia de comandos, hasta que se complete la secuencia de comandos o hasta que no existan cuadros de diálogo adicionales en la secuencia de comandos.

Este error no se puede presenciar mientras se ejecuta el código desde Script Editor.app porque, en Script Editor, no aparecerá un cuadro de diálogo de progreso. En su lugar, se integra un medidor de progreso en forma de pastel en la parte inferior de la ventana del script.


Cómo reproducir el error:

Guarde el siguiente código AppleScript como un archivo .app:

(El archivo debe ser un archivo .app, ya que los archivos .scpt no pueden mostrar cuadros de diálogo de progreso).

set n to 5

set progress total steps to n
set progress description to "Script Progress"
set progress additional description to "Additional description"

repeat with i from 1 to n
    delay 0.1
    set progress completed steps to i
end repeat

display dialog "The progress dialog should be gone at this point."

Al iniciar la aplicación, verá lo siguiente:


La pregunta:

¿Existe una solución alternativa para forzar el cierre del cuadro de diálogo de progreso una vez que esté completo, de modo que se puedan colocar cuadros de diálogo adicionales después del cuadro de diálogo de progreso, sin que el cuadro de diálogo de progreso siga visible?


Lo que he probado:

Intenté abordar el problema interpretando el cuadro de diálogo de progreso simplemente como una "ventana" de la aplicación.

Si ejecuta el siguiente código en un archivo AppleScript separado, mientras ambos cuadros de diálogo de ProgressBarTest.app están en pantalla (como en la captura de pantalla anterior):

tell application "System Events"
    set allWindows to name of window of processes whose visible is true
end tell 

return allWindows

aprenderá que ProgressBarTest.app tiene 2 "ventanas" abiertas. Los títulos de estas ventanas son:

{"", "ProgressBarTest.app"}

La primera ventana de esta lista hace referencia al cuadro de display dialogdiálogo. La segunda ventana de esta lista, titulada ProgressBarTest.app, es el cuadro de diálogo de progreso.

Luego intenté cerrar esta "ventana" de diálogo de progreso usando AppleScript (como se puede hacer para cualquier ventana de aplicación estándar). Pero, el siguiente código:

tell application "System Events" to tell process "ProgressBarTest.app"
    if exists window "ProgressBarTest.app" then
        close window "ProgressBarTest.app"
    end if
end tell

le dará al usuario un error. El texto de este diálogo de error es:

Error de guión

Los eventos del sistema recibieron un error: la ventana "ProgressBarTest.app" del proceso "ProgressBarTest.app" no entiende el mensaje "cerrar".

En breve me di cuenta de que, si su pantalla tiene el mismo punto de vista que el de la captura de pantalla anterior, entonces ni siquiera puede cerrar manualmente el cuadro de diálogo de progreso. Esto se debe a que el display dialogdiálogo tiene prioridad sobre el diálogo de progreso; el cuadro de display dialogdiálogo "atenua" (es decir, desactiva) todos los botones del cuadro de diálogo de progreso.

Entonces, para dar cuenta de esto, en el código ProgressBarTest.app, agregué un delay 5directamente arriba de la display dialog "The progress dialog should be gone at this point."línea. Quería ver si podía cerrar con éxito el cuadro de diálogo de progreso, si el cuadro de diálogo de progreso era el único cuadro de diálogo activo de la aplicación.

Probé el siguiente código:

tell application "System Events" to tell process "ProgressBarTest.app"
    if exists window "ProgressBarTest.app" then
        click button 1 of window "ProgressBarTest.app"
    end if
end tell

En el código anterior, button 1se refiere al Stopbotón que se encuentra en el cuadro de diálogo de progreso. (Alternativamente, puede usar button -4o button 0para hacer referencia a este mismo botón).

¡La buena noticia es que este código cerró con éxito el cuadro de diálogo de progreso!

La mala noticia, sin embargo, es que cuando Stopse presiona el botón del cuadro de diálogo de progreso, en lugar de cerrar solo el cuadro de diálogo de progreso, se cancela todo el script . Esto es obviamente indeseable.

La raíz del problema es que el cuadro de diálogo de progreso no contiene una "x" roja; el botón circular más a la izquierda en la barra superior de este cuadro de diálogo siempre está atenuado. En otras palabras, no hay forma de cerrar el cuadro de diálogo de progreso manualmente, sin finalizar prematuramente el script.

Entonces, este problema es más difícil de resolver de lo que pensaba.

Parece que mi resultado deseado es imposible de lograr.


OS X El Capitán, versión 10.11.6.


¿Llegaste alguna vez a una solución para esto? Planteaste el tema a la perfección; parece una gran falla de diseño que Apple no hubiera proporcionado un interruptor para la visibilidad de la barra de progreso.
@IvanX No tengo.
@IvanX publiqué un código en una respuesta con una solución

Respuestas (1)

Funciona para mí en la última versión de macOS Big Sur

ACTUALIZAR

MEJOR SOLUCIÓN

Si coloca el display dialogcomando dentro de un System Events tell blockluego envuelva el ignoring application responsescomando alrededor de ese display dialogcomando, progress indicatorcompletará su tarea sin esperar a display dialogque se cierre la ventana.

El ignoring application responsescomando solo funcionará si se coloca dentro de la aplicacióntell block

set n to 50

set progress total steps to n
set progress description to "Script Progress"
set progress additional description to "Additional description"

repeat with i from 1 to n
    delay 0.1
    set progress completed steps to i
end repeat

tell application "System Events"
    activate
    ignoring application responses
        display dialog "The progress dialog should be gone at this point."
    end ignoring
end tell

ingrese la descripción de la imagen aquí



SOLUCIÓN ANTIGUA 1

set n to 5

set progress total steps to n
set progress description to "Script Progress"
set progress additional description to "Additional description"

repeat with i from 1 to n
    delay 0.1
    set progress completed steps to i
end repeat
return
quit

on quit
    activate
    display dialog "The progress dialog should be gone at this point."
    continue quit -- allows the script to quit
end quit

Aquí hay una forma de ejecutar algunos bucles repetidos antes de que se cierre el script.

set n to 5

set progress total steps to n
set progress description to "Script Progress"
set progress additional description to "Additional description"

repeat with i from 1 to n
    delay 0.1
    set progress completed steps to i
end repeat
return
quit

on quit
    activate
    display dialog "The progress dialog should be gone at this point."
    repeat 5 times
        my get_my_IP()
        my screenCaptureToDesktop()
        delay 1
    end repeat
    continue quit -- allows the script to quit
end quit


on get_my_IP()
    activate
    tell current application to display dialog (do shell script "curl ifconfig.co") with icon 2 buttons "OK" default button 1 with title "Your Current IP Address Is.." giving up after 5 -- "curl ifconfig.io"    -- alternate
end get_my_IP

on screenCaptureToDesktop()
    do shell script "/usr/sbin/screencapture \"" & ¬
        POSIX path of (path to desktop as string) & ¬
        "Screen Shot " & (current date) & ".png\""
end screenCaptureToDesktop

SOLUCIÓN ANTIGUA 2

Trabajando en uno de mis propios proyectos, tuve uno de esos "¡EUREKA!" momentos Aquí hay un enfoque completamente diferente a mi código en la SOLUCIÓN 1. El enfoque aquí fue envolver la mayor parte de mi código en objetos de script o controladores, luego llamar a esos objetos según sea necesario. En esta solución, guardé este script como una aplicación abierta, con controladores explícitos de ejecución e inactividad. Parte del código se ejecuta cuando se inicia la aplicación, pero la mayoría de los comandos en realidad suceden dentro del controlador inactivo.

Creo que esta solución promete más que la SOLUCIÓN 1.

global allFiles, thisFile, theFileCount, ProgressBar, mainFolder, backupFolder, theDate

on run
    run ProgressBar
end run

on idle
    delay 0.2
    activate
    set theButton to button returned of (display dialog ¬
        "The progress dialog should be gone at this point." buttons {"QUIT", "CONTINUE"} ¬
        default button ¬
        "CONTINUE" with title ¬
        "BACKUP UTILITY" with icon 0 ¬
        giving up after 10)
    if theButton = "QUIT" then
        quit
    else if theButton = "" then
        quit
    else if theButton = "CONTINUE" then
        try
            run ProgressBar
        end try
    end if
    return 1
end idle

on quit
    --  Executed when the script quits
    continue quit -- allows the script to quit
end quit

script ProgressBar
    set mainFolder to choose folder with prompt ¬
        "PLEASE CHOOSE YOUR SOURCE FOLDER TO BACKUP"
    set backupFolder to choose folder with prompt ¬
        "PLEASE CHOOSE YOUR BACKUP DESTINATION FOLDER"
    set theDate to (current date) - (14 * days)
    
    tell application "Finder"
        set allFiles to entire contents of folder mainFolder as alias list
    end tell
    
    set theFileCount to count of allFiles
    
    set progress total steps to theFileCount
    set progress completed steps to 0
    set progress description to "Processing Files..."
    set progress additional description to "Preparing to process."
    
    repeat with theName from 1 to number of items in allFiles
        set this_item to item theName of allFiles
        tell application "Finder"
            duplicate this_item to backupFolder ¬
                with replacing
        end tell
        set progress additional description to ¬
            "Duplicating File " & theName & " of " & theFileCount & ¬
            " to folder " & backupFolder
        set progress completed steps to theName
    end repeat
    
    -- Reset the progress information
    set progress total steps to 0
    set progress completed steps to 0
    set progress description to ""
    set progress additional description to ""
end script
Cualquier otro comando que desee ejecutar, simplemente colóquelo dentro del controlador de salida antes del comando "continuar saliendo"
Wow, te doy un gran crédito por descubrir esta solución. Su método de hecho resuelve mi pregunta. Implementado prácticamente en mis aplicaciones reales, este método tiene sus limitaciones. Dado que no hay forma de salir de la on quitsubrutina una vez que ingresa, ya no es posible envolver todo el script en un repeatbucle, por ejemplo. En cualquier caso, ¡no creo que encuentre una mejor solución que su método! ¡Gracias!
@rubik'ssphere Es posible que no pueda envolver todo el script en un bucle de repetición, pero actualicé el código para mostrar una muestra de cómo puede ejecutar algunos bucles de repetición dentro del controlador de salida. Puede hacer que los otros controladores sean enormes con sus propios bucles de repetición y subrutinas.
Muy buena solución. (Y sigue siendo una decisión de diseño extraña por parte de Apple).
Probando bajo macOS Catalina Tuve los siguientes problemas: 1. Aunque le otorgué permiso a Test.app para permitirle controlar Finder , no mostraba el cuadro de diálogo hasta que comenté temporalmente el ignoring application responses bloque , lo volví a guardar, lo ejecuté dando permiso nuevamente y luego descomentar el bloque , volver a guardarlo, ejecutarlo nuevamente dando permiso nuevamente y luego solo después de todo ese baile, mostraría el cuadro de diálogo normalmente. 2. El uso de Finder es molesto, ya que trae al frente cualquier ventana abierta de Finder .
Para superar el problema de la ventana del Finder , utilicé Eventos del sistema en su lugar y esto no interfirió con ninguna otra ventana que se abriera. Podría considerar cambiar eso en el código de su respuesta.
@ user3439894 Buena captura. Revisando ahora. Por cierto, solo tuve que pasar por la misma canción y bailar que tú, para que funcionara conSystem Events
Sí, también tuve que hacer el baile con System Events y Finder . No es un gran problema, aunque vale la pena mencionarlo en caso de que otros también tengan un problema.