簡體   English   中英

JavaFX:更新多個任務的進度

[英]JavaFX: updating progress for the multiple tasks

我正在用JavaFX 2.2編寫一個多線程分形繪圖程序,現在我需要一些指導。

我想要實現的是創建一個任務或服務(還沒有決定),然后啟動一些實際進行計算的其他任務,並在准備好時返回整個圖像的部分。 當所有部分都返回到啟動任務時,它將這些部分放在一起並將其返回到主線程,以便可以將其可視化。

顯然,所有這一切必須在沒有阻止UI的情況下發生。

問題是我無法弄清楚這些任務如何相互通信。 例如,我需要根據其中任務的平均進度(或類似的東西)更新啟動任務的progress屬性,因此它們的進度屬性應該以某種方式綁定到啟動任務的progress屬性。 圖像塊應放在列表或某個容器中,並在所有圖像可用時重新繪制在單獨的圖像上。

我已經編寫了一個更簡單的(雖然仍然是實驗性的)該程序版本,它只創建了一個計算整個分形的任務。 進度綁定到GUI的progressBar。 在任務成功時,返回值由EventHandler處理。

我不是要求一個完整的解決方案,但一些想法可能只是一些示例代碼將真正幫助我。

這是應該修改的類:

package fractal;

import fractalUtil.DefaultPalette;
import fractalUtil.PaletteInterface;
import javafx.concurrent.Task;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import org.apache.commons.math3.complex.Complex;

/**
 *
 * @author athelionas
 */
public abstract class AbstractFractal extends Task implements FractalInterface {

    private PaletteInterface palette;
    protected final int width, height, order, iterations;
    protected final double scale, xReal, xIm, xCenter, yCenter, zoom;
    protected final boolean julia;

    protected AbstractFractal(final int width, final int height, final double xReal, final double xIm, final double xCenter, final double yCenter, final int order, final boolean julia, final int iterations, final double zoom) {
        this.width = width;
        this.height = height;
        this.xReal = xReal;
        this.xIm = xIm;
        this.xCenter = xCenter;
        this.yCenter = yCenter;
        this.order = order;
        this.julia = julia;
        this.iterations = iterations;
        this.zoom = zoom;
        this.scale = (double) width / (double) height;
        palette = new DefaultPalette();
    }

    @Override
    public final void setPalette(final PaletteInterface palette) {
        this.palette = palette;
    }

    @Override
    public abstract Complex formula(final Complex z, final Complex c, final int order, final Complex center);

    @Override
    public final Color calculatePoint(final Complex z, final Complex c, final int order, final Complex center, final int iterations) {
        Complex zTemp = z;
        int iter = iterations;
        while (zTemp.abs() <= 2.0 && iter > 0) {
            zTemp = formula(zTemp, c, order, center);
            iter--;
        }
        if (iter == 0) {
            return Color.rgb(0, 0, 0);
        } else {
            return palette.pickColor((double) (iterations - iter) / (double) iterations);
        }
    }

    @Override
    public final WritableImage call() {
        Complex z;
        Complex c;
        Complex center = new Complex(xCenter, yCenter);
        final WritableImage image = new WritableImage(width, height);

        if (julia) {
            c = new Complex(xReal, xIm);
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {

                    z = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));

                    image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
                }

            }
        } else {
            z = new Complex(xReal, xIm);
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {

                    c = new Complex(((double) x) / (double) (width - 1) * 2.0 * scale * (1.0 / zoom) - scale * (1.0 / zoom), ((double) y) / (double) (height - 1) * 2.0 * (1.0 / zoom) - 1.0 * (1.0 / zoom));

                    image.getPixelWriter().setColor(x, y, calculatePoint(z, c, order, center, iterations));
                }
                updateProgress(y, height);
            }
        }
        return image;
    }
}

使用綁定和Task 這樣您根本不需要關心線程。 您所需要的只是創建一個綁定,它將根據線程數對每個進度進行規范化並匯總它們。 例如

progressBar.progressProperty().bind(
    task1.progressProperty().multiply(0.5).add(
         task2.progressProperty().multiply(0.5)));

對於未知數量的線程來說,這有點棘手。 見下一個例子:

public class MultiProgressTask extends Application {
    private static final int THREADS_NUM = 10;

    // this is our Task which produces a Node and track progress
    private static class MyTask extends Task<Node> {

        private final int delay = new Random().nextInt(1000) + 100;
        { System.out.println("I update progress every " + delay); }

        @Override
        protected Node call() throws Exception {
            updateProgress(0, 5);
            for (int i = 0; i < 5; i++) {
                System.out.println(i);
                Thread.sleep(delay); // imitating activity
                updateProgress(i+1, 5);
            }
            System.out.println("done");
            return new Rectangle(20, 20, Color.RED);
        }
    };

    @Override
    public void start(Stage primaryStage) {
        ProgressBar pb = new ProgressBar(0);
        pb.setMinWidth(300);

        final VBox root = new VBox();
        root.getChildren().add(pb);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();

        DoubleBinding progress = null;

        for (int i = 0; i < THREADS_NUM; i++) {
            final MyTask mt = new MyTask();

            // here goes binding creation
            DoubleBinding scaledProgress = mt.progressProperty().divide(THREADS_NUM);
            if (progress == null) {
                progress = scaledProgress;
            } else {
                progress = progress.add(scaledProgress);
            }
            // here you process the result of MyTask
            mt.setOnSucceeded(new EventHandler<WorkerStateEvent>() {

                @Override
                public void handle(WorkerStateEvent t) {
                    root.getChildren().add((Node)t.getSource().getValue());
                }
            });
            new Thread(mt).start();
        }
        pb.progressProperty().bind(progress);
    }


    public static void main(String[] args) { launch(args); }
}

這是一個非常有趣的問題:)

如果我們暫時刪除線程安全問題,您可以傳入double屬性(或者任何progress屬性綁定到的)並使用進度更新,然后更新進度指示器。 兩個問題:

  1. 多個任務可以同時增加屬性。
  2. 必須在javafx線程上觸發更改。

我會用一個簡單的API將屬性包裝在它自己的類中:

class ProgressModel {
    private final SimpleDoubleProperty progress;
    public void increment(finally double increment) {
        Platform.runLater(new Runnable() {
            progress.set(progress.doubleValue() + increment);
        }
    }

    public void bindPropertyToProgress(DoubleProperty property) {
        property.bind(progress);
    }
}

在上面的代碼中,所有更新都將按順序在javafx線程上運行,因此它是線程安全加上沒有鎖。 我已經完成了類似的后台任務,性能一直很好(實時到用戶的眼睛)雖然如果你每秒更新數千次,可能不是這樣! 你只需要衡量。 我沒有展示鍋爐板代碼,使其更具可讀性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM