too many different javafx stages - java

I have 2 sides of the MVC. on the model side, is where i have my main class for the entire battleship program. It instantiates the view/controller side of things, which consists of 3 different windows(classes that extend Application): a PreBoard, which gets both players names, and then one player board each (P1Board, P2Board). In all 3 of these separate classes, they extend Application, and all have a start(Stage primaryStage) method.
Since ive been reading about javaFX threading for the last 48 hours i am still barely understanding where the javaFX application thread starts. Does the javaFX Application thread start the very first time that Application.launch() is called, even if you have 3 seperate classes that extend Application and have their own start methods?
My original intentions were to have a window where players can enter their names, then a separate window with their own board, and i have failed MISERABLY because im getting more and more exceptions the longer the whole program runs.
So the question is, where the hell does the javaFX Application thread start?
Main class, on the model side
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package battleship.model;
import battleship.viewcon.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
/**
*
* #author foolishklown
*/
public class MainApp {
Player player1;
Player player2;
BattleshipGame theGame;
PreBoard theGamePreBoard;
ViewCon viewConnector;
public void go() {
theGamePreBoard = new PreBoard();
theGamePreBoard.setMainAppConnection(this);
viewConnector = theGamePreBoard.getVcon();
}
public void startBsGame(String[] names) {
theGame = new BattleshipGame(names[0], names[1]);
viewConnector.setGame(theGame);
}
public BattleshipGame getGame() {
return theGame;
}
public void setConnection(ViewCon vc) {
this.viewConnector = vc;
}
public static void main(String[] args) {
MainApp app = new MainApp();
app.go();
}
}
PreBoard code, which instantiates 2 other classes that extend Application and have their own start methods......
package battleship.viewcon;
import battleship.model.*;
import javafx.geometry.Insets;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
/**
* PreBoard class, used for getting user input for players names
* #author Chris Wilson
* #author Bob McHenry
* #author Mario Rodriguez De la Raza en la casa!
* #author Jessy Bernoudi
*/
public class PreBoard extends Application {
private boolean turn; // field to determine which players name to put into which board
private String player;
private Button hideBtn;
private Button showBtn;
private TextField userText;
private ViewCon controller;
private P1Board p1B;
private P2Board p2B;
private BattleshipGame game;
private String[] playerNames;
private MainApp mainApp;
/**
* Application class override method, where javaFX stage starts
* #param primaryStage
*/
#Override
public void start(Stage primaryStage) {
playerNames = new String[2];
turn = false;
p1B = new P1Board();
p2B = new P2Board();
controller = new ViewCon();
controller.setPreB(this);
controller.setp1(p1B);
controller.setp2(p2B);
controller.setMain();
primaryStage.setTitle("Battleship setup"); //Main stage (window container)
//Gridpane for using rows/columns for child node placement
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER_LEFT);
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(100, 25, 25, 25));
// label in window
Text sceneTitle = new Text("Setup");
sceneTitle.setId("setup-text");
grid.add(sceneTitle, 0, 0, 2, 1);
// label and textfield
Label userName = new Label("Enter Player1 UserName:");
userName.setId("user-name");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
userTextField.setId("text-field");
grid.add(userTextField, 0, 2);
// button for setup, with actionListener to save player name or default if its left blank
Button setupBtn = new Button("Setup Board");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_LEFT);
hbBtn.getChildren().add(setupBtn);
grid.add(hbBtn, 0, 3);
setupBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// determine which player name to use to pass into which player board
if(turn == false) {
String temp1 = userTextField.getText();
if(temp1.equals("")) {
player = "Player1";
} else {
player = temp1;
}
playerNames[0] = player;
controller.setPlayer1(player);
turn = true;
p1B.start(new Stage());
grid.getChildren().remove(userTextField);
userText = new TextField();
userText.setId("text-field");
grid.add(userText, 0, 2);
userName.setText("Enter Player2 username:");
} else {
String temp2 = userText.getText();
if(temp2.equals("")) {
player = "Player2";
} else {
player = temp2;
}
playerNames[1] = player;
controller.startGame(playerNames);
controller.setPlayer2(player);
p2B.start(new Stage());
p1B.primeShow();
}
}
});
hideBtn = new Button();
hideBtn.setId("hideBtn");
hideBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
primaryStage.hide();
}
});
showBtn = new Button();
showBtn.setId("showBtn");
showBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
primaryStage.show();
}
});
controller.setPreShowBtn(showBtn);
controller.setPreHideBtn(hideBtn);
// Add the entire scene into the main window(stage) after setting the scene dimensions
Scene scene = new Scene(grid, 580, 200);
primaryStage.setScene(scene);
// Attach css stylesheet
scene.getStylesheets().add(PreBoard.class.getResource("styles/PreBoardStyle.css").toExternalForm());
// Show this window(stage) upon instantiation
primaryStage.show();
}
/**
*
* #param v
*/
public void setLink(ViewCon v) {
this.controller = v;
}
/**
*
* #param b
*/
public void setBattleshipGame(BattleshipGame b) {
this.game = b;
}
/**
*
* #param main
*/
public void setMainAppConnection(MainApp main) {
this.mainApp = main;
}
/**
*
* #return
*/
public MainApp getMainConnection() {
return mainApp;
}
/**
*
* #return
*/
public ViewCon getVcon() {
return controller;
}
public void exitPre() {
Platform.exit();
}
public static void main(String[] args) {
Application.launch(args);
}
}

Related

Trying to assign an alarm with javafx

Hello there brilliant minds of stack overflow!
I am currently working on a personal program that should ultimately function as a reminder every 20 or so minutes to do another task(one of those productivity-boosting things) as well as a basic timer that will tell me when my shift is over # work.
I am having difficulty parsing the appropriate textfields into an int to place in the timers I am making(aswell as the profile object).
I know there are probably a couple ways to go about this please let me know your thoughts, here is my code so far:
package javafxneoalarm;
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
*
* #author Hvd
*/
public class JavaFXNeoAlarm extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Neo Alarm");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text sceneTitle = new Text("Welcome! \nPlease Enter Name &\nThe Hour/Minute of your Alarm");
sceneTitle.setFont(Font.font("Helvetica", FontWeight.NORMAL, 20));
grid.add(sceneTitle, 0, 0, 2, 1);
Button btn = new Button("Lets go!");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(btn);
grid.add(hbBtn, 1, 4);
final Text actiontarget = new Text();
grid.add(actiontarget, 1, 6);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e){
actiontarget.setFill(Color.FIREBRICK);
actiontarget.setText("Count-down initiated \nMay the force be with you");
}
});
Label userName = new Label("Name: \n");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
grid.add(userTextField, 1, 1);
Label a1 = new Label("Alarm1: \n");
grid.add(a1, 0, 2);
TextField a1BoxHr = new TextField();
grid.add(a1BoxHr, 1, 2);
TextField a1BoxMin = new TextField();
grid.add(a1BoxMin, 2, 2);
Label a2 = new Label("Alarm2: \n");
grid.add(a2, 0, 3);
TextField a2BoxHr = new TextField();
grid.add(a2BoxHr, 1, 3);
TextField a2BoxMin = new TextField();
grid.add(a2BoxMin, 2, 3);
Scene scene = new Scene(grid, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
// double 1BoxHr = Double.parseDouble(a1BoxHr);
// profileOne = new Profile(userTextField, (((a1BoxHr*60)+a1BoxMin)*60), (((a2BoxHr*60)+a2BoxMin)*60));
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
Timer alarmA1 = new Timer();
Timer alarmA2 = new Timer();
TimerTask task = new TimerTask()
{
public void run(){
// when timer goes off
}
};
Profile profileOne;
}
}
And the profile class:
package javafxneoalarm;
/**
*
* #author Hvd
*/
public class Profile {
// declare instance variables
private String user;
private double alarm1;
private double alarm2;
// constructor (overloaded, has new instance variable or parameter to apply maths through)
public Profile(String newUser, double newAlarm1, double newAlarm2){
user = newUser;
alarm1 = newAlarm1;
alarm2 = newAlarm2;
}
// getters
public String getUser(){
return user;
}
public double getAlarm1(){
return alarm1;
}
public double getAlarm2(){
return alarm2;
}
// setters
public void setUser(String newUser){
user = newUser;
}
public void setAlarm1(double newAlarm1){
alarm1 = newAlarm1;
}
public void setAlarm2(double newAlarm2){
alarm2 = newAlarm2;
}
}
So basically how do I assign the inputs to an alarm that will go off after x amount of time, also I would like the window to close/minimize to tray after inputting/submitting the profile information and re-open when the alarm goes off but that might be a challenge for another day.
Thanks a lot guys, I look forward to continuing this creative endeavor :)
Here's how to assign a double depending on the input in the textfield, I should be placing the assignment inside the button click:
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e){
actiontarget.setFill(Color.FIREBRICK);
actiontarget.setText("Count-down initiated \nMay the force be with you");
String BUser = userTextField.getText(); // sets variable BUser from inputs
double BAlarm1Hrs = Double.parseDouble(a1BoxHr.getText());
double BAlarm1Min = Double.parseDouble(a1BoxMin.getText());
double BAlarm1 = (((BAlarm1Hrs * 60) * 60) + (BAlarm1Min * 60)); // sets BAlarm1 to seconds of hrs and minutes inputted
double BAlarm2Hrs = Double.parseDouble(a2BoxHr.getText());
double BAlarm2Min = Double.parseDouble(a2BoxMin.getText());
double BAlarm2 = (((BAlarm2Hrs * 60) * 60) + (BAlarm2Min * 60));
Profile profileA = new Profile(BUser, BAlarm1, BAlarm2);
}
});

JavaFX Transparent Cursor using WritableImage

Edit-Answer:
You can check Fabian's answer and also this library (https://github.com/goxr3plus/JFXCustomCursor)
Actual Question
I want to create a cursor which is fading out in JavaFX so for that i am using a WritableImage and i am continuously reading pixels from the original Image and writing them to a new WritableImage.Then i set a custom cursor to the Scene using ImageCursor(writableImage),below is the full code(give it a try).
The problem is that a get black pixels where transparent pixels are expected.
Note that all the below classes have to be in package sample.
Code(Main):
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application {
FadingCursor fade = new FadingCursor();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setWidth(300);
primaryStage.setHeight(300);
Scene scene = new Scene(new FlowPane());
primaryStage.setScene(scene);
fade.startFade(scene,100);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Code(FadingCursor)(Edited):
package sample;
import java.util.concurrent.CountDownLatch;
import javafx.application.Platform;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
public class FadingCursor {
private int counter;
private Image cursorImage;
/**
* Change the image of the Cursor
*
* #param image
*/
public void setCursorImage(Image image) {
this.cursorImage = image;
}
/**
* Start fading the Cursor
*
* #param scene
*/
public void startFade(Scene scene, int millisecondsDelay) {
// Create a Thread
new Thread(() -> {
// Keep the original image stored here
Image image = new Image(getClass().getResourceAsStream("fire.png"), 64, 64, true, true);
PixelReader pixelReader = image.getPixelReader();
// Let's go
counter = 10;
for (; counter >= 0; counter--) {
CountDownLatch count = new CountDownLatch(1);
Platform.runLater(() -> {
// Create the fading image
WritableImage writable = new WritableImage(64, 64);
PixelWriter pixelWriter = writable.getPixelWriter();
// Fade out the image
for (int readY = 0; readY < image.getHeight(); readY++) {
for (int readX = 0; readX < image.getWidth(); readX++) {
Color color = pixelReader.getColor(readX, readY);
// Now write a brighter color to the PixelWriter.
// -------------------------Here some way happens
// the problem------------------
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (counter / 10.00) * color.getOpacity());
pixelWriter.setColor(readX, readY, color);
}
}
System.out.println("With counter:"+counter+" opacity is:" + writable.getPixelReader().getColor(32, 32).getOpacity());
scene.setCursor(new ImageCursor(writable));
count.countDown();
});
try {
// Wait JavaFX Thread to change the cursor
count.await();
// Sleep some time
Thread.sleep(millisecondsDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
The image(needs to be downloaded)(Right Click ->Save Image as...):
You set the opacity of every pixel to a value only depending on the loop variable here:
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), counter / 10.00);
For transparent pixels (opacity = 0) you actually increase the opacity making the values stored in the other channels (in this case 0 / black) visible. You need to make sure transparent pixels remain transparent, which usually is done like this:
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (counter / 10.00) * color.getOpacity());
Alternatively you could use deriveColor:
color = color.deriveColor(0, 1, 1, counter / 10d);
Edit
For some reason ImageCursor doesn't seem to like a completely transparent image. You can check that this works, if at least one pixel is not completely transparent by adding
pixelWriter.setColor(0, 0, new Color(0, 0, 0, 0.01));
After the for loops writing the image.
To fix this you could simply use Cursor.NONE instead of an ImageCursor with a fully transparent image:
for (; counter >= 1; counter--) {
...
}
Platform.runLater(() -> scene.setCursor(Cursor.NONE));
 
Alternative without the need to recreate the image/cursor
You could simulate the cursor yourself by moving a image Across the root of the Scene. This won't make the image show up beyond the bounds of the Scene, but you can apply animations to the ImageView for fading instead of modifying the opacity of each pixel manually...
public class CursorSimulator {
private final FadeTransition fade;
public CursorSimulator(Image image, Scene scene, ObservableList<Node> rootChildrenWriteable, double hotspotX, double hotspotY) {
ImageView imageView = new ImageView(image);
imageView.setManaged(false);
imageView.setMouseTransparent(true);
fade = new FadeTransition(Duration.seconds(2), imageView);
fade.setFromValue(0);
fade.setToValue(1);
// keep image on top
rootChildrenWriteable.addListener((Observable o) -> {
if (imageView.getParent() != null
&& rootChildrenWriteable.get(rootChildrenWriteable.size() - 1) != imageView) {
// move image to top, after changes are done...
Platform.runLater(() -> imageView.toFront());
}
});
scene.addEventFilter(MouseEvent.MOUSE_ENTERED, evt -> {
rootChildrenWriteable.add(imageView);
});
scene.addEventFilter(MouseEvent.MOUSE_EXITED, evt -> {
rootChildrenWriteable.remove(imageView);
});
scene.addEventFilter(MouseEvent.MOUSE_MOVED, evt -> {
imageView.setLayoutX(evt.getX() - hotspotX);
imageView.setLayoutY(evt.getY() - hotspotY);
});
scene.setCursor(Cursor.NONE);
}
public void fadeOut() {
fade.setRate(-1);
if (fade.getStatus() != Animation.Status.RUNNING) {
fade.playFrom(fade.getTotalDuration());
}
}
public void fadeIn() {
fade.setRate(1);
if (fade.getStatus() != Animation.Status.RUNNING) {
fade.playFromStart();
}
}
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 500, 500);
Image image = new Image("http://i.stack.imgur.com/OHj1R.png");
CursorSimulator simulator = new CursorSimulator(image, scene, root.getChildren(), 32, 50);
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
private boolean fadeOut = true;
#Override
public void handle(MouseEvent event) {
if (fadeOut) {
simulator.fadeOut();
} else {
simulator.fadeIn();
}
fadeOut = !fadeOut;
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
The reason of this question was to create a kind of cursor that can be
modified.For example here i wanted to make it had a fade effect.For
future users who want to create custom cursors i have created a
library on github and i will show some code here:
https://github.com/goxr3plus/JFXCustomCursor
Code:
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
/**
* This class allows you to set as a Cursor in a JavaFX Scene,whatever you want
* ,even a video!. <br>
* <br>
* <b>What you have to do is create a basic layout,for example:</b><br>
* #-->A BorderPane which contains a MediaView,<br>
* #-->A StackPane which contains an animated ImageView,<br>
* #-->A Pane which contains an animated Rectangle or something more complex
* etc..)<br>
*
* <br>
* <br>
* The options are unlimited!
*
* #author GOXR3PLUS
* #param <T>
* #Version 1.0
*/
public class JFXCustomCursor {
private SimpleIntegerProperty hotSpotX = new SimpleIntegerProperty();
private SimpleIntegerProperty hotSpotY = new SimpleIntegerProperty();
private Scene scene;
private Pane sceneRoot;
private Pane content;
private EventHandler<MouseEvent> eventHandler1;
private EventHandler<MouseEvent> eventHandler2;
private EventHandler<MouseEvent> eventHandler3;
/**
* Constructor
*
* #param scene
* The Scene of your Stage
* #param sceneRoot
* The Root of your Stage Scene
* #param content
* The content of the JFXCustomCursor class
* #param hotspotX
* Represents the location of the cursor inside the content on X
* axis
* #param hotspotY
* Represents the location of the cursor inside the content on Y
* axis
*/
public JFXCustomCursor(Scene scene, Pane sceneRoot, Pane content, int hotspotX, int hotspotY) {
// Go
setRoot(scene, sceneRoot, content, hotspotX, hotspotY);
}
/**
* This method changes the content of the JFXCustomCursor
*
* #param scene
* The Scene of your Stage
* #param sceneRoot
* The Root of your Stage Scene
* #param content
* The content of the JFXCustomCursor class
* #param hotspotX
* Represents the location of the cursor inside the content on X
* axis
* #param hotspotY
* Represents the location of the cursor inside the content on Y
* axis
*/
public void setRoot(Scene scene, Pane sceneRoot, Pane content, int hotSpotX, int hotSpotY) {
// Keep them in case of unRegister-reRegister
unRegister(); // has to be called before the below happens
this.scene = scene;
this.sceneRoot = sceneRoot;
this.content = content;
// hot spots
this.hotSpotX.set(hotSpotX);
this.hotSpotX.set(hotSpotY);
// cursor container
content.setManaged(false);
content.setMouseTransparent(true);
// Keep the Content on the top of Scene
ObservableList<Node> observable = sceneRoot.getChildren();
observable.addListener((Observable osb) -> {
if (content.getParent() != null && observable.get(observable.size() - 1) != content) {
// move the cursor on the top
Platform.runLater(content::toFront);
}
});
if (!observable.contains(content))
observable.add(content);
// Add the event handlers
eventHandler1 = evt -> {
if (!sceneRoot.getChildren().contains(content))
observable.add(content);
};
eventHandler2 = evt -> observable.remove(content);
eventHandler3 = evt -> {
content.setLayoutX(evt.getX() - hotSpotX);
content.setLayoutY(evt.getY() - hotSpotY);
};
scene.addEventFilter(MouseEvent.MOUSE_ENTERED, eventHandler1);
scene.addEventFilter(MouseEvent.MOUSE_EXITED, eventHandler2);
scene.addEventFilter(MouseEvent.MOUSE_MOVED, eventHandler3);
}
/**
* Unregisters the CustomCursor from the Scene completely
*/
public void unRegister() {
if (scene != null) {
sceneRoot.getChildren().remove(content);
scene.removeEventFilter(MouseEvent.MOUSE_ENTERED, eventHandler1);
scene.removeEventFilter(MouseEvent.MOUSE_EXITED, eventHandler2);
scene.removeEventFilter(MouseEvent.MOUSE_MOVED, eventHandler3);
}
}
/**
* Re register the CustomCursor to the Scene,<b>this method is
* experimental(use with caution!)</b>
*/
public void reRegister() {
if (scene != null)
setRoot(scene, sceneRoot, content, hotSpotX.get(), hotSpotY.get());
}
public SimpleIntegerProperty hotSpotXProperty() {
return hotSpotX;
}
public SimpleIntegerProperty hotSpotYProperty() {
return hotSpotY;
}
}

Clock isn't starting

I wrote a little Clock in JavaFX8. On my GUI, I have 2 Buttons. One button should start the timer and the other button should pause the timer. But whenever I press start, nothing is happening. When I delete the whole if() clause, I am able to start the timer via thread.start and it's updating my GUI. I think my call if(isRunning) isn't working how I want.
I appreciate any kind of help!
My Window.java (Main Application)
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package gui;
import java.util.Observable;
import java.util.Observer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import logic.Clock;
import logic.ClockObserver;
/**
*
* #author
*/
public class Window extends Application implements Observer {
private Button b_start = new Button("Start");
private Button b_stop = new Button("Stop");
private Label l_time = new Label("gdfgdf");
private HBox buttonbox = new HBox();
private Clock clock = new Clock();
private Thread thread = new Thread(clock);
#Override
public void start(Stage primaryStage) throws Exception {
thread.start();
buttonbox.setSpacing(5.0);
clock.addObserver(this);
buttonbox.setAlignment(Pos.CENTER);
buttonbox.getChildren().addAll(b_start, b_stop);
BorderPane bp = new BorderPane();
bp.setPadding(new Insets(10.0));
bp.setCenter(l_time);
bp.setBottom(buttonbox);
Scene scene = new Scene(bp);
primaryStage.setMinHeight(150);
primaryStage.setMinWidth(250);
primaryStage.setScene(scene);
primaryStage.setTitle("Uhr");
primaryStage.show();
b_start.setOnAction((ActionEvent e) ->{
clock.setRunning(true);
});
b_stop.setOnAction((ActionEvent e) ->{
clock.setRunning(false);
});
}
public static void main(String args[]) {
launch(args);
}
#Override
public void update(Observable o, Object o1) {
Platform.runLater(new Runnable(){
#Override
public void run() {
l_time.setText(clock.getZeit());
}
});
}
}
My Clock.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
import gui.Window;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
/**
*
* #author
*/
public class Clock extends Observable implements Runnable {
private String zeit = "";
private int sek;
private boolean isRunning = false;
public Clock() {
}
public void setZeit() {
zeit = "" + sek;
}
public String getZeit() {
return zeit;
}
public void setRunning(boolean running){
this.isRunning = running;
}
public boolean isRunning(){
return isRunning;
}
public int getSek() {
return sek;
}
#Override
public void run() {
while (true) {
if (isRunning()) {
try {
sek++;
setZeit();
System.out.println(zeit);
this.setChanged();
this.notifyObservers();
Thread.sleep(1000);
} catch (InterruptedException ex) {
//TODO
}
}
}
}
}
Thanks in advance!

JavaFX proper use of stages and stage controllers (bad start)

I have spent the last couple of days looking into this and trying my hand at some different solutions found but Im still having a hard time implementing them correctly.
I have a project that's bound to turn into something mid-sized so I want to make sure I am using MVC correctly from the start instead of just hacking it apart "making it work".
As of now the application will only open 3 scenes, 2 on start(no fxml needed) and another for settings(using a FXML). I do need to be careful of strong references to these as this application will be running on low resource devices.
Ultimately my questions are:
Am I using scenes and controllers correctly? and given the way I have initiated the settings scene Im not finding a way to edit it with a controller. What am I doing wrong?
Below is what I have so far and I must say I dont like the way I initialize the first 2 scenes from Main and of course trying to start the settings scene is rather haphazard. My hurdle now is trying to interact with the scenes through their controllers.
Main.java
package sample;
import javafx.application.Application;
import javafx.stage.Stage;
import sample.controllers.StageController;
<pre>
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
StageController newStage = new StageController();
newStage.start(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
StageController.java
package sample.controllers;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.List;
import static sample.controllers.HotkeyController.createHotkeys;
import static sample.controllers.WebViewController.createWebView;
/**
* Created by devin on 4/14/16.
*/
public class StageController extends Application
{
#Override
public void start(Stage primaryStage)throws Exception
{
Screen primaryScreen = Screen.getPrimary();
Screen secondaryScreen;
List<Screen> allScreens = Screen.getScreens();
if (allScreens.size() <= 1)
{
System.out.println("Only one screen");
secondaryScreen = primaryScreen;
}
else
{
if (allScreens.get(0).equals(primaryScreen))
{
secondaryScreen = allScreens.get(1);
}
else
{
secondaryScreen = allScreens.get(0);
}
}
configureStage("Primary", primaryStage, primaryScreen);
final Stage secondaryStage = new Stage();
configureStage("Secondary", secondaryStage, secondaryScreen);
}
private void configureStage(final String name, final Stage stage, final Screen screen)
{
Rectangle2D bounds = screen.getBounds();
System.out.println(name + ":" + bounds);
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.initStyle(StageStyle.UNDECORATED);
showStage(name, stage, screen);
stage.show();
stage.setFullScreen(true);
}
private void showStage(final String name, final Stage stage, final Screen screen)
{
//Scene scene = new Scene(new Group());
StackPane root = new StackPane();
Scene scene = new Scene(root);
stage.setScene(scene);
createWebView(name, root);
scene.setRoot(root);
/*
* Use hotkeys to allow application actions
*/
createHotkeys(name, scene);
}
}
WebViewController
package sample.controllers;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.w3c.dom.Document;
/**
* Created by devin on 4/13/16.
*/
public class WebViewController
{
public static void createWebView(final String name, final StackPane root)
{
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
if (name == "Primary") {
webEngine.load("http://google.com");
}
else
{
webEngine.load("http://google.com");
}
webEngine.documentProperty().addListener(new ChangeListener<Document>()
{
#Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc)
{
String heightText = browser.getEngine().executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
).toString();
double height = Double.valueOf(heightText.replace("px", ""));
System.out.println("Height of browser on " + name + " " + height);
}
});
root.getChildren().addAll(browser);
/* This is a firebug call if we need to debug the webpage that is being loaded */
// webEngine.documentProperty().addListener(new ChangeListener<Document>() {
// #Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc) {
// enableFirebug(webEngine);
// }
// });
}
private static void enableFirebug(final WebEngine engine) {
engine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}");
}
}
HotkeyController.java
package sample.controllers;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
/**
* Created by devin on 4/13/16.
*/
public class HotkeyController
{
public static void createHotkeys(final String name, final Scene scene)
{
final KeyCombination exitCMD = new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (exitCMD.match(event))
{
System.out.println("CTRL + E was pressed on " + name + " display\n" +
"Exiting the application");
Platform.exit();
}
}
});
final KeyCombination settingsCMD = new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (settingsCMD.match(event))
{
System.out.println("CTRL + S was pressed on " + name + " display\n" +
"Opening Settings Scene");
/*
* This is where we need to launch a scene for settings
*/
try
{
Parent root = FXMLLoader.load(getClass().getResource("/sample/view/settingsscreen.fxml"));
Stage settingsStage = new Stage();
settingsStage.setTitle("FTX Signage Settings");
settingsStage.setScene(new Scene(root, 500 , 400));
settingsStage.show();
// SettingsController settingsController = new SettingsController();
// GettersSetters getSet = new GettersSetters();
// settingsController.getText();
// String hostname = getSet.getHostname();
// settingsController.setText(hostname);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
});
}
}

JavaFX thread issues

I really do my best to not ask for help here unless i am desperate to the point of school assignment failure, being a new coder. That being said, i have spent the last 3 days trying to figure out the issue with threading. I am trying to instantiate a javafx class thats in a separate package, and keep running into the dreaded "java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main" exception.
I have tried calling theGamePreBoard.start(new Stage()), which doesnt work, and i have also tried calling its start method during construction of that object with a new Stage() passed in during construction. Please help!!!
How can i instantiate this PreBoard() class and get it's start method to run without throwing this?
main class:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package battleship.model;
import battleship.viewcon.*;
import javafx.stage.Stage;
/**
*
* #author foolishklown
*/
public class MainApp {
Player player1;
Player player2;
Board board1;
Board board2;
BattleshipGame theGame;
PreBoard theGamePreBoard;
public void go() {
theGame = new BattleshipGame();
theGamePreBoard = new PreBoard();
theGamePreBoard.start(new Stage());
System.out.println(theGamePreBoard);
theGamePreBoard.setBattleshipGame(theGame);
}
public static void main(String[] args) {
MainApp app = new MainApp();
app.go();
}
}
PreBoard class:
/*
* PreBoard object. This is the starter class for the different JavaFX stages (different windows - username screen,
* and each players window)
* After first username input, this class hides and calls on a new P1Board object
*/
package battleship.viewcon;
import battleship.model.*;
import javafx.geometry.Insets;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
/**
*
*
* #author c-dub
*/
public class PreBoard extends Application {
private boolean turn; // field to determine which players name to put into which board
private String player;
private Button hideBtn;
private Button showBtn;
private TextField userText;
private ViewCon controller;
private P1Board p1B;
private P2Board p2B;
private BattleshipGame game;
private Stage theStage;
#Override
public void start(Stage primaryStage) {
turn = false;
p1B = new P1Board();
p2B = new P2Board();
controller = new ViewCon();
controller.setp1(p1B);
controller.setp2(p2B);
controller.setPreB(this);
this.game = controller.getBattleshipGame();
primaryStage.setTitle("Battleship setup"); //Main stage (window container)
//Gridpane for using rows/columns for child node placement
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER_LEFT);
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(100, 25, 25, 25));
// label in window
Text sceneTitle = new Text("Setup");
sceneTitle.setId("setup-text");
grid.add(sceneTitle, 0, 0, 2, 1);
// label and textfield
Label userName = new Label("Enter UserName:");
userName.setId("user-name");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
userTextField.setId("text-field");
grid.add(userTextField, 0, 2);
// button for setup, with actionListener to save player name or default if its left blank
Button setupBtn = new Button("Setup Board");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_LEFT);
hbBtn.getChildren().add(setupBtn);
grid.add(hbBtn, 0, 3);
setupBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// determine which player name to use to pass into which player board
if(turn == false) {
String temp1 = userTextField.getText();
if(temp1.equals("")) {
player = "Player1";
} else {
player = temp1;
}
controller.setPlayer1(player);
game.setPlayer1(player);
turn = true;
Stage stage = new Stage();
p1B.start(stage);
grid.getChildren().remove(userTextField);
userText = new TextField();
userText.setId("text-field2");
grid.add(userText, 0, 2);
hideBtn.fire();
} else {
String temp2 = userText.getText();
if(temp2.equals("")) {
player = "Player2";
} else {
player = temp2;
}
controller.setPlayer2(player);
game.setPlayer2(player);
Stage stage2 = new Stage();
p2B.start(stage2);
hideBtn.fire();
}
}
});
hideBtn = new Button();
hideBtn.setId("hideBtn");
hideBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
primaryStage.hide();
}
});
showBtn = new Button();
showBtn.setId("showBtn");
showBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
primaryStage.show();
}
});
controller.setPreShowBtn(showBtn);
controller.setPreHideBtn(hideBtn);
// Add the entire scene into the main window(stage) after setting the scene dimensions
Scene scene = new Scene(grid, 580, 200);
primaryStage.setScene(scene);
// Attach css stylesheet
scene.getStylesheets().add(PreBoard.class.getResource("styles/PreBoardStyle.css").toExternalForm());
// Show this window(stage) upon instantiation
primaryStage.show();
}
public void setLink(ViewCon v) {
this.controller = v;
}
public static void main(String[] args) {
Application.launch(args);
}
public void setBattleshipGame(BattleshipGame b) {
this.game = b;
}
}
I don't think this has anything at all to do with threading: I don't see any reason why you would ever create another thread in this application. The part you seem to be missing is the actual life-cycle of a JavaFX application. (There's a little you could need to know about how JavaFX manages threading, but it is a bit incidental here.)
The Application class represents an entire application. Your application should typically have just one Application subclass, and one instance of that class. The instance is created for you by JavaFX when you call the static Application.launch() method (or when you execute your Application subclass from the command line, which effectively calls launch for you).
When launch is invoked, the JavaFX toolkit (including the FX Application Thread) is started. An instance of theApplication subclass is created for you, and then start(...) is invoked on that instance on the FX Application Thread.
So what that means is that the start(...) method is the entry point (startup) for your JavaFX application. Since the most common thing to do here is to display something in a window, a window (Stage) is passed into this method for your convenience: however you can ignore it and just create your own if you like.
A typical start(...) method should be quite short, and will usually just create some UI (maybe defined in another class or in an FXML file), put that UI in a scene, and display the scene in a stage. In a more complex application, you will create an instance of your model class here, create some views and controllers, give the controllers references to the model, and assemble the views.
For a simple structural example, see my answer to Java: How do I start a standalone application from the current one when both are in the same package? (which is a similar question, I think).

Categories