Updating JFrame control with progress of call to another class - java

Using NetBeans 8, I have created a JavaFX in Swing Application. The application uses JFrame and JApplet.
The JFrame has a Label and a button that calls a processing class. The processing class calls 5 other subclasses.
I would like to have the processing class send status updates to the Label on the JFrame as the class is processing.
I have found that Sockets and ServerSockets can be used to send data from the processing class to the JFrame class but the Label only gets updated when the processing class completes.
There is too much default code to display.
Here is a summary:
The JFrame class has the typical main, init, createScene methods.
The createScene method creates a GridPane and adds the Label and Button to it, along with an EventHandler for the button.
The EventHandler calls the processing class.
The processing class simply calls 5 other methods.
I would like to do something like this:
public class processingClass {
public static void process(){
updateJFrameLabel("About to do something");
doSomething();
updateJFrameLabel("status: something Done");
doSomethingElse();
updateJFrameLabel("status: something Else Done");
doMore();
updateJFrameLabel("more done");
}
}
How can I update the JFrame label real-time as the process is running?
I understand how to use Sockets but I am not sure how to get the Label to update asynchronously.
Any help or a link to information would be helpful.

Related

JavaFX Event handling from a different class

I looked at the stackoverflow questiions that seem similar to my problem, but none were of any help.
Here is my problem:
For a project, I am making a JavaFX app that is in pure Java without FXML. I have two classes, a controller Controller class and a class containing gui stuff GUI.
Controller has a member variable of type GUI and I am trying to assign an event handler to one of the buttons in GUI but it doesn't seem to work. It only works when I try implementing the handler inside the GUI class, but I need it to work in Comtroller.
In the constructor of Controller is as follows:
this.view = view;
view.addSimpleHandler(new SimpleHandler());
view is of type GUI and addSimpleHandler is a member function of view
SimpleHandler is an inner class of Controller that implements EventHandler and overrides the handle() function
public void addSimpleHandler(EventHandler<ActionEvent> e) {
simpleButton.setOnAction(e);
}
here is my main method and class signature for GUI
public class GUI extends Application {
//member variables for the GUI design including simpleButton
private Button simpleButton;
public static void main(String[] args) {
GUI view = new GUI();
Controller controller = new Controller(view);
Application.launch(view.getClass(), args);
}
public GUI() {
simpleButton = new Button("Simple button");
//rest of code is setting up GUI into my panes
}
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(mainPane, sceneWidth, sceneHeight);
//mainPane is a pane that contains simpleButton with a screenwidth and screenHeight
primaryStage.setTitle("Simple");
primaryStage.setScene(scene);
primaryStage.show();
}
Don't instantiate your application class manually. To understand why, see the documentation regarding the JavaFX life-cycle (emphasis mine):
The entry point for JavaFX applications is the Application class. The JavaFX runtime does the following, in order, whenever an application is launched:
Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
Constructs an instance of the specified Application class
Calls the init() method
Calls the start(javafx.stage.Stage) method
Waits for the application to finish, which happens when either of the following occur:
the application calls Platform.exit()
the last window has been closed and the implicitExit attribute on Platform is true
Calls the stop() method
As you can see, JavaFX itself will instantiate the application class and it's that instance which has its life-cycle methods invoked—those methods being init(), start(Stage), and stop(). However, in your code you have the following:
public static void main(String[] args) {
GUI view = new GUI(); // created your own instance
Controller controller = new Controller(view); // gave controller that instance
// Launches JavaFX which starts the life-cycle documented above
Application.launch(view.getClass(), args);
}
You create your own instance of GUI. This instance is not managed by JavaFX which means its start(Stage) method is never invoked. When you create the Controller instance and pass it your instance of GUI you're adding the EventHandler to a node which is never displayed. The window you see displayed is from the GUI instance created as part of the call to Application#launch and that GUI instance is never associated with a Controller.
For JavaFX applications you should consider the init() and start(Stage) methods as the entry points1. In other words, create the Controller in one of those aforementioned life-cycle methods. Although I suppose you could do the same in the constructor instead2. Whichever you choose, take note of which thread invoke each method, which is documented in the same place as the life-cycle. Here are the essentials:
The application class is loaded, initialized, and constructed on the JavaFX Application Thread.
The init() method is invoked by the JavaFX-Launcher thread.
The start(Stage) and stop() methods are invoked by the JavaFX Application Thread.
Remember that certain actions can only be executed on the JavaFX Application Thread.
1. You can still execute code before the call to Application#launch within the main method if needed. The only constraint is that code should not be directly related to JavaFX.
2. Your constructor is currently public and has zero parameters—keep it that way. JavaFX requires the application class to have a public, no-argument constructor in order to construct an instance via reflection.

Can you build a Swing GUI for an MVC-style game if the Controller doesn't have a reference to the Model?

I couldn't find any similar answers on here, but apologies if this it too specific to my own problem.
I am building a simple game using in Java that has both a command line interface and a GUI (activated using a command line flag). The way that the game is written is that the game logic (Model) has a reference to the input (Controller) and the output (View), but neither the input or output have a reference to the model (this was a requirement). The flow of the game is therefore controlled by a loop in the application model similar to:
while (!gameFinished) {
InputType in = input.getUserInput(); //1
performAction(in);
}
Now that I am trying to build a Swing GUI for this (not something I have experience in), I am struggling to see how it could work with event listening. It would ideally work in the way that when a button is pressed (new game, save game, exit game etc.), an InputType would be sent to the Model (essentially doing the same that the commented line does) to be handled accordingly. But if it doesn't hold a reference to the Model, it can't 'send' the InputType. I suppose I am looking to build a GUI that works with the Model 'asking' for input, rather than 'listening' for it.
Is this possible? And if so, can anyone provide me with any useful resources for solving this? If not, an explanation or potential alternative solution suggestion would be appreciated.
I'm not gonna go into whether your flow is right or wrong.
Create an event queue in input. Listeners can then add events to the queue. Model can then ask the Input whether there are unhandled events in the queue and perform an action depending on the event that occurred. Let model hold a reference to the view interface call the appropriate view method in the performAction method.
Pseudo code:
class Controller{
Queue<UIEvent> events;
void setupUI(){
button.addEventListener( new EventListener(){
Model.this.events.add(new TappedButtonEvent());
});
}
UIEvent dequeueEvent(){
if(events.size() > 0){
return events.pop()
}
return null;
}
}
class Model{
public void loop(){
while (!gameFinished) {
UIEvent in = input.dequeueEvent();
if(in != null){
performAction(in);
}
}
}
}
Do not encapsulate how something is displayed in Model, let view handle it.
interface View{
void displayExitMessage()
}
class CommandLineView implements View{
void displayExitMessage(){
this.commandLine.append("Are you sure you want to exit(Y/N)?");
}
}
class CommandLineView implements View{
void displayExitMessage(){
this.window.showDialog("Are you sure you want to exit?", Button.YES, Button.NO);
}
}

Terminate JavaFX Application using Platform.exit()

I am using JavaFX 2.2 and I have a class which extends Application. Here is my code:
Class A extends Application {
public void Stage(final Stage primaryStage) { ... }
public void Start(){
launch();
}
btnLogin.setOnAction(new EventHandler<ActionEvent>() {
Platform.exit();
}
}
Class B{ }
Class C extends Application{
public void Stage(final Stage primaryStage) { ... }
public void Start(){
launch();
}
}
Actually, Class A is login screen; it will close when I successfully log in. Then the screen closed by platform.exit() function. After that I execute view button in Class B , Class C called but there are some problems.
java.lang.IllegalStateException: Application launch must not be called more than once
I just terminate the screen by using Platform.exit() function but I can't understand why it can't be closed.
Platform.exit() actually terminates whole jfx.
To keep things safe, just invoke launch() once and show/hide new windows.
Something like:
Platform.setImplicitExit(false);//make fx running in backgound.
Platform.runLater/AndWait {//make sure u create window in jfx thread
//window creation/show code here.
}
If Class B is the main screen and you need to Embed JavaFX in your application for Login Screen or any other screen, you don't need Class A and Class C to extend Application.
You can just create a new Window in Swing inside these classes (A and C) and use JFXPanel to embed JavaFX into your Swing Application. This way you can have full control on the application and you can easily open and close windows for Login or any other functionality that you want.
N.B. You should not have two class extending Application inside one app, as only one JavaFX thread is allowed per JVM.
Everytime you try to do this you will get this error
java.lang.IllegalStateException: Application launch must not be called more than once

Display pop-up message window in Java?

I read about JDialogs and JOptionPane messages but I still can't get it to work. I have a GUI class that extends JFrame. All I want to do is have a popup at the beginning of my program which informs the user about a couple of things. In my main I create the following gui:
GUI g = new GUI();
Right after that I was to display the window. I have tried the following in the main method:
JOptionPane.showMessageDialog(g, "work?");
JOptionPane.showMessageDialog(frame, "work?"); //(frame was used in documentation example so I tried it)
I also tried to add the pop up into the GUI class with the following
JOptionPane.showMessageDialog(this, "work?"); //(I'm not exactly sure what the Frame Owner parameter is supposed to be, unless I'm confusing this with JDialog.)
In any case, how would I make this window appear? Every single one of the methods I tried compiled, and nothing happened.
public class GUI extends JFrame implements ActionListener{
private Container background;
private static buttons etc...
private static JLabel disp,edisp;
private static JTextArea info;
//setting up the GUI for my program, adding action listeners, I can post more if necessary
}
And then I have the main where I want to call the pop up window
public static void main(String[] args){
GUI g = new GUI();
JOptionPane.showMessageDialog(g,"Work?");
}
Make sure that these are called near the beginning, be it in the main method or not.
Also, try just setting the first parameter as null.
So it reads:
JOptionPane.showMessageDialog(null,"Work?");
Also, remember to import it!

JavaFX: Updating UI elements in a Controller class from a Thread

In JavaFX, I have a Controller class that pulls control components from an FXML file and has methods that act on the component, shown with a Label here:
public class ViewController {
#FXML private Label labelStatus;
public void updateStatusLabel(String label) {
labelStatus.setText("Status: " + label);
}
}
I also have a Java Thread with a run() method, like this:
public class Server extends Thread {
public void run() {
super.run();
}
}
This Server thread handles some socket connections that I need for my particular application. After a connection has been established (in the run() method -- not shown), I need to update the Label in the FXML Controller. How would I do this?
Note: I've purposely made my code and question general so it may help others with the same problem.
You call Platform.runLater(runnable) off the JavaFX UI thread to execute a runnable that updates elements of the active JavaFX Scene Graph on the JavaFX UI thread.
Also review Concurrency in JavaFX, with the Task and Service classes and see if that is not a more appropriate solution to your particular task.
For more information, see:
Usage of JavaFX Platform.runLater and access to UI from a different thread.
Platform.runLater and Task in JavaFX
JavaFx response to SwingUtilities.invokeLater

Categories

Resources