0

I think my question must be confusing. Let me briefly explain, I am using JavaFX, creating a photo editor. I have two controllers and two views. HomeController (from where image view layout is set), and BrightnessController, which opens a new dialogue window, from where I set values of brightness, but when apply changes, it throws error, saying this: enter image description here

This is my HomeController:

package com.example.photoeditor;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;

import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;

import javafx.stage.Stage;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

@SuppressWarnings("All")
public class HomeController implements Initializable {

    @FXML
    Button cropButton;

    @FXML
    Button brightnessButton;

    @FXML
    Button contrastButton;

    @FXML
    Button sharpnessButton;

    @FXML
    HBox imageViewLayout;

    @FXML
    ImageView imageView;

    @FXML
    MenuBar menuBar;

    Mat imageMat;
    String inputImageFileLocation;

    Utilities utilities = new Utilities();

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        // Create two menus
        Menu fileMenu = new Menu("File");

        // Create some menu items
        MenuItem newMenuItem = new MenuItem("New");
        MenuItem openMenuItem = new MenuItem("Open");
        MenuItem saveMenuItem = new MenuItem("Save");
        MenuItem exitMenuItem = new MenuItem("Exit");

        // Add menu items to the File menu
        fileMenu.getItems().addAll(newMenuItem, openMenuItem, saveMenuItem, exitMenuItem);

        // Add menus to the menubar
        menuBar.getMenus().addAll(fileMenu);

        openMenuItem.setOnAction(new EventHandler<>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                openImage();
            }
        });

        brightnessButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                openBrightnessWindow();
            }
        });
    }

    // LOADING IMAGE INTO IMAGEVIEW
    public void openImage() {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

        // Open dialogue to get image location path
        FileChooser fileChooser = new FileChooser();
        File inputImageFile = fileChooser.showOpenDialog(null);
        inputImageFileLocation = inputImageFile.toString().replaceAll(
                "\\\\", "\\\\\\\\");


        imageMat = Imgcodecs.imread(inputImageFileLocation);
        Image image = utilities.mat2Image(imageMat);

        setImage(image);

    }

    public void openBrightnessWindow() {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("BrightnessView.fxml"));
            Scene scene = new Scene(root);
            Stage primaryStage = new Stage();
            primaryStage.setTitle("Brightness Controller");
            primaryStage.setScene(scene);
            primaryStage.setResizable(false);
            primaryStage.initOwner(brightnessButton.getScene().getWindow());
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setImage(Image image) {
        imageView.setImage(image);
        imageView.fitWidthProperty().bind(imageViewLayout.widthProperty());
        imageView.fitHeightProperty().bind(imageViewLayout.heightProperty());
    }
}

And this is my BrightnessController:

package com.example.photoeditor;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

import java.net.URL;
import java.util.ResourceBundle;

@SuppressWarnings("All")
public class BrightnessController extends HomeController implements Initializable {

    @FXML
    Button applyChanges;

    @FXML
    TextField textField;

    Utilities utilities = new Utilities();

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        applyChanges.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                enhanceBrightness();
            }
        });
    }

    public void enhanceBrightness() {
        Mat source = Imgcodecs.imread("D://img.jpg");
        Mat destination = new Mat(source.rows(), source.cols(), source.type());
        source.convertTo(destination, -1, 10, 50);

        Image newImage = utilities.mat2Image(destination);
        setImage(newImage);
    }
}

I know that some of you will say that I am sending null method (no image) from brightness controller to home controller, well that's not true. When i run the same code in HomeController where Image View layout exists, it works without any problem.

Now this clearly means that JavaFX is having problem with setting images in ImageView from different controllers. This used to be super easy when JavaFX supported to connect multiple views with one controller from fxml files.

Please help me to fix this problem.

Okay so I am inserting this new less complicated version of my program to make it more understandable. I have two controllers, "Controller1.java" and "Controller2.java", they both are connected to scenes, "SceneView1" and "SceneView2". "Controller1.java" has method setImage(Image image) to set an image in image view of "SceneView1". Now I am sending image from "Controller2.java", in other words calling setImage() method, and it is throwing an error.

Controller1:

package com.example.demo;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller1 implements Initializable {

 @FXML
 Button nextScene;

@FXML
HBox imageViewLayout;

@FXML
ImageView imageView;

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    nextScene.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            try {
                Parent root = FXMLLoader.load(getClass().getResource("SceneView2.fxml"));
                Scene scene = new Scene(root);
                Stage primaryStage = new Stage();
                primaryStage.setTitle("Video Library");
                primaryStage.setScene(scene);
                primaryStage.setResizable(false);
                primaryStage.initOwner(nextScene.getScene().getWindow());
                primaryStage.show();
            } catch (Exception e) {
                // PASS
            }
        }
    });
}

public void setImage(Image image) {
    imageView.setImage(image);
    imageView.fitWidthProperty().bind(imageViewLayout.widthProperty());
    imageView.fitHeightProperty().bind(imageViewLayout.heightProperty());
}
}

Controller2:

package com.example.demo;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;

import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.scene.image.Image;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;

public class Controller2 implements Initializable {

 @FXML
 Button addImage;

 Controller1 controller1 = new Controller1();

 @Override
 public void initialize(URL url, ResourceBundle resourceBundle) {
     addImage.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
            Mat imageMat = Imgcodecs.imread("D://img.jpg");
            Image image = mat2Image(imageMat);

            controller1.setImage(image);
        }
    });
}

// CONVERTING OPENCV IMAGE TO JAVAFX IMAGE
public Image mat2Image(Mat mat) {
    MatOfByte buffer = new MatOfByte();
    Imgcodecs.imencode(".png", mat, buffer);
    return new Image(new ByteArrayInputStream(buffer.toArray()));
}
}

Here's a print stack trace:

"C:\Program Files\Java\jdk-19\bin\java.exe" -Djava.library.path=D:\Softwares\OpenCV-4.6.0\opencv\build\java\x64 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=60044:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\.m2\repository\org\openjfx\javafx-controls\19.0.2.1\javafx-controls-19.0.2.1.jar;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-graphics\19.0.2.1\javafx-graphics-19.0.2.1.jar;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-base\19.0.2.1\javafx-base-19.0.2.1.jar;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-fxml\19.0.2.1\javafx-fxml-19.0.2.1.jar -p "C:\Users\Administrator\.m2\repository\org\openjfx\javafx-controls\19.0.2.1\javafx-controls-19.0.2.1-win.jar;D:\Softwares\OpenCV-4.6.0\opencv\build\java\opencv-460.jar;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-fxml\19.0.2.1\javafx-fxml-19.0.2.1-win.jar;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-graphics\19.0.2.1\javafx-graphics-19.0.2.1-win.jar;D:\Software Engineering\App Development\Desktop Development\Java\Demo\target\classes;C:\Users\Administrator\.m2\repository\org\openjfx\javafx-base\19.0.2.1\javafx-base-19.0.2.1-win.jar" -m com.example.demo/com.example.demo.Main
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.image.ImageView.setImage(javafx.scene.image.Image)" because "this.imageView" is null
    at com.example.demo/com.example.demo.Controller1.setImage(Controller1.java:53)
    at com.example.demo/com.example.demo.Controller2$1.handle(Controller2.java:35)
    at com.example.demo/com.example.demo.Controller2$1.handle(Controller2.java:28)
    at javafx.base@19.0.2.1/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@19.0.2.1/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base@19.0.2.1/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@19.0.2.1/javafx.scene.Node.fireEvent(Node.java:8923)
    at javafx.controls@19.0.2.1/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls@19.0.2.1/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
    at javafx.controls@19.0.2.1/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base@19.0.2.1/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base@19.0.2.1/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@19.0.2.1/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@19.0.2.1/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@19.0.2.1/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@19.0.2.1/javafx.scene.Scene$MouseHandler.process(Scene.java:3894)
    at javafx.graphics@19.0.2.1/javafx.scene.Scene.processMouseEvent(Scene.java:1887)
    at javafx.graphics@19.0.2.1/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2620)
    at javafx.graphics@19.0.2.1/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@19.0.2.1/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@19.0.2.1/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@19.0.2.1/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@19.0.2.1/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@19.0.2.1/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@19.0.2.1/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@19.0.2.1/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@19.0.2.1/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
    at java.base/java.lang.Thread.run(Thread.java:1589)
jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • 1
    This is my first time seeing a `Controller` be extended. Why did you do it? – SedJ601 Mar 19 '23 at 17:19
  • So I can directly use setImage(). But that's not what causing the problem because I have tried it using creating objects as well. Unless there is a third way to use setImage () method from Brightness controller class. – Sohaib Malik Mar 19 '23 at 17:39
  • 1
    _this clearly means that JavaFX is having problem with setting images_ most certainly not ;) the error most probably is somewhere in _your_ code - so provide a [mcve] demonstrating what's wrong (please read study that help page to understand what's needed to help you find the error - it has to be the most __mininal__ example that is runnable __as-is__) – kleopatra Mar 19 '23 at 17:41
  • 1
    Create and post a [mre]. I don't think it makes sense to use inheritance here. Does the FXML that uses `BrightnessController` as its controller class have an element with `fx:id="imageView"`? If not, `imageView` will be null in the controller that is created when you load that FXML. – James_D Mar 19 '23 at 17:46
  • @James_D No my Brightness controller class does not have Image View. Only my Home controller class have that element. That's the reason I want to send image from Brightness controller class to Home controller class, so I can display it. Bit it throws error. Question is why, because I am clearly sending an Image in setImage() method extended from that HomeController – Sohaib Malik Mar 19 '23 at 17:57
  • The only `@FXML`-annotated fields that will be initialized in a controller are the ones with matching `fx:id`s in the FXML that is being loaded when the controller is created. It doesn't make any difference if the field is inherited from a superclass (I really don't understand what you think inheritance is going to give you here). Consequently `imageView` is null, as is clearly stated in the error message. You need to give (either directly or indirectly) the `BrightnessController` access to the `imageView` that is created when your `HomeController` is created. – James_D Mar 19 '23 at 18:05
  • @James_D Inheritance was the last option I tried. I have also used objects as well. Unless there is a third way to send and set image from one controller to another. – Sohaib Malik Mar 19 '23 at 18:07
  • 1
    To clarify: *"No my Brightness controller class does not have Image View."*. Well, yes it does, but that's not what I asked. You make `BrightnessController` a subclass of `HomeController`. This means that every instance of `BrightnessController` is also an instance of `HomeController`. Since every instance of `HomeController` has a field called `imageView` of type `ImageView`, your `BrightnessController` certainly has a field called `imageView`. The question is whether or not it is initialized, which it isn't, because there is no matching `fx:id`. – James_D Mar 19 '23 at 18:10
  • 1
    The quick and dirty way is just to [pass the instance](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml) of `imageView` that is present in the `HomeController` to the `BrightnessController` when you load `BrightnessView.fxml`. – James_D Mar 19 '23 at 18:11
  • @James_D Wait so while loading a new FXML window, I can instance to BrightnessController class. If you are saying to use constructors to do that, then I tried it didn't worked. Unless you are referring to some other way to pass ImageView instance to BrightnessController. It will be helpful if you can share a code, or explain how to do it. – Sohaib Malik Mar 19 '23 at 18:17
  • *"while loading a new FXML window, I can instance to BrightnessController class"* I don't know what this means (I don't know what "I can instance to" means). When you load an FXML file, the `FXMLLoader` creates an instance of the controller class that is specified in the FXML. That's all I am referring to. Your last comment makes absolutely no sense to me. – James_D Mar 19 '23 at 18:18
  • *"Unless you are referring to some other way to pass ImageView instance to BrightnessController."* See the link in the comment above. – James_D Mar 19 '23 at 18:19
  • It sounds to me like the OP is expecting the BrightnessController to extend an instantiated instance of HomeController, thinking that the ImageView field will be instantiated. This isn't how inheritance works. – DaveB Mar 19 '23 at 20:23
  • @DaveB That wouldn't really make sense, would it? How would the `BrightnessController` instance "know" which `HomeController` instance to inherit from? I can't really see a mental picture of inheritance that works at the object, rather than class, level. (I'm not saying your interpretation is wrong, I just can't see how the misunderstanding would make sense.) – James_D Mar 19 '23 at 21:04
  • @James_D and yet that seems to be the case. Look at the OP's second comment. He seems to be indicating that accessing imageView from BrightnessController should do something meaningful, yet he asserts that he has no imageView in BrightnessController and it must be referencing the the imageView in HomeController. Maybe the "magic" around the FXMLLoader process is leading him to think that they are somehow connected? – DaveB Mar 19 '23 at 21:16
  • Please place the full stack trace in the question as text formatted as code rather than as an image. – jewelsea Mar 20 '23 at 07:13
  • @James_D I have edited a post, and inserted a less complicated code. Check it out please. – Sohaib Malik Mar 20 '23 at 08:49
  • 1
    @jewelsea Inserted a full stack trace. Edited the post. Also have inserted a less complicated code. – Sohaib Malik Mar 20 '23 at 08:49
  • If you catch an exception, always either log it or rethrow it, never just ignore it. – jewelsea Mar 20 '23 at 09:01
  • 1
    Controllers are, by default, and I guess should be in your case, created by the FXMLLoader, you should not be calling `new Controller1`. The new controller you create is unrelated to that created by the FXMLoader and will not have fields initialized. Controllers should not reference controllers unless they are nested (yours are not and probably shouldn’t be). Probably use [MVC](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx) with the image stored in the model and the image property of the image view bound to the image in the model. – jewelsea Mar 20 '23 at 09:10
  • 1
    as assumed: the error - already spotted by @jewelsea, controllers must be __loaded__ to inject fields - is in _your_ code :) a [mcve] is mighty tool which can spares tons of comments, time, assumptions .. – kleopatra Mar 20 '23 at 09:37
  • 1
    @jewelsea You were right. An only way to pull this task off is by using any architectural patterns. In my case MVC worked. As controllers only purpose is to pass data from view. In the end you will always a third party (class) to process the data, instead of trying to process it inside the controller class. Thanks for help. – Sohaib Malik Mar 20 '23 at 14:15

0 Answers0