-1

I have a method that allows the user to select an image from the file system. Part of my layout, including the Toolbar and MenuBar are created in FXML, but the main Image, ImageView, BorderPane and ScrollPane are set in the Main Application Class. Previously, I was able to show the selected image in the appropriate ImageView, but now it is just invisible. I tested to see if the image was being retrieved correctly, and sure enough I can retrieve its file path and width when debugging, but the screen still doesn't show the image. What is going wrong?

FXML:

<VBox prefHeight="600" prefWidth="800" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">

    <VBox alignment="TOP_CENTER">
        <ToolBar minHeight="50.0" prefHeight="50.0" prefWidth="800.0" stylesheets="@style.css" GridPane.rowIndex="1">
            <ImageView fitHeight="35.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
                <Image url="@react-toolbar-logo.png" />
            </ImageView>
        </ToolBar>
        <MenuBar fx:id="menuBar" prefHeight="0.0" prefWidth="0.0" />
    </VBox>

</VBox>

Main Application Class:

public class Main extends Application {

    private Image mainImage;
    private final Group selectionGroup = new Group();

    @Override
    public void start(Stage primaryStage) throws Exception {

        // Basic stage settings
        primaryStage.setTitle("Picture Viewer");
        primaryStage.setResizable(true);

        // Set task bar primary icon
        primaryStage.getIcons().add(new javafx.scene.image.Image("main/react-app-icon.png"));

        // Declare UI variables
        final BorderPane borderPane = new BorderPane();
        final ScrollPane scrollPane = new ScrollPane();
        final Scene scene = new Scene(borderPane, 800, 600);

        ImageView mainImageView = new ImageView();

        // Select main layout file
        FXMLLoader loader = new  FXMLLoader(getClass().getResource("/main/scene.fxml"));
        loader.setController(new MainController(primaryStage, selectionGroup, mainImage, mainImageView));
        Parent root = loader.load();

        // Add custom stylesheet URL
        scene.getStylesheets().add("main/style.css");

        // Set UI element properties
        selectionGroup.getChildren().add(mainImageView);
        scrollPane.setContent(selectionGroup);
        scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);

        // Show primary stage
        // primaryStage.setScene(scene);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();

    }

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

}

Main Controller:

class MainController implements Initializable {

    private Stage primaryStage;
    private Group selectionGroup;

    private Image mainImage;
    private ImageView mainImageView;

    MainController(Stage primaryStage, Group selectionGroup, Image mainImage, ImageView mainImageView) {
        this.primaryStage = primaryStage;
        this.selectionGroup = selectionGroup;
        this.mainImage = mainImage;
        this.mainImageView = mainImageView;
    }

    private boolean isAreaSelected = false;
    private final AreaSelection areaSelection = new AreaSelection();

    @FXML
    private
    MenuBar menuBar;

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

        // Declare menus
        final Menu menu1 = new Menu("File");
        final Menu menu2 = new Menu("Options");

        // Define menu 1 items
        final MenuItem open = new MenuItem("Open");
        final MenuItem clear = new MenuItem("Clear");
        final MenuItem exit = new MenuItem("Exit");

        // Define menu 2 items
        // final MenuItem select = new MenuItem("Select Area");
        final MenuItem crop = new MenuItem("Crop & Upload");
        final MenuItem clearSelectionItem = new MenuItem("Clear Selection");

        // Set menu items
        menu1.getItems().addAll(open, clear, exit);
        menu2.getItems().addAll(crop, clearSelectionItem);

        // Set menu click events
        setMenu1ClickEvents(primaryStage, open, clear, exit);
        setMenu2ClickEvents(crop, clearSelectionItem);

        // Instantiate menus
        menuBar.getMenus().addAll(menu1, menu2);

    }

    private void setMenu1ClickEvents(Stage primaryStage, MenuItem open, MenuItem clear, MenuItem exit) {
        // Open file system to select image
        open.setOnAction(event -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Open Image File");
            fileChooser.getExtensionFilters().addAll(
                    new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg"));

            File selectedFile = fileChooser.showOpenDialog(primaryStage);

            if (selectedFile != null) {
                clearSelection(selectionGroup);
                this.mainImage = convertFileToImage(selectedFile);
                System.out.println("selectedFile: " + selectedFile);
                mainImageView.setImage(mainImage);
                System.out.println("mainImage.getWidth(): " + mainImage.getWidth());
                changeStageSizeImageDimensions(primaryStage, mainImage);
            }
        });

        // Clear the current image
        clear.setOnAction(event -> {
            clearSelection(selectionGroup);
            mainImageView.setImage(null);
            System.gc();
        });

        // Exit the application
        exit.setOnAction(event -> {
            Platform.exit();
            System.exit(0);
        });
    }

    private void setMenu2ClickEvents(MenuItem crop, MenuItem clearSelectionItem) {
        // Set menu 2 click events

        crop.setOnAction(event -> {
            if (isAreaSelected()) {
                cropImage(areaSelection.selectArea(selectionGroup).getBoundsInParent(), mainImageView);
            }
        });

        clearSelectionItem.setOnAction(event -> clearSelection(selectionGroup));
    }
}

convertFileToImage:

private Image convertFileToImage(File imageFile) {
        Image image = null;
        try (FileInputStream fileInputStream = new FileInputStream(imageFile)) {
            image = new Image(fileInputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return image;
    }
Martin Erlic
  • 5,467
  • 22
  • 81
  • 153
  • I updated my question to include the convertFileToImage() method. The class is quite big so I missed it. Will refactor as soon as I can get this working :/ – Martin Erlic Aug 07 '17 at 13:55
  • Your `ImageView` isn't actually displayed anywhere, is it? You just create it and pass it to the controller, but never actually put it in the scene graph anywhere. You put the image view in the `Group`, which is in the `ScrollPane`, and you create a `Scene` from the `BorderPane`, but none of these things are ever displayed anywhere. – James_D Aug 07 '17 at 13:55
  • Yeah, I guess you're right. Should I add "Group" to the FXML? I'm trying to convert it over from a purely programmatic UI: https://gist.github.com/santafebound/c91f9d41457132ade38645195fa43ecd – Martin Erlic Aug 07 '17 at 14:00
  • I don't understand your design at all. Why are you creating part of the UI in Java and part of it in FXML? Why not just create the entire UI in FXML? And there is [no need to pass the stage to the controller](https://stackoverflow.com/questions/25491732/how-do-i-open-the-javafx-filechooser-from-a-controller-class). – James_D Aug 07 '17 at 14:01
  • It was originally all in Java... there were a lot of classes so it was difficult to translate into pure FXML. Trying to get the BorderPane and ScrollPane into FXML basically. And until I do, it seems that the Image in the ImageView will be invisible... – Martin Erlic Aug 07 '17 at 14:03
  • No, the `ImageView` is not visible because you are not displaying it anywhere. Just read the code you have posted. If you want to move a portion of the UI to FXML, move one entire container and its content to FXML, and you shouldn't have to change anything else in the Java code (just load that container from FXML instead of creating it in code). Somewhere in the original you must have put the `ImageView` in the scene graph somewhere: you should not remove that code. – James_D Aug 07 '17 at 14:03
  • I wasn't using FXML at all before... – Martin Erlic Aug 07 '17 at 14:06
  • Yes, I know. I understand what you say you are trying to do, I just don't understand your attempt to do it. Previously you must have displayed the `ImageView` somewhere. Why remove that code? – James_D Aug 07 '17 at 14:07
  • What are you actually expecting to see in the UI here? Where are the different pieces supposed to be in relation to each other? (And why are you creating two `Scene`s: you can only display one scene in a single window.) – James_D Aug 07 '17 at 14:12
  • There would be a ToolBar, then MenuBar just underneath, then the selected image taking up the rest of the screen space. – Martin Erlic Aug 07 '17 at 14:13
  • So the toolbar and menu bar should go in the top of the border pane, and the image view in the center? Is that the idea? Have you considered describing what you are actually trying to do in the question? – James_D Aug 07 '17 at 14:14
  • Yes exactly, that's how it was before. – Martin Erlic Aug 07 '17 at 14:14

1 Answers1

1

You never display the ImageView, or indeed any of the controls you create in the Java code.

According to the comments, you want the content of the FXML in the top of the border pane, and the scroll pane containing the group with the image view in the center.

So you should do:

public class Main extends Application {

    private Image mainImage;
    private final Group selectionGroup = new Group();

    @Override
    public void start(Stage primaryStage) throws Exception {

        // Basic stage settings
        primaryStage.setTitle("Picture Viewer");
        primaryStage.setResizable(true);

        // Set task bar primary icon
        primaryStage.getIcons().add(new javafx.scene.image.Image("main/react-app-icon.png"));

        // Declare UI variables
        final BorderPane borderPane = new BorderPane();
        final ScrollPane scrollPane = new ScrollPane();
        final Scene scene = new Scene(borderPane, 800, 600);

        ImageView mainImageView = new ImageView();

        // Select main layout file
        FXMLLoader loader = new  FXMLLoader(getClass().getResource("/main/scene.fxml"));
        loader.setController(new MainController(primaryStage, selectionGroup, mainImage, mainImageView));
        Parent root = loader.load();

        // Add custom stylesheet URL
        scene.getStylesheets().add("main/style.css");

        // Set UI element properties
        selectionGroup.getChildren().add(mainImageView);
        scrollPane.setContent(selectionGroup);
        scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);

        borderPane.setCenter(scrollPane);
        borderPane.setTop(root);

        // Show primary stage
        primaryStage.setScene(scene);
        // primaryStage.setScene(new Scene(root));
        primaryStage.show();

    }

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

}
James_D
  • 201,275
  • 16
  • 291
  • 322