从线程返回图像

时间:2017-11-22 19:03:19

标签: java multithreading javafx return

我基本上试图让用户从FileChooser输入文件路径然后将其传递给任务,以便可以对其进行操作并将其转换为可以在JavaFX ImageView中显示的图像。我的问题是我在线程中运行任务,并且我的方法不会等到线程完成执行以返回所需的图像。有没有办法实现这个目标?

//带有fileChooser的JavaFX控制器

public class JavaCVTestController implements Initializable{

    @FXML
    ImageView image;
    @FXML
    Button btnUpload;



    FileChooser fileChooser = new FileChooser();
    String selectedFile = null;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.image.setPreserveRatio(true);
    }

    @FXML
    private void uploadImage(ActionEvent Event) throws IOException, Exception{



        selectedFile = fileChooser.showOpenDialog(null).getAbsolutePath();
        System.out.println(selectedFile);

        FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressBar.fxml"));
        Parent root = (Parent) loader.load();

        ProgressBarController pBarController = loader.getController();


        Stage stage = new Stage();
        stage.setScene(new Scene(root));
        stage.show();
        image.setImage(pBarController.loadImage2(selectedFile));
    }
}

//用于进度条弹出的JavaFX控制器

public class ProgressBarController implements Initializable{

    @FXML
    private ProgressBar pBar;
    @FXML
    private Label task;


    RandomAccessFile raf;
    TiffDecoder decoder;
    BufferedImage bufferedImage;
    Image image;


    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }



    public Image loadImage2 (String filePath) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(1);

        Task<Image> task = new UploadTask(filePath);
        pBar.progressProperty().bind(task.progressProperty());
        this.task.textProperty().bind(task.messageProperty());
        Thread thread = new Thread(task);
        thread.start();
        return task.get();
            //NEED TO RETURN IMAGE back to previous controller somehow without interrupting the task

    }




    private class UploadTask extends Task<Image> {

        private String filePath;
        private RandomAccessFile raf;
        private TiffDecoder decoder;
        private BufferedImage bufferedImage;
        private Image image;

        public UploadTask(String filePath){
            this.filePath = filePath;
        }

        @Override
        protected Image call() throws Exception {
            updateMessage("Loading file into memory");
            generateRaf(filePath);
            updateProgress(10, 100);

            updateMessage("Decoding image format");
            decodeTiff(raf);
            updateProgress(30, 100);

            updateMessage("Buffering image to stream");
            bufferImage(decoder);
            updateProgress(600, 100);

            updateMessage("Converting to image");
            image = convertImageToFXImage(bufferedImage);
            updateProgress(90, 100);

            updateMessage("Finished");
            updateProgress(100, 100);

            return image;
        }

        private void generateRaf(String filePath) throws FileNotFoundException{
            this.raf = new RandomAccessFile(filePath, "r");
        }

        private void decodeTiff(RandomAccessFile raf) throws Exception{
            this.decoder = new TiffDecoder(raf);
        }

        private void bufferImage(TiffDecoder decoder) throws Exception{
            bufferedImage = decoder.read();
        }

        private Image convertImageToFXImage(BufferedImage bufferedImage){
            return SwingFXUtils.toFXImage(bufferedImage, null);
        }

        @Override
        protected void failed() {
            System.out.println("Upload failed");
        }

        @Override
        protected void succeeded() {
            System.out.println("Upload succeeded");
        }
    }
}

1 个答案:

答案 0 :(得分:1)

对您的代码进行相当简单的更改将允许您执行此操作:

public class ProgressBarController {

    // ...

    public ReadOnlyObjectProperty<Image> loadImage2 (String filePath) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(1);

        Task<Image> task = new UploadTask(filePath);
        pBar.progressProperty().bind(task.progressProperty());
        this.task.textProperty().bind(task.messageProperty());
        Thread thread = new Thread(task);
        thread.start();
        return task.valueProperty();

    }

    // ...
}

然后简单地

@FXML
private void uploadImage(ActionEvent Event) throws IOException, Exception{



    selectedFile = fileChooser.showOpenDialog(null).getAbsolutePath();
    System.out.println(selectedFile);

    FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressBar.fxml"));
    Parent root = (Parent) loader.load();

    ProgressBarController pBarController = loader.getController();


    Stage stage = new Stage();
    stage.setScene(new Scene(root));
    stage.show();
    image.imageProperty().bind(pBarController.loadImage2(selectedFile));
}

但我认为你可能想要重构这一点,以便正确区分问题(单一责任原则)。基本上,您的问题出现是因为您无权访问图像视图的访问权限;这是因为进度条控制器(错误地)封装了任务。您的进度条控制器不应该真正负责加载图像(再次:单一责任原则);你可以让它从进度条中公开progress属性。类似的东西:

public class ProgressBarController {

    @FXML
    private ProgressBar pBar;
    @FXML
    private Label task;

    public DoubleProperty progressProperty() {
        return pBar.progressProperty();
    }

    public StringProperty textProperty() {
        return task.textProperty();
    }

}

UploadTask提升为独立类:

public class UploadTask extends Task<Image> {

    private File file ;
    private BufferedImage bufferedImage ;

    public UploadTask(File file){
        this.file = file;
    }

    @Override
    protected Image call() throws Exception {
        updateMessage("Loading file into memory");
        RandomAccessFile raf = generateRaf(file);
        updateProgress(10, 100);

        updateMessage("Decoding image format");
        TiffDecoder decoder = decodeTiff(raf);
        updateProgress(30, 100);

        updateMessage("Buffering image to stream");
        BufferedImage bufferedImage = bufferImage(decoder);
        updateProgress(600, 100);

        updateMessage("Converting to image");
        Image image = convertImageToFXImage(bufferedImage);
        updateProgress(90, 100);

        updateMessage("Finished");
        updateProgress(100, 100);

        return image;
    }

    private RandomAccessFile generateRaf(File file) throws FileNotFoundException{
        return new RandomAccessFile(file, "r");
    }

    private TiffDecoder decodeTiff(RandomAccessFile raf) throws Exception{
        return new TiffDecoder(raf);
    }

    private BufferedImage bufferImage(TiffDecoder decoder) throws Exception{
       return decoder.read();
    }

    private Image convertImageToFXImage(BufferedImage bufferedImage){
        return SwingFXUtils.toFXImage(bufferedImage, null);
    }

    @Override
    protected void failed() {
        System.out.println("Upload failed");
    }

    @Override
    protected void succeeded() {
        System.out.println("Upload succeeded");
    }
}

现在做

public class JavaCVTestController {

    @FXML
    private ImageView image;
    @FXML
    private Button btnUpload;

    private ExecutorService exec = Executors.newCachedThreadPool();

    private FileChooser fileChooser = new FileChooser();


    // can't you do this in the FXML?
    public void initialize(URL location, ResourceBundle resources) {
        this.image.setPreserveRatio(true);
    }

    @FXML
    private void uploadImage(ActionEvent Event) throws IOException, Exception{



        File selectedFile = fileChooser.showOpenDialog(image.getScene().getWindow());

        FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressBar.fxml"));
        Parent root = (Parent) loader.load();

        ProgressBarController pBarController = loader.getController();

        Task<Image> uploadTask = new UploadTask(selectedFile);
        pBarController.progressProperty().bind(uploadTask.progressProperty());
        pBarController.textProperty().bind(uploadTask.messageProperty());

        uploadTask.setOnSucceeded(e -> image.setImage(uploadTask.getValue()));

        Stage stage = new Stage();
        stage.setScene(new Scene(root));
        stage.show();

        exec.execute(uploadTask);
    }
}

请注意,我清理了常见或园艺最佳实践的代码的各个方面。