Hemos visto que la infraestructura de UI de JFace proporciona soporte básico para mostrar el progreso de las tareas en un diálogo (consulte la sección Operaciones de larga ejecución para obtener detalles). En la sección Infraestructura de simultaneidad, hemos examinado el soporte de ejecución de la plataforma para la simultaneidad y las operaciones de larga ejecución. Ahora veremos cómo la UI de la plataforma mejora esta infraestructura en el paquete
org.eclipse.ui.progress. Este paquete suministra la UI para visualizar el progreso de los trabajos en el entorno de trabajo y define soporte adicional para los trabajos que se ejecutan en la hebra de UI.
Antes de presentar las API nuevas, empecemos por repasar algunos conceptos. En primer lugar, debemos distinguir entre diferentes tipos de operaciones de segundo plano:
Iniciadas por usuario. Estos trabajos están definidos como trabajos de usuario (Job#setUser). El entorno de trabajo mostrará automáticamente los trabajos de usuario en un diálogo de progreso modal con un botón que permite al usuario ejecutar la operación en segundo plano y seguir trabajando. Se utiliza una preferencia global para indicar si los trabajos de usuario deben ejecutarse siempre en segundo plano.
Las operaciones de construcción, reserva de un proyecto, sincronización con el repositorio, exportación de un conector y búsqueda son ejemplos de trabajos de usuario.Desencadenada automáticamente. Estas operaciones son significativas para los usuarios, pero éstos no las han iniciado. Esta es la variedad por omisión de un trabajo. Estos trabajos se muestran en la vista de progreso y en la línea de estado, pero el diálogo de progreso modal no los mostrará cuando se ejecuten. La construcción automática y la sincronización planificada son ejemplos de este tipo de trabajos.
Operaciones del sistema. Operaciones no desencadenadas por el usuario que pueden considerarse un detalle de implementación. Estos trabajos se crean estableciendo el indicador del sistema (Job#setSystem). Los ejemplos de trabajo del sistema incluyen trabajos que llenan widgets de forma diferida o calculan decoraciones y anotaciones para vistas.
Dado un entorno en el que pueden estar ocurriendo varias cosas simultáneamente, el usuario necesita:
Una indicación de cuándo se ha iniciado una operación de larga ejecución.
Los trabajos de usuario se muestran al usuario en un diálogo de progreso que ofrece información inmediata, mientras que los trabajos desencadenados automáticamente se muestran en la línea de estado y en la vista de progreso. Asimismo, los trabajos que afectan a un componente deben planificarse o registrarse con el componente a fin de que el entorno de trabajo pueda suministrar al usuario indicios de que se está ejecutando algo que afecta al componente.
Una indicación de cuándo ha finalizado una operación.
El usuario puede saber fácilmente cuándo finalizan los trabajos de usuario, ya que el diálogo de progreso se cierra. Sin embargo, para los trabajos que no son de usuario existen un par de mecanismos de información disponibles. Si el trabajo se ha planificado o registrado con un componente, la información de progreso de componentes se mostrará cuando haya finalizado. Si un trabajo devuelve un error, aparecerá un indicador de error en la parte inferior derecha de la línea de estado que mostrará un indicio de que se ha producido un error.
Una indicación de resultados nuevos de interés o información nueva sin quitar el foco de los diálogos mostrados por la operación de segundo plano.
Un trabajo de usuario puede mostrar directamente los resultados al usuario cuando finaliza la operación. Para trabajos que no sean de usuario, es aconsejable no interrumpir al usuario con un diálogo. En lugar de ello, si los resultados del trabajo se muestran en una vista, ésta puede abrirse cuando se inicia el trabajo y los resultados se mostrarán en ella. Esto no interrumpirá el flujo de trabajo de los usuarios. Además, puede añadir propiedades a un trabajo para indicar que debe mantenerse en la vista de progreso y que tiene una acción que mostrará los resultados. En la esquina inferior derecha de la línea de estado aparecerá una indicación de aviso cuando un trabajo permanezca en la vista de progreso y tenga resultados que mostrar al usuario.
Una idea general de conservar el control de lo que se está ejecutando, con la posibilidad de supervisar y cancelar operaciones en segundo plano.
Los trabajos de usuario proporcionan el mejor control al usuario, ya que se cancelan con
facilidad y proporcionan una indicación evidente de las operaciones simultáneas o en bloqueo
en ejecución por medio de la pestaña Detalles del diálogo de progreso. Tenga en cuenta que el diálogo de progreso mejorado que proporciona el área Detalles sólo se muestra cuando los usuarios llaman a IProgressService#busyCursorWhile
o IProgressService#runInUI.
Además, la vista de progreso proporciona acceso a los trabajos en ejecución.
Todos los conectores instalados deben mostrar coherentemente el progreso de la misma forma.
La ventaja de utilizar la API de servicio de progreso consiste en que los usuarios obtienen información de progreso coherente.
A continuación, entraremos en detalles acerca de la utilización de las nuevas API.
El servicio de progreso del entorno de trabajo (IProgressService) es la interfaz primaria del soporte de progreso del entorno de trabajo. Puede obtenerse del entorno de trabajo y, a continuación, utilizarse para mostrar el progreso de las operaciones de segundo plano y de las operaciones ejecutadas en la hebra de la UI. La finalidad de esta clase consiste en suministrar un solo punto de visualización para las operaciones en ejecución, eliminando la necesidad de que los desarrolladores de conectores decidan el mecanismo que debe utilizarse para mostrar el progreso en una situación determinada. Otra ventaja consiste en que el diálogo de progreso mostrado con estos métodos proporciona un buen soporte para indicar cuándo una operación queda bloqueada por otra y otorga al usuario el control para resolver el conflicto. Cuando sea posible, las operaciones de larga ejecución deben ejecutarse mediante IProgressService#busyCursorWhile:
IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); progressService.busyCursorWhile(new IRunnableWithProgress(){ public void run(IProgressMonitor monitor) { //realizar trabajo no de UI } });
Este método colocará inicialmente un cursor ocupado y lo sustituirá por un diálogo de progreso si la duración de la operación es superior al umbral de tiempo especificado. La ventaja de este método con respecto a la utilización de un diálogo de progreso consiste en que, si la operación es de corta ejecución, no se mostrará el diálogo de progreso. Si la operación debe actualizar la UI, siempre puede enviar un Display.asyncExec o Display.syncExec para ejecutar el código que modifica la UI. Si una operación debe ejecutarse totalmente en la hebra de la UI, debe llamar a IProgressService#runInUI. De nuevo, la ventaja de este método es que visualizará un diálogo de progreso si la operación queda bloqueada y otorgará el control al usuario.
progressService.runInUI( PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { //realizar trabajo de UI } }, Platform.getWorkspace().getRoot());
El tercer parámetro puede ser nulo o una norma de planificación para la operación. En este caso, se especifica el directorio raíz del área de trabajo que esencialmente bloqueará el área de trabajo mientras se ejecuta esta operación de UI.
También puede registrar con el servicio de progreso un icono para una familia de trabajos, para que la vista de progreso puede mostrar el icono junto al trabajo en ejecución. A continuación figura un ejemplo de cómo se asocia la construcción automática con su icono:
IProgressService service = PlatformUI.getWorkbench().getProgressService(); ImageDescriptor newImage = IDEInternalWorkbenchImages.getImageDescriptor( IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC); service.registerIconForFamily(newImage, ResourcesPlugin.FAMILY_MANUAL_BUILD); service.registerIconForFamily(newImage, ResourcesPlugin.FAMILY_AUTO_BUILD);
IWorkbenchSiteProgressService
añade una API destinada a planificar los trabajos que cambian el aspecto de un componente del entorno de trabajo durante la ejecución del trabajo. Si el conector está ejecutando operaciones en segundo plano que afectan al estado de un componente, puede planificar el trabajo por medio del componente para que el usuario obtenga información acerca de que el componente está ocupado. A continuación se ofrece un ejemplo:
IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class); siteService.schedule(job, 0 /* now */, true /* utilizar cursor medio-ocupado en componente */);
Existe un conjunto de propiedades predefinidas definidas en IProgressConstants
que pueden utilizarse para controlar cómo se muestra un trabajo en la vista de progreso. Estas propiedades pueden utilizarse para indicar a la vista de progreso que conserve (IProgressConstants#KEEP_PROPERTY)
el trabajo en la vista una vez finalizado o que sólo conserve un trabajo (IProgressConstants#KEEPONE_PROPERTY)
a la vez en la vista. También puede asociar una acción (IProgressConstants#ACTION_PROPERTY)
con un trabajo. Si un trabajo tiene ninguna acción asociada, la vista de progreso muestra un hiperenlace para que el usuario pueda ejecutar la acción. También puede averiguar si un trabajo de usuario se está mostrando actualmente en un diálogo de progreso (IProgressConstants#PROPERTY_IN_DIALOG).
En la esquina inferior derecha de la línea de estado se suministra una indicación cuando una acción está disponible. A continuación figura un ejemplo que utiliza estas propiedades:
Job job = new Job("Realizar trabajo") { public IStatus run(IProgressMonitor monitor) { // realizar algún trabajo y conservar sólo el trabajo finalizado en la vista de // progreso si no se ejecuta en el diálogo de progreso Boolean inDialog = (Boolean)getProperty(IProgressConstants.PROPERTY_IN_DIALOG); if(!inDialog.booleanValue()) setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); } }; job.setProperty(IProgressConstants.ICON_PROPERTY, Plugin.getImageDescriptor(WORK_IMAGE)); IAction gotoAction = new Action("Results") { public void run() { // mostrar los resultados } }; job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction); job.setUser(true); job.schedule();
Cuando sea posible, las operaciones de larga ejecución deben ejecutarse fuera de la hebra de la UI. Sin embargo, esto no siempre puede evitarse si el propósito de la operación es actualizar la UI. La sección Elementos de las hebras SWT describe cómo puede realizarse esta operación mediante la operación Display de SWT. El entorno de trabajo define un trabajo especial, UIJob, cuyo método run se ejecuta dentro de un asyncExec SWT. Las subclases de UIJob deben implementar el método runInUIThread en lugar del método run.
WorkbenchJob amplía UIJob para que el trabajo sólo pueda planificarse o ejecutarse cuando el entorno de trabajo está en ejecución. Como siempre, debe evitar un trabajo excesivo en la hebra de la UI, ya que ésta no se renovará durante el tiempo que dure el trabajo de UI.