簡體   English   中英

從線程返回圖像

[英]Return an image from a thread

我基本上是想讓用戶從FileChooser輸入文件路徑,然后將其傳遞給任務,以便可以對其進行操作並將其轉換為可以在JavaFX ImageView中顯示的圖像。 我的問題是,我的任務正在線程中運行,而我的方法要等到線程完成執行以返回所需的圖像后,才能執行。 有什么辦法可以做到這一點?

//帶有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

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

對代碼進行的相當簡單的更改將允許您執行以下操作:

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

請注意,我清理了代碼的各個方面,以實現常見或最佳實踐。

暫無
暫無

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

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