简体   繁体   English

从线程返回图像

[英]Return an image from a thread

I am basically trying to have a user input a filepath from the a FileChooser then pass that to task so it can be manipulated and turned into an image that can display in a JavaFX ImageView. 我基本上是想让用户从FileChooser输入文件路径,然后将其传递给任务,以便可以对其进行操作并将其转换为可以在JavaFX ImageView中显示的图像。 My problem is that I have the task running in a thread and my method doesn't wait until the thread has finished executing to return the image needed. 我的问题是,我的任务正在线程中运行,而我的方法要等到线程完成执行以返回所需的图像后,才能执行。 Is there any way of achieving this? 有什么办法可以做到这一点?

//JavaFX Controller with fileChooser //带有fileChooser的JavaFX Controller

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 Controller for progressbar popup //用于进度条弹出的JavaFX Controller

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");
        }
    }
}

A fairly simple change to your code that would allow you to do this would be: 对代码进行的相当简单的更改将允许您执行以下操作:

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();

    }

    // ...
}

and then simply 然后简单地

@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));
}

But I think you probably want to refactor this, so that it properly separates concerns (single responsibility principle). 但是我认为您可能希望重构它,以使其适当地分离关注点(单一责任原则)。 Essentially, your problem arises because you don't have access to the task at the point where you have access to the image view; 本质上,出现问题是因为您在有权访问图像视图的那一刻无法访问任务。 this is because the task is (incorrectly) encapsualted by the progress bar controller. 这是因为进度条控制器(错误地)封装了任务。 Your progress bar controller shouldn't really be responsible for loading the image (again: single responsibility principle); 您的进度条控制器不应真正负责加载图像(再次:单一责任原则); you can just let it expose the progress property from the progress bar. 您可以让它从进度条中显示progress属性。 Something like: 就像是:

public class ProgressBarController {

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

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

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

}

Promote the UploadTask to a standalone class: 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");
    }
}

And now do 现在做

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);
    }
}

Note that I cleaned up various aspects of the code for common-or-garden best practice. 请注意,我清理了代码的各个方面,以实现常见或最佳实践。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM