web-dev-qa-db-fra.com

javafx, mettre à jour l'interface utilisateur à partir d'un autre thread

J'ai une application javafx et un thread de travail implémenté via javafx.concurrent.Task, qui exécute un long processus, qui consiste à compresser et à télécharger un ensemble de fichiers.
J'ai connecté la progression de la tâche à une barre de progression via progressProperty.
En plus de cela, je veux qu'un état détaillé de l'élément en cours de traitement soit signalé dans l'interface utilisateur. C'est-à-dire, le nom du fichier en cours de traitement avec sa taille et toute erreur qui pourrait survenir du processus de fichier unique.
La mise à jour de l'interface utilisateur avec ces informations ne peut pas être effectuée à partir du thread de travail, tout au plus je peux l'ajouter à une collection synchronisée.
Mais j'ai besoin d'un événement pour informer l'interface utilisateur que de nouvelles données sont disponibles.
Javafx a-t-il un support spécifique pour ce problème?

mise à jour, meilleure formulation

Au lieu de concevoir un mécanisme cross-thread ad hoc comme Platform.runLater, j'essaie de permettre à chaque propriété d'être écoutée à partir d'autres threads. Tout comme runningProperty et stateProperty fournis par Task

30
AgostinoX

Je rencontre un problème similaire, pour autant que je sache, vous devez gérer vous-même l'erreur. Ma solution consiste à mettre à jour l'interface utilisateur via un appel de méthode:

Quelque chose comme:

  try
  {
    //blah...
  }
  catch (Exception e)
  {
    reportAndLogException(e);
  }
  ...
  public void reportAndLogException(final Throwable t)
  {
    Platform.runLater(new Runnable() {
      @Override public void run() {
        //Update UI here     
      }
    });
  }

Essentiellement, je ne fais que le déplacer manuellement vers le thread d'interface utilisateur pour une mise à jour (comme je le ferais dans à peu près n'importe quel autre cadre).

34
Daniel B. Chapman

Cette réponse utilise le même concept que la réponse de Daniel.

Vous trouverez ci-joint une copie de l'exemple de résultat partiel du tâche javadoc (corrigé pour les erreurs de syntaxe actuellement intégrées dans le Java 8 javadoc et pour ajouter des types génériques plus spécifiques). Vous pouvez utiliser une modification de cela.

Placez vos exceptions dans la collection partialResults. Dans votre cas, vous n'avez pas besoin de renvoyer la liste des exceptions de la tâche, mais vous pouvez les placer à la place dans un contrôle d'interface utilisateur qui affiche les exceptions (comme un ListView avec un CellFactory pour affichage des exceptions). Notez que la collection partialResults n'a pas eu besoin d'être synchronisée car elle est toujours mise à jour et accessible sur le thread JavaFX UI (la mise à jour se fait via un appel Platform.runLater() similaire à la solution de Daniel).

public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
    private ReadOnlyObjectWrapper<ObservableList<Rectangle>> partialResults =
            new ReadOnlyObjectWrapper<>(
                    this, 
                    "partialResults",
                    FXCollections.observableArrayList(
                            new ArrayList<>()
                    )
            );

    public final ObservableList<Rectangle> getPartialResults() {
        return partialResults.get();
    }

    public final ReadOnlyObjectProperty<ObservableList<Rectangle>> partialResultsProperty() {
        return partialResults.getReadOnlyProperty();
    }

    @Override
    protected ObservableList<Rectangle> call() throws Exception {
        updateMessage("Creating Rectangles...");
        for (int i = 0; i < 100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);
            Platform.runLater(() -> partialResults.get().add(r));
            updateProgress(i, 100);
        }
        return partialResults.get();
    }
}

Lors de la mise à jour de ses propriétés observables, la tâche vérifie d'abord si la mise à jour se produit sur le thread d'application FX. Si c'est le cas, il effectue une mise à jour immédiate. Si ce n'est pas le cas, il encapsule la mise à jour dans un appel à Platform.runLater(). Voir code source de la tâche pour comprendre comment cela se fait.

Il serait peut-être possible de définir un ensemble de propriétés génériques simultanées, mais JavaFX ne fournit pas de telles fonctionnalités à la base. En effet, il n'en a pas besoin. À l'exception du package javafx.concurrent, JavaFX est une infrastructure d'interface utilisateur à thread unique.

7
jewelsea