Es wurde bereits dargestellt, dass das JFace-Benutzerschnittstellengerüst eine Fortschrittsanzeige für Tasks in einem Dialog grundlegend unterstützt (weitere Details finden Sie unter Lang andauernde Operationen). Unter Infrastruktur für gemeinsamen Zugriff wurde die Laufzeitunterstützung der Plattform für gleichzeitigen Zugriff und lang andauernde Operationen erläutert. An dieser Stelle soll schließlich untersucht werden, wie die Benutzerschnittstelle der Plattform diese Infrastruktur im Paket
org.eclipse.ui.progress noch verbessert. Dieses Paket liefert die Benutzerschnittstelle zur Anzeige des Jobverlaufs in der Workbench und definiert zusätzliche Unterstützung für Jobs, die im Benutzerschnittstellenthread ausgeführt werden.
Vor der Einführung der neuen APIs werden zunächst einmal ein paar Konzepte erläutert. Als erstes muss zwischen verschiedenen Arten von Hintergrundoperationen unterschieden werden:
Vom Benutzer eingeleitet. Diese Jobs werden als Benutzer-Jobs eingestellt (Job#setUser). Die Workbench zeigt Benutzer-Jobs automatisch in einem modalen Fortschrittsdialog an, der eine Schaltfläche enthält, über die der Benutzer die Operation im Hintergrund ausführen und mit seiner Arbeit fortfahren kann. In einer globalen Benutzervorgabe wird angegeben, ob Benutzer-Jobs immer im Hintergrund laufen sollen.
Beispiele für Benutzer-Jobs sind Erstellungsprozesse, die Entnahme von Projekten, Synchronisierungen mit dem Repository, das Exportieren von Plug-ins und Suchvorgänge.Automatisch ausgelöst. Diese Operationen sind für den Benutzer von Bedeutung, werden von ihm aber nicht eingeleitet. Dies ist die Standardvarietät von Jobs. Diese Jobs werden zwar in der Sicht für Verarbeitungsfortschritt und der Statuszeile angezeigt, im modalen Fortschrittsdialog erscheinen sie während Ihrer Ausführung jedoch nicht. Beispiele hierfür sind automatische Erstellfunktionen und terminierte Synchronisierungen.
Systemoperationen. Operationen, die nicht durch den Benutzer ausgelöst werden und als Detail der Implementierung angesehen werden können. Diese Jobs werden durch Einstellung der Markierung 'System' (Job#setSystem) erstellt. Beispiele für System-Jobs sind Jobs, die Fensterobjekte langsam ausfüllen oder Dekoratoren und Anmerkungen für Sichten berechnen.
In einer gegebenen Umgebung, in der mehrere Dinge gleichzeitig passieren können, benötigt der Benutzer:
Eine Meldung, wenn ein lang andauernder Prozess gestartet wurde.
Benutzer-Jobs werden dem Benutzer in einem Fortschrittsdialog angezeigt, der eine unmittelbare Rückmeldung erstattet, während automatisch ausgelöste Jobs in der Statuszeile und der Sicht für den Verarbeitungsfortschritt angezeigt werden. Außerdem sollten Jobs, die eine Komponente beeinflussen, bei der Komponente terminiert oder registriert werden, damit die Workbench dem Benutzer Hinweise darüber liefern kann, das zur Zeit etwas läuft, das die Komponente beeinflusst.
Eine Meldung, wenn eine Operation beendet wurde.
Bei Benutzer-Jobs weiß der Benutzer sofort bescheid, wenn sie beendet sind, da der Fortschrittsdialog geschlossen wird. Für die anderen Jobtypen stehen mehrere Feedbackmechanismen zur Verfügung. Wenn der Job bei einer Komponente terminiert oder registriert wurde, so gibt der Fortschrittshinweis der Komponente Rückmeldung, wenn er beendet wurde. Gibt ein Job einen Fehler zurück, so erscheint unten rechts in der Statuszeile ein Fehleranzeiger, der einen Hinweis über den aufgetretenen Fehler enthält.
Eine Meldung über interessante neue Ergebnisse oder neue Informationen, ohne dass der Fokus verschoben wird, indem Dialoge von einer Hintergrundoperation angezeigt werden.
Ein Benutzer-Job kann dem Benutzer die Ergebnisse bei Beendigung der Operation direkt anzeigen. Für die anderen Jobtypen wird empfohlen, den Benutzer nicht durch einen Dialog zu unterbrechen. Wenn die Jobs in einer Sicht angezeigt werden, kann stattdessen die Sicht bei Beginn des Jobs geöffnet werden, so dass die Ergebnisse dann in dieser Sicht angezeigt werden. Hierdurch wird der Arbeitsablauf des Benutzer nicht unterbrochen. Darüber hinaus können Sie dem Job Eigenschaften hinzufügen, um anzuzeigen, dass die Fortschrittssicht beibehalten werden soll und der Job über eine Aktion verfügt, die die Ergebnisse anzeigt. Eine Warnmeldung erscheint in der unteren rechten Ecke der Statuszeile, wenn ein Job in der Fortschrittssicht bleibt und Ergebnisse hat, die dem Benutzer angezeigt werden können.
Ein allgemeines Gefühl, die Kontrolle über alle ausgeführten Prozesse zu haben, sowie die Fähigkeit, Hintergrundoperationen zu überwachen und abzubrechen.
Benutzer-Jobs bieten dem Benutzer die beste Form der Kontrolle, da sie leicht abgebrochen werden können und über die Registerkarte Details des Fortschrittdialogs einen deutlichen Hinweis auf möglicherweise blockierende oder gleichzeitige Operationen liefern. Beachten Sie, dass ein erweiterter Fortschrittdialog mit dem Bereich Details nur angezeigt wird, wenn Benutzer IProgressService#busyCursorWhile
oder IProgressService#runInUI aufrufen.
Darüber hinaus bietet die Sicht für Verarbeitungsfortschritt Zugriff auf laufende Jobs.
Alle installierten Plug-ins müssen in der Anzeige des Fortschritts konsistent sein.
Ein Vorteil der Verwendung der Fortschrittservice-API besteht darin, dass Benutzer an ein konsistentes Fortschrittverhalten gewöhnt werden.
Als nächstes wird detailliert dargestellt, wie die neuen APIs verwendet werden können.
Der Fortschrittservice der Workbench (IProgressService) ist die Hauptschnittstelle zur Fortschrittunterstützung der Workbench. Er kann aus der Workbench abgerufen und dann dazu verwendet werden, den Fortschritt für Operationen sowohl im Hintergrund als auch im Benutzerschnittstellenthread anzuzeigen. Die Hauptaufgabe dieser Klasse ist die Bereitstellung eines einzelnen Zugriffspunktes für laufende Operationen, wodurch Plug-in-Entwickler nicht mehr entscheiden müssen, welcher Mechanismus in einer bestimmten Situation für die Fortschrittsanzeige verwendet werden soll. Ein weiterer Vorteil besteht darin, dass der über diese Methoden dargestellte Fortschrittdialog eine gute Unterstützung für die Anzeige, ob eine Operation durch eine andere blockiert wird, darstellt. Darüber hinaus bietet er dem Benutzer die Möglichkeit, den Konflikt zu beheben. Wenn möglich, sollten lang andauernde Operationen immer über IProgressService#busyCursorWhile ausgeführt werden:
IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); progressService.busyCursorWhile(new IRunnableWithProgress(){ public void run(IProgressMonitor monitor) { //do non-UI work } });
Diese Methode zeigt zunächste einen aktiven Cursor an und ersetzt diesen dann durch einen Fortschrittdialog, wenn die Operation länger dauert, als in einem vorbestimmten Schwellenzeitwert angegeben. Der Vorteil dieser Methode im Gegensatz zur Verwendung eines Fortschrittdialogs besteht darin, dass der Fortschrittdialog für schnell durchlaufende Operationen nicht angezeigt wird. Wenn Ihre Operation die Benutzerschnittstelle aktualisieren muss, können Sie immer Display.asyncExec oder Display.syncExec übergeben, um den Code auszuführen, der die Benutzerschnittstelle ändert. Wenn eine Operation vollständig im Benutzerschnittstellenthread ausgeführt werden soll, sollten Sie IProgressService#runInUI aufrufen. Wiederum besteht der Vorteil dieser Methode darin, dass ein Fortschrittdialog angezeigt wird, wenn die Operation blockiert ist und der Benutzer so die Kontrolle erlangt.
progressService.runInUI( PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { //do UI work } }, Platform.getWorkspace().getRoot());
Der dritte Parameter kann den Wert Null oder eine Zeitplanungsregel für die Operation annehmen. In diesem Fall wird das Stammverzeichnis des Arbeitsbereichs angegeben, wodurch der Arbeitsbereich gesperrt wird, während diese Benutzerschnittstellenoperation ausgeführt wird.
Sie können den Fortschrittservice auch mit dem Symbol einer Jobfamilie registrieren, damit in der Sicht für Verarbeitungsfortschritt das Symbol neben dem laufenden Job angezeigt wird. Im folgenden Beispiel wird die automatische Erstellfunktion dem entsprechenden Symbol zugeordnet:
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
bietet API für die Zeitplanung von Jobs, die während der Ausführung die Darstellung einer Workbench-Komponente ändern. Wenn Ihr Plug-in Hintergrundoperationen ausführt, die den Status einer Komponente beeinflussen, können Sie den Job über die Komponente terminieren, so dass der Benutzer eine Rückmeldung erhält, dass die Komponente ausgelastet ist. Hier ein Beispiel:
IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class); siteService.schedule(job, 0 /* now */, true /* use half-busy cursor in part */);
In IProgressConstants
wird ein Set von vordefinierten Eigenschaften definiert, über die gesteuert werden kann, wie ein Job in der Sicht für Verarbeitungsfortschritt angezeigt wird. Hierüber kann der Sicht für Verarbeitungsfortschritt mitgeteilt werden, dass Ihr Job nach Beendigung in der Sicht erhalten bleiben soll (IProgressConstants#KEEP_PROPERTY)
oder dass nur ein Job auf einmal in der Sicht angezeigt werden soll (IProgressConstants#KEEPONE_PROPERTY). Sie können einem Job auch eine Aktion zuordnen (IProgressConstants#ACTION_PROPERTY). Wenn einem Job eine Aktion zugeordnet ist, wird in der Sicht für Verarbeitungsfortschritt ein Hyperlink angezeigt, über den der Benutzer die Aktion ausführen kann. Sie können auch feststellen, ob ein Benutzer-Job zur Zeit in einem Fortschrittdialog angezeigt wird (IProgressConstants#PROPERTY_IN_DIALOG).
In der rechten unteren Ecke der Statuszeile wird ein Hinweis angezeigt, wenn eine Aktion zur Verfügung steht. Das folgende Beispiel verwendet diese Eigenschaften:
Job job = new Job("Do Work") { public IStatus run(IProgressMonitor monitor) { // do some work then only keep the finished job in the progress view if // not running in the progress dialog 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() { // show the results } }; job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction); job.setUser(true); job.schedule();
Wenn möglich, sollten lang andauernde Operationen außerhalb des Benutzerschnittstellenthreads ausgeführt werden. Dies kann allerdings nicht immer vermieden werden, wenn Zweck der Operation eine Aktualisierung der Benutzerschnittstelle ist. Hinweise zur Verwendung von SWT-Threads erklärt, wie dies über die SWT-Anzeige bewerkstelligt werden kann. Die Workbench definiert einen speziellen Job namens UIJob, dessen Methode 'run' innerhalb einer SWT-asyncExec läuft. Unterklassen von UIJob sollten die Methode runInUIThread statt der Methode run implementieren.
WorkbenchJob erweitert UIJob, so dass der Job nur terminiert oder ausgeführt werden kann, wenn die Workbench läuft. Sie sollten wie immer übermäßige Nutzung des Benutzerschnittstellenthreads vermeiden, da die Benutzerschnittstelle während der Ausführung eines Benutzerschnittstellen-Jobs nicht aktualisiert wird.