I am getting the follow error when I run a JavaFx application as "Run" only. Debug works fine...
Exception in thread "main" java.lang.ExceptionInInitializerError
at Goose.Program.<clinit>(Program.java:26)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:123)
Caused by: java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 4 more
I have read that you should subclass Application but I am already doing that so I am not sure why it doesn't work... It works fine if I debug but as soon as I try to run the application instead of debugging it, it throws that error message. Which is a little crazy.... Anyone have any idea what the heck is going on? Here is the code.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Program extends Application{
TextField input;
GameServer gm;
Player p = new Player();
/**
* Just starts our GameServer
*/
public static void main(String[] args) throws Exception {
launch(args);
}
public static final TextArea textArea = new TextArea();
#Override
public void start(Stage primaryStage) {
p.setState(Player.States.Ready);
p.setAccess(Player.AccessStatus.GameMaster);
input = new TextField();
input.setPrefWidth(500);
input.setOnKeyPressed(event -> {
if(event.getCode().equals(KeyCode.ENTER)){
textArea.appendText("Command: " + input.getText() + "\n");
handleEvent(input);
input.setText("");
}
});
GridPane gridPane = new GridPane();
gridPane.setAlignment(Pos.CENTER);
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.add(input, 0, 0, 2, 1);
gridPane.add(textArea, 0,2, 2, 1);
Scene scene = new Scene(gridPane, 530, 250);
primaryStage.setMaxWidth(540);
primaryStage.setMaxHeight(280);
primaryStage.setMinWidth(540);
primaryStage.setMinHeight(280);
primaryStage.setTitle("My Server");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setOnCloseRequest(we -> {
try {
textArea.appendText("Shutting down server...");
if(gm.gameworld.getRunning()) {
gm.gameworld.setRunning(false);
Thread.sleep(2000);
}
System.exit(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
run();
}
public void run(){
try {
GameServer gameServer = new GameServer();
this.gm = gameServer;
gameServer.start();
}catch (Exception e){
e.printStackTrace();
}
}
public void handleEvent(TextField textField){
try {
String eventKey = textField.getText().trim();
Event e = gm.gameworld.getEventHandler().stringToEvent.get(eventKey);
if(e != null) {
e.setPlayer(p);
e.ready(gm.gameworld);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a 'public void init() throws Exception' method in Application class whose documentation says:
"The application initialization method. This method is called immediately after the Application class is loaded and constructed. An application may override this method to perform initialization prior to the actual starting of the application". "NOTE: This method is not called on the JavaFX Application Thread. An application must not construct a Scene or a Stage in this method. An application may construct other JavaFX objects in this method."
So, I suppose that you should move: p = new Player(); and
textArea = new TextArea(); to it.
Related
I have a simple login window in JavaFX. When the user inserts his username and password I want to make a simple string "progress bar" in another thread while the main thread processes the inputs.
When the main thread gets to the if statement (let's say the passwords don't match) I want the progress to stop when the alert is thrown. But with this code it continues even after the alert is thrown.
public void validateLogin(ActionEvent actionEvent) throws Exception {
Thread thread = new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(loading_txt.getText().length() < 10)loading_txt.setText(loading_txt.getText() + "|");
else loading_txt.setText("|");
}
});
thread.start();
String username = username_field.getText();
String password = password_field.getText();
if (!(BCrypt.checkpw(password_field.getText(), dbHandler.getLoginByUsername(username_field.getText()).getPassword()))) {
throwAlert(Alert.AlertType.ERROR,"Login problem", "Password doesn't match.", "Wrong password. Please, check out and try it again. ");
thread.join();
return;
}
thread.join();
//other code
}
So I made a little change in the if statement and put the thread.join() before the alert. Now the progress can't be even seen.
if (!(BCrypt.checkpw(password_field.getText(), dbHandler.getLoginByUsername(username_field.getText()).getPassword()))) {
thread.join();
throwAlert(Alert.AlertType.ERROR,"Login problem", "Password doesn't match.", "Wrong password. Please, check out and try it again. ");
return;
}
How does this little change cause the progress to be seen or not to be seen? What do I have to change to stop the progressing when the alert is thrown? Could it be caused by some functionality in JavaFX?
Here is an Example, you may take the idea and apply it to your program (Explanation in comments).
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.control.ProgressBar;
public class ProgressBarExample extends Application{
ProgressBar pb = new ProgressBar(); // your progress bar
#Override
public void start(Stage primaryStage) throws Exception {
// The structure and components are for example only
TextField password = new TextField();
Button test = new Button("Test");
HBox container = new HBox();
container.getChildren().addAll(password, test);
container.setAlignment(Pos.CENTER);
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(container);
// add action listener to the button
test.setOnAction(e->{
// when it's pressed add Progress bar and other stuff that are concerned with the GUI!
Platform.runLater(new Runnable(){ // always use this to update GUI components
#Override
public void run() {
root.getChildren().add(pb);
// you can add label to the root...etc
// or update your progress bar ..etc
// in a nutshell: anything needs to be updated in GUI.
}
});
Task<Boolean> validatePassword = new Task<Boolean>(){ // always use Task to do complex-long calculations
#Override
protected Boolean call() throws Exception {
return validatePassword(password); // method to validate password (see later)
}
};
validatePassword.setOnSucceeded(ee->{ // when Task finishes successfully
System.out.println("Finished");
root.getChildren().remove(pb); // remove the progress bar
if(!validatePassword.getValue()){
Alert alert = new Alert(AlertType.ERROR, "Wrong Password", ButtonType.OK);
alert.showAndWait();
}
});
validatePassword.setOnFailed(eee->{ // if it fails
System.out.println("Failed");
root.getChildren().remove(pb); // remove it anyway
});
new Thread(validatePassword).start(); // add the task to a thread and start it
});
Scene scene = new Scene(root, 300,300);
primaryStage.setScene(scene);
primaryStage.show();
}
// validate here in this method
public static boolean validatePassword(TextField password){
for(int i=0; i<99999; i++){ // suppose it is a long process
System.out.println("Processing");
}
if(password.getText().equalsIgnoreCase("Invalid")){ // suppose it's invalid, just for testing
return false
}
return true;
}
public static void main(String[] args) {
launch();
}
}
Test:
i have a simple JavaFX stage with a TextField. What i want to do is: when user inserts letters into the TextField, i want to print "now" (just to look if it works). Im using a Thread because later i want to scan a dictonary to see, if the letters the user entered are part of words from the dictionary.
But i get: java.lang.IllegalMonitorStateException
Any ideas? I don't seem to understand the whole concept of Condition.await and Multithreading..
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DictionaryThreading extends Application {
private static Lock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
public static void main(String[] args) {
launch();
}
private static class ScanWords implements Runnable{
#Override
public void run() {
lock.lock();
try{
while(true){
this.wait();
System.out.println("clicked");
}
} catch (Exception e){
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
new ScanWords().run();
TextField tf = new TextField("Please enter a word");
tf.setOnKeyPressed(e -> {});
pane.getChildren().add(tf);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
There is no need to create a thread that does nothing other than wait for user events. The JavaFX framework already provides this for you (it is one of the fundamental pieces of functionality of any UI toolkit). All you need to do to respond to changes in the text in a text field is register a change listener with the text field's text property:
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
TextField tf = new TextField("Please enter a word");
tf.textProperty().addListener((obs, oldText, newText) -> {
System.out.println("text changed");
});
pane.getChildren().add(tf);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
If the thing you need to do in response to the text changing takes a long time, then you should launch that process in a background thread in the listener on the text field. If you are searching something large, you probably want to cancel any existing search, so that you don't end up with a large number of searches all running concurrently. The JavaFX Service class provides the functionality you need for this:
public class SearchService extends Service<List<String>> {
// modify and access only on FX Application Thread:
private String searchString ;
#Override
protected Task<List<String>> createTask() {
final String s = searchString ;
return new Task<List<String>>() {
#Override
protected List<String> call() throws Exception {
List<String> matches = new ArrayList<>();
// do search for strings matching s
// be sure to check isCancelled() regularly
return matches ;
}
};
}
public String getSearchString() {
checkThread();
return searchString ;
}
public void setSearchString(String searchString) {
checkThread();
this.searchString = searchString ;
}
private void checkThread() {
if (! Platform.isFxApplicationThread()) {
throw new IllegalStateException("Not on FX Application Thread");
}
}
}
Then you can do
public void start(Stage primaryStage) throws Exception {
StackPane pane = new StackPane();
SearchService searchService = new SearchService();
searchService.setOnSucceeded(e -> {
List<String> matches = searchService.getValue();
// do whatever you need with search results...
// this is called on FX application thread
});
TextField tf = new TextField("Please enter a word");
tf.textProperty().addListener((obs, oldText, newText) -> {
searchService.cancel();
searchService.setSearchText(newText);
searchService.restart();
});
pane.getChildren().add(tf);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
I don't use JavaFX, but I think you need to use EventListener. Try to use TextListener or InputMethodListener. For example:
StackPane pane = new StackPane();
TextField tf = new TextField("Please enter a word");
tf.addTextListener(e -> System.out.println("Pushed"));
pane.getChildren().add(tf);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
wait method should be executed within synchronized block:
try{
while(true){
synchronized(this){
this.wait();
System.out.println("clicked");
}
}
} catch (Exception e){
e.printStackTrace();
} finally{
lock.unlock();
}
I'm trying to create a simple UI that is accessible for screenreaders. I've been mostly successful, but I can't manage to design the UI in a way that has the screenreader read new text output.
Currently, I have a TextArea displaying the output of an anonymous PrintStream created and set by System.setOut. Sometimes I open up a TextField for string inputs, but I've been working with just the TextArea to test the reading of text (for now it just listens for keystrokes to display more text for testing purposes).
The issue is this: when new text is added via System.out to the TextArea, the screenreader does not read it. I am still able to navigate upward with the arrow keys to read what was added but it is not read when first added. Is there any way to get the screenreader to treat my TextArea more like a standard console (in which it reads all new text automatically)? I'm using NVDA.
Things I have tried:
- Using TextArea.notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT)
- Using TextArea.requestFocus() and TextArea.notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_NODE)
- Disabling autoflush on the PrintStream while using TextArea.setAccessibleText(theNewText) during a flush
- Using a hidden Label set to the new text and focusing on it (I'm still fiddling with this one; Screenreaders can't read actual "hidden" text so I'm trying to find a way to draw it but also be "invisible", perhaps behind the TextArea somehow)
- Changing focus to another Node and back, which doesn't work as I like because it reads the other Nodes accessible stuff and then reads the entire body of the TextArea
- Various combinations of these
I just can't seem to get it to work. I feel like I'm missing something simple and obvious here, but the JavaFX Accessibility API is still relatively new and I can't find solutions to specific problems like this one.
Here's the relevant code of my Application, if it helps any:
#Override
public void start(Stage primaryStage) {
try {
primaryStage.setTitle("Test");
root = new BorderPane();
root.setFocusTraversable(false);
Scene scene = new Scene(root,800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
//Create middle
output = new TextArea();
output.setEditable(false);
output.setFocusTraversable(false); //I've tried true also, just to test
output.setAccessibleRole(AccessibleRole.TEXT_AREA);
root.setCenter(output);
...
//Begin
primaryStage.show();
Thread th = new Thread(new AppMain());
th.start();
} catch(Exception e) {
e.printStackTrace();
}
}
#Override
public void init() {
//Set output to TextArea
System.setOut(new PrintStream(new OutputStream() {
#Override
public void write(int b) throws IOException {
appendTextArea(String.valueOf((char) b));
}
}, true)); //I've also overriden flush while this is false, see above
}
public void appendTextArea(String str) {
Platform.runLater(() -> {
output.appendText(str);
});
}
I seriously appreciate any help or suggestions you can provide. I've been messing with this small issue for way too long, and I'm still new to JavaFX. Thank you!
Here is a full working example based off of your code.
Disclosure: For the screen reader I'm using the "Voice Over Utility" on my Mac, but hopefully that doesn't differ too much from your environment.
The key was to utilize the Control#executeAccessibleAction method.
Example:
TextArea.executeAccessibleAction(AccessibleAction.SET_TEXT_SELECTION, start, finish);
The Application Class
package application;
import java.io.PrintStream;
import java.io.IOException;
import java.io.OutputStream;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleRole;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
public class Main extends Application
{
private TextArea output;
private BorderPane root;
private final StringBuilder STR_BUFFER = new StringBuilder();
private static final String NEW_LINE = System.lineSeparator();
#Override
public void start(Stage primaryStage)
{
try
{
primaryStage.setTitle("Test");
root = new BorderPane();
root.setFocusTraversable(false);
Scene scene = new Scene(root, 800, 600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
// Create middle
output = new TextArea();
output.setEditable(false);
output.setFocusTraversable(true); // I've tried true also, just to test
// ----------------------------------------------
// Tell the Screen Reader what it needs to access
// ----------------------------------------------
output.setAccessibleRole(AccessibleRole.TEXT_AREA);
root.setCenter(output);
// ...
// Begin
primaryStage.show();
// start the thread
Thread th = new Thread(new AppMain());
th.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void init()
{
// Set output to TextArea when we have a full string
System.setOut(new PrintStream(new OutputStream()
{
#Override
public void write(int b) throws IOException
{
if (b == '\r')
{
return;
}
if (b == '\n')
{
final String text = STR_BUFFER.toString() + NEW_LINE;
appendTextArea(text);
STR_BUFFER.setLength(0);
}
else
{
STR_BUFFER.append((char) b);
}
}
}, true));
}
public void appendTextArea(String str)
{
Platform.runLater(() ->
{
int anchor = output.getText().length();
output.appendText(str);
// just to clear it
output.positionCaret(0);
// ----------------------------------------------
// Tell the Screen Reader what it needs to do
// ----------------------------------------------
output.executeAccessibleAction(AccessibleAction.SET_TEXT_SELECTION, anchor, anchor + str.length());
});
}
public static void main(String[] args)
{
launch(args);
}
}
The Thread Class
/*
* Just to simulate a feed to the console (textArea).
* This will die after 1 minute.
*/
package application;
public class AppMain implements Runnable
{
#Override
public void run()
{
int i = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < 60000)
{
try
{
Thread.sleep(3000);
}
catch (InterruptedException e)
{}
System.out.println("This is line number " + ++i);
}
}
}
I use a background-thread that should not stop immediately when JavaFX stops (as it does when no stage is open anymore as configured with setImplicitExit), so I do not use setDaemon for this one. But how can I check if JavaFX is shutting down? (This thread should just finish some things and stop itself)
I know I could put an setOnCloseRequest to all stages, but I'd prefer not doing that.
Runtime.getRuntime().addShutdownHook() does not work in this case as the machine does not go down as long as this thread is running).
Override Application.stop() and set a flag. Your thread will need to periodically check that flag.
SSCCE:
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ExitThreadGracefullyOnExit extends Application {
AtomicBoolean shutdownRequested = new AtomicBoolean();
#Override
public void start(Stage primaryStage) {
Label countLabel = new Label();
Thread thread = new Thread(() -> {
try {
int count = 0 ;
while (! shutdownRequested.get()) {
count++ ;
final String message = "Count = "+count ;
Platform.runLater(() -> countLabel.setText(message));
Thread.sleep(1000);
}
System.out.println("Shutdown... closing resources");
Thread.sleep(1000);
System.out.println("Almost done...");
Thread.sleep(1000);
System.out.println("Exiting thread");
} catch (InterruptedException exc) {
System.err.println("Unexpected Interruption");
}
});
VBox root = new VBox(10, countLabel);
root.setAlignment(Pos.CENTER);
thread.start();
Scene scene = new Scene(root, 350, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
shutdownRequested.set(true);
}
public static void main(String[] args) {
launch(args);
}
}
I am trying to terminate the thread that runs the JavaFX application when I close the window, without closing any other threads. This is my application class:
package testIt;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
public class MemoryVisualizerApp extends Application{
public static void main(String[] args) {
launch(args);
}
//Setup the scene and launch with given properties
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("/MemoryVisualizer.fxml"));
Scene scene = new Scene(root, 650, 340);
//Set whether the screen should be re-sizable (possibly best size = default)
primaryStage.setResizable(true);
primaryStage.setMinHeight(300);
primaryStage.setMinWidth(550);
primaryStage.setTitle("MINT Performance");
primaryStage.setScene(scene);
scene.getStylesheets().add("testIt/MemoryVisualizer.css");
primaryStage.show();
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
stop();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
}
}
For testing purposes, this app is the only thing running when I start the program, and so when I close the window, the entire program should terminate. But I still have the option to terminate the program (I'm using eclipse and the red square is still clickable), meaning the thread is still active.
How can I have it so that this thread terminates after closing the GUI window?
You can use Platform.exit() instead of stop()
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
Platform.exit();
}
catch (Exception e1) {
e1.printStackTrace();
}
}
});
Take a look at the JavaFX Application life cycle
Try overriding the stop() method:
#Override
public void stop(){
System.exit(0);
}
This will cause the application to stop running in the IDE.