Já vimos que o quadro da UI de JFace faculta suporte básico para mostrar o progresso das tarefas num diálogo (consulte Operações de longa execução para detalhes). Na Infra-estrutura de simultaneidade, revimos o suporte de tempo de execução da plataforma para simultaneidade e operações de longa execução. Agora veremos como a UI da plataforma aperfeiçoa esta infra-estrutura no pacote
org.eclipse.ui.progress. Este pacote faculta a UI para mostrar progresso de trabalhos na área de trabalho e define suporte adicional para trabalhos que sejam executados no módulo da UI.
Primeiro, vejamos os diferentes tipos de operações em segundo plano que podem estar em execução e como são apresentados na UI da área de trabalho:
Os trabalhos iniciados pelo utilizador são aqueles que o utilizador tenha desencadeado. A área de trabalho mostra automaticamente trabalhos de utilizador num diálogo de progresso modal, com um botão para permitir ao utilizador executar a operação em segundo plano e continuar a trabalhar. É utilizada uma preferência global para indicar se os trabalhos de utilizador devem ser sempre executados em segundo plano. Os trabalhos de utilizador estão distinguidos como tal na API Trabalho com Job#setUser. Dentre os exemplos de trabalhos de utilizador temos construir, dar saída de um projecto, sincronizar com o repositório, exportar um plug-in, e pesquisar.
Os trabalhos desencadeados automaticamente têm significado para os utilizadores mas não são iniciados por eles. Estes trabalhos são mostrados na vista de progresso e na linha de estado, mas não é apresentado nenhum diálogo de progresso modal durante a sua execução. Dentre os exemplos temos construção automática e sincronização agendada.
As operações de sistema não são desencadeadas pelo utilizador e podem ser consideradas um detalhe de implementação da plataforma. Estes trabalhos são criados mediante definição do sinalizador de sistema com Job#setSystem. Dentre os exemplos de trabalhos de sistema temos aqueles que preenchem ociosamente widgets ou que calculam decorações e anotações para vistas.
Perante um ambiente em que várias coisas acontecem ao mesmo tempo, o utilizador precisa do seguinte:
Indicação de que foi iniciada uma operação de longa execução.
Os trabalhos de utilizador são mostrados ao mesmo num diálogo de progresso que
dá feedback imediato, ao passo que os trabalhos desencadeados automaticamente
são mostrados na linha de estado e na vista de progresso. Os trabalhos que afectem uma parte devem ser agendados ou registados junto da parte, de modo a que a área de trabalho possa facultar pistas ao utilizador em como está em execução algo que afecta a parte.
Indicação de que foi terminada uma operação.
O utilizador pode facilmente saber quando termina um trabalho de utilizador porque o diálogo de progresso se fecha. No caso de trabalhos alheios ao utilizador, há dois mecanismos de feedback disponíveis. Se o trabalho estiver agendado ou registado junto de uma parte, a pista de progresso da parte será mostrada quando estiver concluída. Se um trabalho devolver um erro, aparecerá um indicador de erro no canto inferior direito da linha de estado a mostrar uma pista em como ocorreu um erro.
Indicação de novos resultados interessantes ou novas informações, sem desviar o foco como acontece com um diálogo.
Um trabalho de utilizador pode mostrar directamente os resultados ao utilizador quando a operação estiver concluída. No caso de trabalhos alheios ao utilizador, recomenda-se a utilização de outro mecanismo para mostrar resultados, para que o utilizador não seja interrompido. Por exemplo, poderia abrir-se uma vista quando o trabalho iniciasse e os resultados mostrados nesta vista, sem perturbar o fluxo de trabalho do utilizador. Além disso, podem ser adicionadas propriedades de trabalho ao trabalho para indicar que deve ser mantido numa vista de progresso e que faculta uma acção que irá mostrar os resultados. Neste caso, aparece uma indicação de aviso no canto inferior direito da linha de estado, quando um trabalho permanece na vista de progresso e tem resultados a mostrar ao utilizador.
Convicção global de controlo dos acontecimentos, com a capacidade de supervisionar e cancelar operações em segundo plano.
Os trabalhos de utilizador facultam o melhor controlo ao mesmo, dado que são facilmente cancelados e facultam fortes indicações de bloqueios ou operações simultâneas executadas por via do separador Detalhes do diálogo de progresso. Repare que o diálogo de progresso aperfeiçoado que a área Detalhes faculta só é apresentado quando os plug-ins usam IProgressService#busyCursorWhile
ou IProgressService#runInUI.
Além disso, a vista de progresso faculta acesso a trabalhos que estejam em execução.
Comunicação coerente do progresso por parte de todos os plug-ins instalados.
A vantagem de utilizar a API de serviço de progresso é que os utilizadores beneficiam de uma experiência de progresso coerente.
O serviço de progresso da área de trabalho (IProgressService) é a interface principal para o suporte de progresso na área de trabalho. Pode ser obtido junto da área de trabalho e depois usado para mostrar progresso, tanto para operações em segundo plano como para aquelas executadas no módulo da UI. A principal finalidade desta classe consiste em facultar um ponto de compra para operações em execução, evitando assim a necessidade de os programadores de plug-in decidirem qual o mecanismo a utilizar para mostrar progresso em dada situação. Outra vantagem é que o diálogo de progresso apresentado com estes métodos faculta bom suporte para indicar quando uma operação está bloqueada por outra, e dá ao utilizador o controlo de resolver o conflito. Sempre que possível, as operações de longa execução devem ser executadas com IProgressService#busyCursorWhile:
IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); progressService.busyCursorWhile(new IRunnableWithProgress(){ public void run(IProgressMonitor monitor) { //realizar trabalho alheio à UI } });
Este método irá inicialmente mostrar um cursor ocupado, e substituí-lo por um diálogo de progresso se a operação demorar mais do que determinado limiar de tempo. A vantagem deste método sobre o uso de um diálogo de progresso é que este último não é mostrado se a operação for de curta execução. Se a operação vier a actualizar a UI, poderá sempre utilizar Display.asyncExec ou Display.syncExec para executar código que modifique a UI.
Se uma operação vier a ser executada na totalidade no módulo da UI; deverá ser usado IProgressService#runInUI. Este método também apresenta um diálogo de progresso se a operação for bloqueada e dá o controlo ao utilizador.
progressService.runInUI( PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { //realizar trabalho da UI } }, Platform.getWorkspace().getRoot());
O terceiro parâmetro pode ser nulo ou uma regra de agendamento para a operação. Neste exemplo, especificamos a raiz do espaço de trabalho que o irá essencialmente bloquear enquanto for executada esta operação de UI.
Também pode registar um ícone para uma família de trabalhos junto do serviço de progresso, para que a vista de progresso possa mostrar o ícone junto ao trabalho em execução. De seguida é apresentado um exemplo que mostra como a família de trabalhos de construção automática é associada ao seu ícone:
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 inclui uma API para agendadar trabalhos que alterem a aparência de uma parte de área de trabalho enquanto o trabalho estiver em execução. Se o plug-in estiver a executar operações em segundo plano que afectem o estado de uma parte, poderá agendadar o trabalho através da parte e o utilizador receberá feedback de que a parte está ocupada. Segue-se um exemplo:
IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class); siteService.schedule(job, 0 /* now */, true /* utilizar o cursor meio-ocupado na parte */);
A área de trabalho define propriedades relacionadas com progresso para trabalhos em IProgressConstants . Estas podem ser usadas para controlar como um trabalho é mostrado na vista de progresso. Estas podem ser usadas para dizer à vista de progresso que mantenha (IProgressConstants#KEEP_PROPERTY) o trabalho na vista depois deste terminar ou manter somente um único (IProgressConstants#KEEPONE_PROPERTY) trabalho de cada vez na vista. Também é possível associar uma acção IProgressConstants#ACTION_PROPERTY) a um trabalho. Quando um trabalho tiver uma acção associada, a vista de progresso mostra uma hiperligação para que o utilizador possa executar a acção. Também poderá saber se um trabalho está a ser mostrado num diálogo de progresso (IProgressConstants#PROPERTY_IN_DIALOG). Será facultada uma pista no canto inferior direito da linha de estado quando estiver disponível uma acção. O exemplo seguinte utiliza estas propriedades:
Job job = new Job("Realizar Trabalho") { public IStatus run(IProgressMonitor monitor) { // realizar algum trabalho. // Manter o trabalho terminado na vista de progresso somente se não estiver em execução no diálogo de progresso 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 os resultados } }; job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction); job.setUser(true); job.schedule();
Sempre que possível, as operações de longa execução devem ser executadas fora do módulo da UI. Todavia, isto nem sempre pode ser evitado quando a finalidade da operação for exactamente actualizar a UI. A secção Questões de módulos de SWT explica como se pode fazer isto no Ecrã do SWT. A área de trabalho define um trabalho especial, UIJob, cujo método run é executado dentro de um asyncExec de SWT. As subclasses de UIJob devem implementar o método runInUIThread e não o método run.
O WorkbenchJob estende UIJob de modo a que o trabalho só possa ser agendado ou executado quando a área de trabalho estiver em execução. Como sempre, recomenda-se evitar trabalho excessivo no módulo da UI porque esta não será renovada enquanto decorrer o Trabalho de UI (UI Job).