JavaFX Event handling from a different class - java

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.

Related

Avoiding framework-imposed circular dependencies in Guice

Please note: Although this question specifically calls out Swing, I believe this to be a pure Guice (4.0) question at heart and which highlights a potential generic issue with Guice and other opinionated frameworks.
In Swing, you have your application UI, which extends JFrame:
// Pseudo-code
class MyApp extends JFrame {
// ...
}
Your app (JFrame) needs a menubar:
// Pseudo-code
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
And your menu items need action listeners, many of which will updated your app's contentPane:
widgetsSubmenu.addActionListener(new ActionListener() {
#Override
void actionPerformed(ActionEvent actionEvent) {
// Remove all the elements from the main contentPane
yourApp.contentPane.removeAll()
// Now add a new panel to the contentPane
yourApp.contentPane.add(someNewPanel)
}
})
And so there is intrinsically a circular dependency:
Your app/JFrame needs a JMenuBar instance
Your JMenuBar needs 0+ JMenus and JMenuItems
Your JMenuItems (well, the ones that will update your JFrame's contentPane') need action listeners
And then, in order to update the JFrame's contentPane, these action listeners need to reference your JFrame
Take the following module:
// Pseudo-code
class MyModule extends AbstractModule {
#Override
void configure() {
// ...
}
#Provides
MyApp providesMyApp(JMenuBar menuBar) {
// Remember MyApp extends JFrame...
return new MyApp(menuBar, ...)
}
#Provides
JMenuBar providesMenuBar(#Named('widgetsListener') ActionListener widgetsMenuActionListener) {
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
widgetsSubmenu.addActionListener(widgetsMenuActionListener)
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
}
#Provides
#Named('widgetsListener')
ActionListener providesWidgetsActionListener(Myapp myApp, #Named('widgetsPanel') JPanel widgetsPanel) {
new ActionListener() {
#Override
void actionPerformed(ActionEvent actionEvent) {
// Here is the circular dependency. MyApp needs an instance of this listener to
// to be instantiated, but this listener depends on a MyApp instance in order to
// properly update the main content pane...
myApp.contentPane.removeAll()
myApp.contentPane.add(widgetsPanel)
}
}
}
}
This will produce circular dependency errors at runtime, such as:
Exception in thread "AWT-EventDispatcher" com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Tried proxying com.me.myapp.MyApp to support a circular dependency, but it is not an interface.
while locating com.me.myapp.MyApp
So I ask: what's the way of circumventing this? Does Guice have an API or extension library for dealing with this sort of problem? Is there a way to refactor the code to break the circular dependency? Some other solution?
Update
Please see my guice-swing-example project on GitHub for a SSCCE.
There are several techniques available to refactor a circular dependency so that it is no longer circular, and Guice docs recommend doing that whenever possible. For when that is not possible, the error message Guice provided hints at the Guice way to resolve this in general: separate your API from your implementation by using an interface.
Strictly speaking it is only necessary to do this for one class in the circle, but it may improve modularity and ease of writing test code to do it for most or every class. Create an interface, MyAppInterface, declare in it every method that your action listeners need to call directly (getContentPane() seems to be the only one in the code you posted), and have MyApp implement it. Then bind MyAppInterface to MyApp in your module configuration (in your case simply changing the return type of providesMyApp should do it), declare the action listener provider to take a MyAppInterface, and run it. You might also need to switch some other places in your code from MyApp to MyAppInterface, depends on the details.
Doing this will allow Guice itself to break the circular dependency for you. When it sees the circular dependency, it will generate a new zero-dependency implementing class of MyAppInterface that acts as a proxy wrapper, pass an instance of that into the action listener provider, fill out the dependency chain from there until it can make a real MyApp object, and then it will stick the MyApp object inside Guice's generated object which will forward all method calls to it.
Though I used MyAppInterface above, a more common naming pattern would be to actually use the MyApp name for the interface and rename the existing MyApp class to MyAppImpl.
Note that Guice's method of automatic circular dependency breaking requires that you not call any methods on the generated proxy object until after initialization is complete, because it won't have a wrapped object to forward them to until then.
Edit: I don't have Groovy or Gradle ready to try to run your SSCCE for testing, but I think you are very close to breaking the circle already. Annotate the DefaultFizzClient class and each of your #Provides methods with #Singleton, and remove the menuBar parameter from provideExampleApp, and I think that should make it work.
#Singleton: A class or provider method should be marked #Singleton when only a single instance of it should be made. This is important when it is injected in multiple different places or requested multiple times. With #Singleton, Guice makes one instance, saves a reference to it, and uses that one instance every time. Without, it makes a new separate instance for each reference. Conceptually, it's a matter of whether you are defining "how to make an X" or "this here is the X". It seems to me that the UI elements you're creating fall in the latter category - the singular menu bar for your app, not any arbitrary menu bar, etc.
Removing menuBar: You've already commented out the one and only line in that method that uses it. Simply delete the parameter from the method declaration. As for how to get the menu bar into the app anyway, that is already handled by the addMenuToFrame method combined with the requestInjection(this) call.
Perhaps it might help to do a run through of the logic Guice will go through if these alterations are made:
When you create the injector with an ExampleAppModule, it calls your module's configure() method. This sets up some bindings and tells Guice that, whenever it's done with all the bindings setup, it should scan the ExampleAppModule instance for fields and methods annotated with #Inject and fill them in.
configure() returns and, with bindings setup complete, Guice honors the requestInjection(this) call. It scans and finds that addMenuToFrame is annotated with #Inject. Inspecting that method, Guice finds that an ExampleApp instance and a JMenuBar instance are needed in order to call it.
Guice looks for a way to make an ExampleApp instance and finds the provideExampleApp method. Guice examines that method and finds that a FizzClient instance is needed to call it.
Guice looks for a way to make a FizzClient instance and finds the class binding to DefaultFizzClient. DefaultFizzClient has a default no-args constructor, so Guice just calls that to get an instance.
Having acquired a FizzClient instance, Guice is now able to call provideExampleApp. It does so, thereby acquiring an ExampleApp instance.
Guice still needs a JMenuBar in order to call addMenuToFrame, so it looks for a way to make one and finds the providesMenuBar method. Guice examines that method and notes that it needs an ActionListener named "widgetsMenuActionListener".
Guice looks for a way to create an ActionListener named "widgetsMenuActionListener" and finds the providesWidgetsMenuActionListener method. Guice examines that method and finds that it needs an ExampleApp instance and a JPanel named "widgetsPanel". It already has an ExampleApp instance, acquired in step 5, and it got that from something marked as #Singleton, so it reuses the same instance rather than calling provideExampleApp again.
Guice looks for a way to make a JPanel named "widgetsPanel" and finds the providesWidgetPanel method. This method has no parameters, so Guice just calls it and acquires the JPanel it needs.
Having acquired a JPanel with the right name in addition to the already-made ExampleApp, Guice calls providesWidgetsMenuActionListener, thereby acquiring a named ActionListener instance.
Having acquired an ActionListener with the right name, Guice calls providesMenuBar, thereby acquiring a JMenuBar instance.
Having finally, at long last, acquired both an ExampleApp instance and a JMenuBar instance, Guice calls addMenuToFrame, which adds the menu to the frame.
Now after all that has happened, your getInstance(ExampleApp) call in main gets executed. Guice checks, finds that it already has an ExampleApp instance from a source marked #Singleton, and returns that instance.
Having looked through all of that, it seems #Singleton is strictly necessary only on provideExampleApp, but I think makes sense for everything else too.
Well, if you do have a circular dependency, it can't be a creational circular dependency - since otherwise you wouldn't be able to create that cycle of objects at all.
You can perform post-creation configuration by requesting injection on a module. For example, you can use this to add the JMenuBar to the JFrame:
class MyModule extends AbstractModule {
#Override void configure() {
requestInjection(this);
}
#Inject void addMenuToFrame(JFrame frame, JMenuBar menu) {
frame.setMenuBar(menu);
}
}
I've not tested this to see if it works (as you've not given enough code to try it on) - so you might need to experiment with where the right place is to break the current cycle in order to subsequently join it like this - but something like this should work for you.
I've answered a few other questions about injecting into guice modules like this - maybe have a look at those too, since there are some great tips from others about this too, rather than me trying and failing to repeat them correctly here.
I've been playing with this a bit more to help my understanding, and I've found that you need your JFrame and JMenuBar to be bound in singleton scope for this to work usefully, because the method injection occurs upon creation of the injector, not upon subsequent creation of JFrame and JMenuBar instances (probably obviously).
I'm answering this without being able to test the answer against your code, because I don't know (and I don't want to learn) Groovy.
But basically, when you have circular dependencies, the best way to break it is to use a Provider.
package so36042838;
import com.google.common.base.*;
import com.google.inject.Guice;
import javax.inject.*;
public class Question {
#Singleton public final static class A {
B b;
#Inject A(B b) { this.b = b; }
void use() { System.out.println("Going through"); }
}
#Singleton public final static class B {
A a;
#Inject B(A a) { this.a = a; }
void makeUseOfA() { a.use(); }
}
public static void main(String[] args) {
Guice.createInjector().getInstance(B.class).makeUseOfA(); // Produces a circular dependency error.
}
}
This can then be rewritten like this:
package so36042838;
import com.google.common.base.*;
import com.google.inject.Guice;
import javax.inject.*;
public class Question {
#Singleton public final static class A {
B b;
#Inject A(B b) { this.b = b; }
void use() { System.out.println("Going through"); }
}
#Singleton public final static class B {
Supplier<A> a;
#Inject B(Provider<A> a) { this.a = Suppliers.memoize(a::get); } // prints "Going through" as expected
void makeUseOfA() { a.get().use(); }
}
public static void main(String[] args) {
Guice.createInjector().getInstance(B.class).makeUseOfA();
}
}

Null pointer when loading another scene in JavaFX [duplicate]

I'm currently teaching myself JavaFX, and I've taken a simple example program that's hardcoded the view and am turning it into one that uses FXML (mostly so I can use SceneBuilder for building UIs). Rather than writing a separate controller class, I'm using the application class (so there's 1 Java file and 1 FXML file). I'm not using an initialize() method as it's a linear flow (display the UI, populate the fields, wait for input). The view pops up, but then the app errors out as none of the controls are mapped to the appropriate variables (so for #FXML TableView<...> table, table is null).
However, I put in an initialize() method for debugging, the controls are injected while in initialize(), and then return to null when initialize() exits.
So the question is, does JavaFX instantiate a new instance of the application class as a separate controller class? This would explain why the variable are going out of scope. Or is it something else (e.g. the controls are injected only when being called back from JavaFX actions)?
The default behavior of the FXMLLoader is to create a new instance of the controller class and use that instance as the controller.
Specifically, the FXMLLoader does something like:
Read the root FXML element.
If the root FXML element has a fx:controller attribute, then
If a controller already exists, throw an exception, otherwise create an instance of the specified class1 and set that as the controller
Continue parsing the FXML file. If elements have a fx:id attribute, and a controller exists (by any mechanism), inject those fields into the controller. Similarly register event handlers as calls to methods in the controller instance.
Invoke initialize() on the controller, if a controller exists and it has such a method.
So, the question you asked:
Can application class be the controller class
Yes, but it's probably a terrible idea. If you simply specify the Application subclass as the controller class using fx:controller, then a second instance of the Application subclass is created, #FXML-annotated fields are injected on that second instance, and the initialize() method is invoked on that second instance. Obviously, the #FXML-fields are never initialized on the instance on which start(...) is invoked, and the initialize() method is never invoked on that instance.
The question you probably meant is:
Can the application class instance created at launch be used as the controller?
The answer to this is also yes, and, aside from very small demo programs you intend to immediately discard, it's also probably a very bad idea. You would do this by
public class MyApp extends Application {
#FXML
private Node someNode ;
public void initialize() {
// do something with someNode
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
loader.setController(this);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Note that to use this code, your FXML file must not have a fx:controller attribute.
The problem with this is that you have no separation and no flexibility. (E.g. if you create a second instance of the view defined in your FXML file somewhere, you end up with a second Application subclass instance, which is at best counterintuitive (one application with two Application instances...).)
So I would advocate using a separate class for the controller in basically every case. The Application subclass should contain minimal code and should be used only for starting the application.
1 This step is actually a little more complex. If a class is specified in the fx:controller attribute, and no controller already exists, the FXMLLoader checks for a controllerFactory. If one exists, then the controller is set as the result of passing the specified Class to the controllerFactory's call() method, otherwise it is created by calling newInstance() on the specified class (effectively calling its no-argument constructor).
If you have defined your application class to be the controller in the FXML file, JavaFX will, if I remember correctly, create a new instance of your application class and use the new instance as a controller. Thus, your existing application class still has null for the table.
You can however define the controller programmatically in your application class to use your own instance:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();

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

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

Update UI from parent class on different thread from secondary class

I have a parent class which builds my UI (basically just a text box at the moment). I have created a secondary class which creates a new thread and I need to be able to update the textbox on the parent class from the new thread. Everything I try throws errors. I'm assuming I need to create some kind of dispatcher but my background is C# and I'm not 100% familiar on how to do this in Java.
My latest iteration passes the object of the parent class to a static method in the secondary which ultimately creates the new thread by instantiating an object for the secondary class (which has the run() method inside). I have a constructor in the secondary object that expects the passed in object from the parent class which, when passed in, sets a property that I've decalred in my member section for the parent class. when I try to access that member in the run() method to update a textbox on the parent class, I get an error.
Essentially the secondary class looks something like this:
public class SecondaryClass extends Thread {
private ParentClass pc = null;
public SecondaryClass(ParentClass PC){
pc = PC;
}
public static void StartThread(ParentClass pC){
Thread thread = new SecondaryClass(pC);
thread.start();
}
public void run() {
pc.myTextBox.append("something");
}
}
I've also tried creating a public method on the parent class that accepts a string as it's only argument and calling that method from within run() on the secondary class and passing "somethign" there. The method on the parent class updates the same textbox but having problems there as well.
can anyone please provide some insight as to what I need to do to access UI elements across these threads?
you will need to use SwingWorker
Similar question answered on stackoverflow for J2ME:
Interacting with UI threads in Java/J2ME
Also read following:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
Check simple example here:
http://download.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Hope this helps.

Categories

Resources