JavaFX setIconified(false) does not display window - javafx

I have a situation in which I have a JavaFX application that allows the primary stage to be iconified by a set of REST endpoints. Here's the application code:
public class server extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/Hello.fxml"));
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("FXML Welcome");
primaryStage.setScene(scene);
Server jettyServer = new Server(65534);
ServletContextHandler context = new ServletContextHandler(jettyServer, "/");
jettyServer.setHandler(context);
final ResourceConfig rc = new ResourceConfig().register(
new AbstractBinder() {
#Override
protected void configure() {
bind(primaryStage).to(Stage.class);
}
}
);
rc.packages("com.cognivue.jettypoc.rest");
ServletHolder jerseyServlet = new ServletHolder(new ServletContainer(rc));
jerseyServlet.setInitOrder(0);
context.addServlet(jerseyServlet, "/*");
jettyServer.start();
primaryStage.show();
}
}
Here's the REST controller code:
#Path("stagecontrol")
public class StageControl {
#Inject
private Stage primaryStage;
public StageControl() {
}
#GET
#Path("start")
public Response start() {
Platform.runLater(() ->primaryStage.setIconified(false));
return Response.ok().build();
}
#GET
#Path("stop")
public Response stop() {
Platform.runLater(() ->primaryStage.setIconified(true));
return Response.ok().build();
}
}
When I make a call to /stagecontrol/stop, the application iconifies; when I make a call to /stagecontrol/start, nothing happens. Is there something about setIconified?

Related

JavaFX - How to switch to another tab with mouse click event [duplicate]

So I'm trying to load and save Images into an imageView where the location of the image is chosen through a file browser. I've been working on this for several days now and I'm gonna have a stroke if I can't get it fixed. I've tried everything I can think of. Thank you in advance for helping.
UPDATED:
Here is my main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
public Main(){}
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout(){
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
RootLayout controller = loader.getController();
controller.setMain(this);
primaryStage.show();
}catch(Exception e ){e.printStackTrace();}
}
public void showScreen(){
try{FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane)loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
}catch (Exception e){e.printStackTrace();}
}
public Stage getPrimaryStage(){return primaryStage;}
public static void main(String[] args) {
launch(args);
}
}
Here is the rootLayout:
public class RootLayout {
private Main main;
private Controller controller = new Controller();
public void setMain(Main main){this.main = main;}
#FXML
private void handleOpen(){
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(
"PNG files (*.png)","*png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if(file!= null){
controller.updateImage(file.toURI().toString());
}
}
}
And here is the controller:
public class Controller implements Initializable {
#FXML
ImageView imageView = new ImageView();
String imageURL;
Main main = new Main();
public void setMain(Main main){
this.main = main;
}
public void updateImage(String url){
if(url.length()>=1){
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
}
else{
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
Two things:
Never assign a field whose value is to be injected by an FXMLLoader (e.g. #FXML fields). Doing so is a waste of resources at best and introduces subtle bugs at worst. For instance, if you were to leave the imageView field uninitialized you'd be getting a NullPointerException which would indicate a problem with your setup. Since you do initialize the field, however, you don't get any errors and there's a false impression of the code working.
In your RootLayout controller class, you have:
private Controller controller = new Controller();
That instance of Controller you just created is not linked to any FXML file. And since you initialize the imageView field (see first point) you end up updating an ImageView which is not being displayed anywhere; this is where not initializing said field would have given a nice indication of there being a problem. The solution is to pass the Controller instance created by the FXMLLoader to the RootLayout instance created by the other FXMLLoader.
Also, in the same class you have:
Main main = new Main();
Which is also unnecessary since the created instance of Main is both not the correct instance and is replaced by the call to #setMain(Main) almost immediately.
Assuming your FXML files (which you did not provide) are correct, the Java classes should look more like:
Main.java
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private RootLayout rootLayoutController;
public Main() {}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// store RootLayout instance in field so #showScreen()
// can reference it
rootLayoutController = loader.getController();
rootLayoutController.setMain(this);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void showScreen() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane) loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
// set Controller instance on RootLayout instance
rootLayoutController.setController(controller);
} catch (Exception e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
RootLayout.java
public class RootLayout {
private Main main;
private Controller controller;
public void setMain(Main main) {
this.main = main;
}
public void setController(Controller controller) {
this.controller = controller;
}
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Note extensions should be prefixed with "*."
FileChooser.ExtensionFilter extensionFilter =
new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if (file != null) {
controller.updateImage(file.toURI().toString());
}
}
}
Controller.java
public class Controller implements Initializable {
#FXML ImageView imageView; // leave uninitialized, will be injected
String imageURL;
Main main;
public void setMain(Main main) {
this.main = main;
}
public void updateImage(String url) {
if (url.length() >= 1) {
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
} else {
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
Note: Did not test new code.

ProgressIndicator finishes when I start another Stage

I want to create an application starter. The ProgressIndicator should running during start of the new Stage in new Thread. But if I click at the start button the ProgressIndicator stops running.
It works if I make some I/O and ProgressIndicator shows progress. It is apparently not possible to update two stages in JavaFX in parallel or does anyone have a solution for me?
public class Main extends Application {
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(new UserVC().getView(), 600, 200));
primaryStage.show();
userVC.autologin();
}
public static void main(String[] args) { launch(args); }
}
public class UserView extends BorderPane {
private ProgressIndicator progressIndicator = new ProgressIndicator();
public UserView() {
super.setCenter(createContent());
}
public Node createContent() {
HBox userbox = new HBox();
userbox.getChildren().add(progressIndicator);
progressIndicator.show();
return userbox;
}
}
public class UserVC {
private UserView view = new UserView();
public UserVC() {
}
public void autologin() {
Task<Void> task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
try {
Thread.sleep(1000);
Platform.runLater(new Runnable() {
#Override public void run() {
Stage stage = new Stage();
stage.setScene(new Scene(new MainControlVC().getView(), 900, 300));
stage.show();
}
});
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
};
view.getProgressIndicator().progressProperty().unbind();
view.getProgressIndicator().progressProperty().bind(task.progressProperty());
new Thread(task).start();
}
}
I changed the Task to Thread and it works fine now:
Thread thread = new Thread() {
public void run() {
mainController = new MainControlVC(dataDirectory, user);
Platform.runLater(new Runnable() {
#Override public void run() {
Stage stage = new Stage();
Scene scene = new Scene(mainController.getView(), mainController.getWidth(), mainController.getHeight());
stage.setScene(scene);
stage.show();
}
});
}
};
thread.start();

JavaFX log4j in a textarea with a different thread

I'm trying to redirect log4j output to a textarea but this one get filled at the end of the action since I'm using Platform.runLater
Is there any way I can do it with a different thread ?
AppFX.java
public class AppFX extends Application {
public static void main(String[] args) {
ConfHandler.initConf();
launch(args);
}
#Override
public void start(Stage primaryStage)throws IOException {
primaryStage.getIcons().add(new Image("file:src/main/resources/images/frameIcon.png"));
FXMLLoader loader = new FXMLLoader();
String currentPath = System.getProperty("user.dir");
loader.setLocation(new URL("file:\\"+currentPath+"\\src\\main\\resources\\scenes\\TestsFrame.fxml"));
Parent content = loader.load();
primaryStage.setTitle("IGED Tests");
Scene scene = new Scene(content);
primaryStage.setScene(scene);
primaryStage.show();
TextAreaAppender.setTextArea(((EaaSCleanerController)loader.getController()).getLogTextArea());
}}
TextAreaAppender.java
public class TextAreaAppender extends WriterAppender {
private static volatile TextArea textArea = null;
public static void setTextArea(final TextArea textArea) {
TextAreaAppender.textArea = textArea;
}
#Override
public void append(final LoggingEvent loggingEvent) {
final String message = this.layout.format(loggingEvent);
try {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
if (textArea != null) {
if (textArea.getText().length() == 0) {
textArea.setText(message);
} else {
textArea.selectEnd();
textArea.insertText(textArea.getText().length(), message);
}
}
} catch (final Throwable t) {
System.out.println("Unable to append log to textarea:" + t.getMessage());
}
}
});
} catch (final IllegalStateException e) {
}
}}
log4j.properties
# Append the logs to the GUI
log4j.appender.gui=com.bp.nest.testauto.gui.TextAreaAppender
log4j.appender.gui.layout=org.apache.log4j.PatternLayout
log4j.appender.gui.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n
You should put your actions on a separate thread. Then the logging can happen in parallel to your actions. You should never block the GUI-thread.

Binding JavaFX Label to StringProperty

In my JavaFX application I have a label that I want to update with the StringProperty of another class that defines server functionality.
In the server class I have a StringProperty defined as shown below:
public class eol_server {
private StringProperty serverMessagesProperty;
etc..
I have a method to return the StringProperty on request:
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
In the main gui class, Start() method I build the Scene, then instantiate a new server object. After this I update one of the labels in my Scene graph by binding it to the StringProperty of the server object:
public void start(Stage primaryStage) {
...set up all the gui components
primaryStage.show();
dss = new eol_server();
lbl_dssMessage.textProperty().bind(dss.getServerMessagesProperty());
}
When I run the application, the scene is rendered as I expect, and the lbl_dssMessage text is set to value that is set up in the constructor of eol_server. But, from that point on the binding is not working, although I have actions that would update the StringProperty of the dss object they are not updating the label in the GUI.
Here is the complete file that generates a stripped down version of the scene:
public class eol_gui extends Application {
private static eol_server dss = null;
private static Stage primaryStage;
/**
* Application Main Function
* #param args
*/
public static void main(String[] args) {
launch(args);
}
/**
* #param stage
*/
private void setPrimaryStage(Stage stage) {
eol_gui.primaryStage = stage;
}
/**
* #return Stage for GUI
*/
static public Stage getPrimaryStage() {
return eol_gui.primaryStage;
}
/* (non-Javadoc)
* #see javafx.application.Application#start(javafx.stage.Stage)
*/
#Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
BorderPane root = new BorderPane();
Label lbl_dssMessage = new Label("initialized");
HBox topMenu = new HBox();
topMenu.getChildren().add(lbl_dssMessage);
eol_supervisor_I config1 = new eol_supervisor_I();
// Build Scene
root.setStyle("-fx-background-color: #FFFFFF;");
root.setTop(topMenu);
primaryStage.setScene(new Scene(root, 300, 50));
primaryStage.show();
dss = new eol_server();
dss.getServerMessagesProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> o,Object oldVal,Object newVal)
{
//Option 1: lbl_dssMessage.setText(newVal.toString());
}
});
//Option 2
lbl_dssMessage.textProperty().bind(dss.serverMessagesProperty);
}
}
As you can see I have tried the bind method and a change listener. It looks like bind was working, as was the listener, in all cases except those that run in the server service threads. These throw the IllegalStateException due to not being on the main JavaFX application thread. How do I safely and correctly exchange messages from the service to the main thread?
The server is defined in the following class, which is intended to run services independent of the main JavaFX thread. But I would like to exchange info between the threads to show status. Ii'm trying to avoid the GUI hanging while the server connections and data exchanges are made.
public class eol_server {
public StringProperty serverMessagesProperty;
public eol_server() {
/* Create Scripting Environment */
serverMessagesProperty().set("Establishing Debug Server Environment");
connect();
}
public boolean connect() {
serverMessagesProperty().set("Creating Server");
ConnectService connectService = new ConnectService();
connectService.start();
return false;
}
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
public StringProperty getServerMessagesProperty() {
return serverMessagesProperty;
}
private class ConnectService extends Service<Void> {
#Override
protected void succeeded() {
}
#Override
protected void failed() {
}
#Override
protected void cancelled() {
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
serverMessagesProperty().set("Connecting....");
Thread.sleep(5000);
// DEMO: uncomment to provoke "Not on FX application thread"-Exception:
// connectButton.setVisible(false);
serverMessagesProperty().set("Waiting for server feedback");
Thread.sleep(5000);
return null;
}
};
}
}
private class DisconnectService extends Service<Void> {
#Override
protected void succeeded() {
}
#Override
protected void cancelled() {
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("Disconnecting....");
serverMessagesProperty().set("Disconnecting....");
Thread.sleep(5000);
updateMessage("Waiting for server feedback.");
serverMessagesProperty().set("Waiting for server feedback.");
Thread.sleep(5000);
return null;
}
};
}
}
}
Thanks in advance
JW
Read the below paragraph from JavaFX Node documentation.
Node objects may be constructed and modified on any thread as long
they are not yet attached to a Scene in a Window that is showing. An
application must attach nodes to such a Scene or modify them on the
JavaFX Application Thread.
The correct and more functional way to solve your problem is
dss.serverMessagesProperty.addListener((observable, oldValue, newValue) -> Platform.runLater(() -> lbl_dssMessage.setText(newValue));
I seem to have a solution, derived from what I read on the runlater() Platform method. Also see discussion here
Multi-Threading error when binding a StringProperty
changing my listener to invoke the message update via runLater seems to break the multi-threading problem. Yes I expect it is not immediate but it will be very very close to immediate and good enough. I appreciate that JavaFX requires you to not mess with the Application thread values / nodes etc etc of the scene graph but it is quite a complicated area of the JavaFX library.
Here is the listener that worked for me
// Get new DSS session active
dss = new eol_server();
dss.serverMessagesProperty.addListener(new ChangeListener<Object>() {
#Override
public void changed (ObservableValue<?> observable, Object oldValue, Object newValue) {
Platform.runLater(() -> lbl_dssMessage.setText(newValue.toString()));
}
});
}
Happy for further discussion as to why this works when the other options did not, also open to any other more elegant suggestions.

How can I open a new Scene from a different class

I have a simple button that i want to click and open a new scene but i made the new scene in another class but it doesn't work and it does not show my errors either.
Here is my Controller class
public class Controller
{
public TextField txtUsername;
public TextField txtEmail;
public TextField txtPass;
public TextField txtPhone;
public Button btnLogin;
}
public void buttonClicked()
{
Chattingform chattingform = new Chattingform ();
}
Here is my Chattingform class
public class Chattingform extends Application
{
#Override
public void start (Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Chattingform.fxml"));
try {
Parent root = fxmlLoader.load();
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setOpacity(1);
stage.setTitle("Exchat");
stage.setScene(new Scene (root, 450, 450));
stage.showAndWait();
}catch ( IOException io )
{
JOptionPane.showMessageDialog (null, "Could not open ");
}
}
From my experince, all JavaFX Controllers have:
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}

Resources