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.
En primer lugar, veamos las distintas clases de operaciones de fondo que pueden ejecutarse y cómo se muestran en la UI del entorno de trabajo:
Los trabajos iniciados por el usuario son aquéllos que ha desencadenado el usuario. 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. Los trabajos de usuario se distinguen como tales en la API de trabajos al utilizar (Job#setUser). 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.
Los trabajos desencadenados automáticamente tienen sentido para los usuarios pero no son iniciados por el usuario. Estos trabajos se muestran en la vista de progreso y en la línea de estado, pero no se mostrará un diálogo de progreso modal cuando se ejecuten. La construcción automática y la sincronización planificada son ejemplos de este tipo de trabajos.
Las operaciones del sistema no son desencadenadas por el usuario y pueden considerarse como un detalle de implementación de plataforma. Estos trabajos se crean estableciendo el distintivo del sistema utilizando (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 lo siguiente:
Una indicación de que 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 que ha finalizado una operación.
El usuario puede saber fácilmente cuándo finaliza un trabajo de usuario, ya
que el diálogo de progreso se cierra. 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 sugerencia de progreso del componente 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 mediante un diálogo.
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 utilizar algo distinto de
un diálogo para mostrar resultados, con el fin de no interrumpir al usuario. Por ejemplo, podría abrirse una vista cuando el trabajo se inicia y los
resultados se muestran en esta vista sin interrumpir el flujo de trabajo del
usuario. Además, se pueden añadir propiedades de trabajo al trabajo para indicar que debe
mantenerse en la vista de progreso y que proporciona una acción que
mostrará los resultados. En este caso, 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 de 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 conectores utilizan IProgressService#busyCursorWhile
o IProgressService#runInUI.
Además, la vista de progreso proporciona acceso a los trabajos en ejecución.
Informe coherente de progreso por todos los conectores instalados.
La ventaja de utilizar la API de servicio de progreso consiste en que los usuarios obtienen información de progreso coherente.
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 ejecución breve, no se mostrará el diálogo de progreso. Si la operación debe actualizar la UI, siempre puede utilizar un Display.asyncExec o Display.syncExec para ejecutar el código que modifica la UI.
Si una operación debe ejecutarse en su totalidad en la hebra de la UI, debe utilizarse IProgressService#runInUI. Este método también mostrará un diálogo de progreso si la operación queda bloqueada y dará 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 ejemplo, 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 un icono para una familia de trabajos con el servicio de progreso, para que la vista de progreso pueda mostrar el icono junto al trabajo en ejecución. A continuación se muestra un ejemplo de cómo se asocia la familia de trabajos de 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 incluye 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 */);
El entorno de trabajo define las propiedades relacionadas con el progreso para trabajos en IProgressConstants . 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. El siguiente ejemplo utiliza estas propiedades:
Job job = new Job("Realizar trabajo") { public IStatus run(IProgressMonitor monitor) { // realizar un trabajo. // Mantener el trabajo finalizado en la vista de progreso sólo 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.