Issue
So I'm trying to bind my Numpad keys so i can use them in my calculator app but when I try to send my keyEvent as a string from my MainWindow.java to my MainWindowController public void method it gives me the error "Non-static method cannot be refered from a static context" even though none of my classes are static? Here is the MainWindow code:
package main`import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MainWindow extends Application implements EventHandler<KeyEvent> {
private static Stage historyStage;
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MainWindowInterface.fxml"));
Scene scene = new Scene(loader.load());
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setResizable(false);
stage.setTitle("Calculator");
stage.getIcons().add(new Image(getClass().getResourceAsStream("../images/icon.png")));
((MainWindowController)loader.getController()).init(stage);
stage.show();
//scene.setOnKeyPressed(MainWindowController.handle());
scene.setOnKeyPressed(this);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE){
stage.close();
}
});
createHistoryStage();
}
public void createHistoryStage() throws Exception{
historyStage = new Stage();
historyStage.initStyle(StageStyle.TRANSPARENT);
historyStage.setTitle("Calculator History");
historyStage.setAlwaysOnTop(true);
historyStage.setResizable(false);
historyStage.getIcons().add(new Image(getClass().getResourceAsStream("../images/icon.png")));
historyStage.initModality(Modality.APPLICATION_MODAL);
}
public static Stage getHistoryStage(){
return historyStage;
}
public void run() {
launch();
}
@Override
public void handle(KeyEvent event) {
String valueOfKey = String.valueOf(event.getCode());
MainWindowController.keyPress(valueOfKey);
}
}
and here is the MainWindowController file (look at keyPress method)
package main`import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;`;
public class MainWindowController {
@FXML private Pane titlePane;
@FXML private ImageView btnMinimize, btnClose;
@FXML private Label lblResult;
private double x, y;
private double num1 = 0;
private String operator = "+";
private ArrayList<String> calculation_history = new ArrayList<>();
public void init(Stage stage) {
titlePane.setOnMousePressed(mouseEvent -> {
x = mouseEvent.getSceneX();
y = mouseEvent.getSceneY();
});
titlePane.setOnMouseDragged(mouseEvent -> {
stage.setX(mouseEvent.getScreenX()-x);
stage.setY(mouseEvent.getScreenY()-y);
});
btnClose.setOnMouseClicked(mouseEvent -> stage.close());
btnMinimize.setOnMouseClicked(mouseEvent -> stage.setIconified(true));
}
public void openHistoryWindow(){
try{
FXMLLoader loader2 = new FXMLLoader(getClass().getResource("history.fxml"));
Parent root = loader2.load();
Scene historyScene = new Scene(root);
historyScene.setFill(Color.TRANSPARENT);
MainWindow.getHistoryStage().setScene(historyScene);
HistoryController historyController = loader2.getController();
historyController.initializeCalculations(calculation_history);
((HistoryController)loader2.getController()).init(MainWindow.getHistoryStage());
MainWindow.getHistoryStage().show();
historyScene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE){
MainWindow.getHistoryStage().close();
}
});
}catch (IOException ex){
System.out.println(ex);
}
}
public void addCalculation(String num1, String operator, String num2, String result){
String expression = num1 + " " + operator + " " + num2;
this.calculation_history.add(expression + " = " + result);
}
@FXML
void onNumberClicked(MouseEvent event) {
int value = Integer.parseInt(((Pane)event.getSource()).getId().replace("btn",""));
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
}
public void keyPress(String keyPressed){
int value = 0;
switch (keyPressed){
case "NUMPAD1": value = 1;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD2": value = 2;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD3": value = 3;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD4": value = 4;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD5": value = 5;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD6": value = 6;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD7": value = 7;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD8": value = 8;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD9": value = 9;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
case "NUMPAD0": value = 0;
lblResult.setText(Double.parseDouble(lblResult.getText())==0?String.valueOf((double)value):String.valueOf(Double.parseDouble(lblResult.getText())*10+value));
break;
default: break;
}
}
@FXML
void onSymbolClicked(MouseEvent event) {
String symbol = ((Pane)event.getSource()).getId().replace("btn","");
if(symbol.equals("Equals")) {
double num2 = Double.parseDouble(lblResult.getText());
switch (operator) {
case "+" -> lblResult.setText((num1+num2) + "");
case "-" -> lblResult.setText((num1-num2) + "");
case "*" -> lblResult.setText((num1*num2) + "");
case "/" -> lblResult.setText((num1/num2) + "");
}
addCalculation(String.valueOf(num1), operator, String.valueOf(num2), lblResult.getText());
operator = ".";
}
else if(symbol.equals("Clear")) {
lblResult.setText(String.valueOf(0.0));
operator = ".";
}
else if(symbol.equals("HIST")){
openHistoryWindow();
}
else {
switch (symbol) {
case "Plus" -> operator = "+";
case "Minus" -> operator = "-";
case "Multiply" -> operator = "*";
case "Divide" -> operator = "/";
}
num1 = Double.parseDouble(lblResult.getText());
lblResult.setText(String.valueOf(0.0));
}
}
}
Solution
This addresses the immediate question; there are other issues with your code which are too numerous to deal with.
The error message tells you the problem: keyPress(...)
is an instance ("non-static") method, and you are trying to access it as though it is a static method (i.e. via the class name).
Note that keyPress(...)
accesses instance variables, so you cannot make it static. Your only option is to access the method through the controller instance. Since you already retrieve that instance via loader.getController()
, this is straightforward; just save it to an instance variable:
public class MainWindow extends Application implements EventHandler<KeyEvent> {
private static Stage historyStage;
private MainWindowController controller ;
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MainWindowInterface.fxml"));
Scene scene = new Scene(loader.load());
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setResizable(false);
stage.setTitle("Calculator");
stage.getIcons().add(new Image(getClass().getResourceAsStream("../images/icon.png")));
controller = loader.getController();
controller.init(stage);
stage.show();
//scene.setOnKeyPressed(MainWindowController.handle());
scene.setOnKeyPressed(this);
// this will replace the handler from the previous line,
// so is probably not what you want to do
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE){
stage.close();
}
});
createHistoryStage();
}
// ...
@Override
public void handle(KeyEvent event) {
String valueOfKey = String.valueOf(event.getCode());
controller.keyPress(valueOfKey);
}
}
Answered By - James_D
Answer Checked By - Katrina (JavaFixing Volunteer)