JavaFX 2 Timeline setcyclecount() - javafx

I'm using javafx in javase8 and netbeans 8.0.2, I made randomly generated shape images and show them sequentially with timeline. But last image isn't shown. timeline.setcyclecount(12) i use java generate 12 images but doesn't show 12. image in timeline.
public class JavaFXApplication3 extends Application {
int k;
Timeline timeline;
class ResizableCanvas extends Canvas {
private void draw() {
int[] uyaran = {3, 7, 12};
boolean[] type = new boolean[12];
for (int i = 0; i < 12; i++) {
type[i] = false;
}
for (int v : uyaran) {
type[v - 1] = true;
}
double w = getWidth();
double h = getHeight();
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(0, 0, w, h);
gc.setFill(Color.RED);
System.out.println(k);
if (type[k]) {
gc.fillOval(0, 0, w, h);
}
k++;
}
}
#Override
public void start(Stage stage) throws Exception {
k = 0;
ResizableCanvas canvas = new ResizableCanvas();
timeline = new Timeline(new KeyFrame(Duration.millis(1000), ae -> canvas.draw()));
timeline.setCycleCount(12);
timeline.setOnFinished(ActionEvent -> {
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {}
stage.close();
});
timeline.play();
Pane pane = new Pane();
pane.getChildren().add(canvas);
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
stage.setScene(new Scene(pane));
stage.show();
}
}

You are calling Thread.sleep(...) on the FX Application Thread, which blocks the thread and prevents it from updating. The final ellipse is only actually rendered when the pause is over, but of course you then close the window so you never see it.
Use a PauseTransition to pause, and use its onFinished handler to do something when the pause is over:
timeline.setOnFinished(ae -> {
PauseTransition pause = new PauseTransition(Duration.seconds(10));
pause.setOnFinished(event -> stage.close());
pause.play();
});

Related

How to use Javafx and scenebuilder to draw on canvas (Group element)

i am new to scenebuilder and fx and need some help.
I have the class Puffer , TestMain and MainViewController.
I tried to paint on the canvas and it worked.I tried Puffer and it worked too.
Now I wanted to use scenebuilder and have my problems.I would like to draw the Group from Puffer on the canvas with a button and don't know what's the best solution.I already tried to modify Puffer without any result.
public class Puffer {
Group root = new Group();
public void draw() {
for (int x = 0; x < 800; x++) {
for (int y = 0; y < 600; y++) {
Complex z0 = new Complex(0.11, 0.123); //!!!!!!!!!!!!! under 1
Complex c = new Complex(x / 400.0 - 2, 1 - y / 400.0);
if (isBounded(iterate(z0, c))) {
drawPoint(x, y, root);
}
}
}
// primaryStage.setScene(new Scene(root, 800, 600));
// primaryStage.show();
}
public void drawPoint(int x, int y, Group root) {
int max = 255;
int min = 0;
int range = max - min + 1;
Line line = new Line(x, y, x, y);
// line.setStroke(Color.rgb((int)(Math.random() * range) + min,(int)(Math.random() * range) + min,(int)(Math.random() * range) + min));
root.getChildren().add(line);
}
public Complex iterate(Complex z0, Complex c) {
Complex zn = z0;
for (int i = 0; i < 20; i++) {
zn.square().add(c);
}
return zn;
}
public boolean isBounded(Complex zn) {
return zn.module() < 2;
}
public class MainViewController {
#FXML
Canvas canvas;
// Event Listener on Button.onAction
#FXML
public void btnOkClicked(ActionEvent event) {
System.out.println("Test");
}
#FXML
public void drawCanvas(){
// GraphicsContext gc = canvas.getGraphicsContext2D();
// gc.setLineWidth(3);
// gc.setStroke(Color.BLACK);
// System.out.println("drawCanvas");
//
// try {
// canvas.setOnMousePressed(event -> {
// System.out.println("Mouse click");
// gc.beginPath();
// gc.lineTo(event.getSceneX(), event.getSceneY());
// gc.stroke();
// });
//
// canvas.setOnMouseDragged(event -> {
// System.out.println("Mouse dragged");
// gc.lineTo(event.getSceneX(), event.getSceneY());
// gc.stroke();
// });
// }catch (Exception e){
// System.out.println(e);
// System.exit(0);
// }
}
public class TestMain extends Application {
#Override
public void start(Stage s1) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
Scene scene = new Scene(root);
s1.setTitle("Test");
s1.setScene(scene);
s1.show();
}catch(Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}

JavaFX How to Handle Input With Very High Framerate?

I'm on Ubuntu 20.04 using OpenJavaFX. I want to have the user press the escape key to toggle the display of a menu. Due to the very high frame rate, I'm struggling to achieve this.
The simple program:
class
AutoScalingCanvas extends Region {
private final Canvas canvas;
public AutoScalingCanvas(double canvasWidth, double canvasHeight) {
this.canvas = new Canvas(canvasWidth, canvasHeight);
getChildren().add(canvas);
}
public GraphicsContext getGraphicsContext2D() {
return canvas.getGraphicsContext2D();
}
#Override
protected void layoutChildren() {
double x = getInsets().getLeft();
double y = getInsets().getTop();
double w = getWidth() - getInsets().getRight() - x;
double h = getHeight() - getInsets().getBottom() - y;
// preserve aspect ratio while also staying within the available space
double sf = Math.min(w / canvas.getWidth(), h / canvas.getHeight());
canvas.setScaleX(sf);
canvas.setScaleY(sf);
positionInArea(canvas, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
}
}
public class
Gui extends Application
{
long target_ns_per_frame = 1_000_000_00 / 60;
boolean in_menu;
boolean esc_down;
#Override
public void
start(Stage primary_stage) throws Exception
{
primary_stage.setTitle("GUI");
AutoScalingCanvas canvas = new AutoScalingCanvas(1280, 720);
Scene scene = new Scene(canvas);
scene.setFill(Color.BLACK);
primary_stage.setScene(scene);
GraphicsContext gc = canvas.getGraphicsContext2D();
scene.setOnKeyPressed(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event)
{
esc_down = (event.getCode() == KeyCode.ESCAPE);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.ESCAPE)
{
esc_down = false;
}
}
});
new AnimationTimer()
{
#Override
public void
handle(long total_elapsed_time_ns)
{
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 1280, 720);
if (esc_down)
{
in_menu = !in_menu;
}
if (in_menu)
{
gc.setFill(Color.BLUE);
gc.fillRect(300, 300, 200, 200);
}
else
{
gc.setFill(Color.RED);
gc.fillRect(100, 100, 100, 100);
}
long elapsed_time_ns = System.nanoTime() -
total_elapsed_time_ns;
if (elapsed_time_ns < target_ns_per_frame)
{
long time_remaining_ms = (target_ns_per_frame - elapsed_time_ns)
/ 1000;
try {
Thread.sleep(time_remaining_ms);
}
catch (InterruptedException e)
{
}
}
}
}.start();
primary_stage.show();
}
}
If run without Thread.sleep() the framerate is around 600fps. As a result, pressing the escape key once will be seen as down for a number of frames (due to the speed limit of my human finger) thereby triggering the toggle multiple times. This is obviously not intended. So, I tried to cap the framerate at 60fps. However, with the sleeping, the program runs very slow (perhaps I'm sleeping on the wrong thread?)
How best to keep track of the input to achieve this toggling behavior?
First, you should never block the FX Application Thread by calling Thread.sleep() on it. That will prevent the UI from being updated, or events being handled, until the sleep() is complete.
If the intention is simply that each time the user presses the ESCAPE key that the menu is toggled, then your code is way too complex. Simply toggle a flag indicating if the menu should be painted in the onReleased handler, and check the flag in AnimationTimer.handle():
public class Gui extends Application {
boolean inMenu;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("GUI");
AutoScalingCanvas canvas = new AutoScalingCanvas(1280, 720);
Scene scene = new Scene(canvas);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
GraphicsContext gc = canvas.getGraphicsContext2D();
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.ESCAPE) {
inMenu = ! inMenu;
}
});
new AnimationTimer() {
#Override
public void handle(long now) {
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 1280, 720);
if (inMenu) {
gc.setFill(Color.BLUE);
gc.fillRect(300, 300, 200, 200);
} else {
gc.setFill(Color.RED);
gc.fillRect(100, 100, 100, 100);
}
}
}.start();
primaryStage.show();
}
}
If you want to optimize repaints only to when they are needed, simply introduce another flag indicating a repaint is necessary:
public class Gui extends Application {
private boolean inMenu;
private boolean repaintRequested = true ;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("GUI");
AutoScalingCanvas canvas = new AutoScalingCanvas(1280, 720);
Scene scene = new Scene(canvas);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
GraphicsContext gc = canvas.getGraphicsContext2D();
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.ESCAPE) {
inMenu = ! inMenu;
repaintRequested = true ;
}
});
new AnimationTimer() {
#Override
public void handle(long now) {
if (repaintRequested) {
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 1280, 720);
if (inMenu) {
gc.setFill(Color.BLUE);
gc.fillRect(300, 300, 200, 200);
} else {
gc.setFill(Color.RED);
gc.fillRect(100, 100, 100, 100);
}
repaintRequested = false ;
}
}
}.start();
primaryStage.show();
}
}
I didn't try any of this, so I can only nudge you in a general direction.
You could add another boolean variable esc_handled you set to true at the end of your handle method. Then you can add one more check to the method if the event has already been handled and if it has, you skip the handling step.
The following code achieves this:
add variable
boolean in_menu;
boolean esc_down;
boolean esc_handled;
check for esc_handled (inside handle) and set event to handled
if (esc_down && !esc_handled)
{
in_menu = !in_menu;
esc_handled = true;
}
on release esc set esc_handled to false
scene.setOnKeyReleased(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.ESCAPE)
{
esc_down = false;
esc_handled = false;
}
}
});
It looks like you're using the animation timer to do some kind of sampling of the state of the in or out and the press/release of the escape key. You don't need to do that at all.
You're trying to turn the key press/release events into a state, which makes sense, but you simply need to toggle that state in the event handler. Since the show/hide action is in response to an event, you can just call the draw routine directly from the event. So then the event will toggle the state and call the screen redraw:
public class Gui extends Application {
boolean in_menu;
GraphicsContext gc;
#Override
public void start(Stage primary_stage) throws Exception {
primary_stage.setTitle("GUI");
AutoScalingCanvas canvas = new AutoScalingCanvas(1280, 720);
primary_stage.setScene(new Scene(canvas));
gc = canvas.getGraphicsContext2D();
showOrHideMenu();
new Scene(canvas).setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE) {
in_menu = !in_menu;
showOrHideMenu();
}
});
primary_stage.show();
}
private void showOrHideMenu() {
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 1280, 720);
if (in_menu) {
gc.setFill(Color.BLUE);
gc.fillRect(300, 300, 200, 200);
} else {
gc.setFill(Color.RED);
gc.fillRect(100, 100, 100, 100);
}
}
}
Alternatively (and this is probably more "JavaFX"), you can make the In/Out Menu state observable, and then put a change listener on the state to do the repaint:
public class Gui extends Application {
BooleanProperty in_menu = new SimpleBooleanProperty(false);
GraphicsContext gc;
#Override
public void start(Stage primary_stage) throws Exception {
primary_stage.setTitle("GUI");
AutoScalingCanvas canvas = new AutoScalingCanvas(1280, 720);
Scene scene = new Scene(canvas);
primary_stage.setScene(scene);
gc = canvas.getGraphicsContext2D();
showOrHideMenu(false);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ESCAPE) {
in_menu.set(!in_menu.get());
}
});
in_menu.addListener(((observable, oldValue, newValue) -> showOrHideMenu(newValue)));
primary_stage.show();
}
private void showOrHideMenu(boolean inMenu) {
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, 1280, 720);
if (inMenu) {
gc.setFill(Color.BLUE);
gc.fillRect(300, 300, 200, 200);
} else {
gc.setFill(Color.RED);
gc.fillRect(100, 100, 100, 100);
}
}
}

How to make an pane stay over line connecting draggablenode in javafx

I am designing a UI of a graph structure with draggable nodes. In the graph I have a component called relation(it is a pane) which shows the link between two nodes.
I want relation to stay and move along with line at mid of line.
Current UI design is as shown below
And the expected one is like:
You need to refresh the position of the node when the line's end coordinates are modified. To avoid triggering the calculation multiple times per layout pass, I recommend doing this from the layoutChildren method of the parent, but you could also do this from a listener to the startX, endY, ... properties. This will lead to some unnecessary computations though.
As for calcualting the position of the node: The center of the node needs to align with the midpoint of the line, so you need to solve the following equation for markTopLeft:
markTopLeft + (markWidth, markHeight) / 2 = (lineStart + lineEnd) / 2
markTopLeft = (lineStart + lineEnd - (markWidth, markHeight)) / 2
Example
Pane allowing for custom layout calculations
public class PostProcessPane extends Pane {
private final Set<Node> modifiedChildren = new HashSet<>();
private final Set<Node> modifiedChildrenUnmodifiable = Collections.unmodifiableSet(modifiedChildren);
private final List<Consumer<Set<Node>>> postProcessors = new ArrayList<>();
public List<Consumer<Set<Node>>> getPostProcessors() {
return postProcessors;
}
private final ChangeListener listener = (o, oldValue, newValue) -> modifiedChildren.add((Node) ((ReadOnlyProperty) o).getBean());
private void initListener() {
getChildren().addListener((ListChangeListener.Change<? extends Node> c) -> {
while (c.next()) {
if (c.wasRemoved()) {
for (Node n : c.getRemoved()) {
n.boundsInParentProperty().removeListener(listener);
}
}
if (c.wasAdded()) {
for (Node n : c.getAddedSubList()) {
n.boundsInParentProperty().addListener(listener);
}
}
}
});
}
public PostProcessPane() {
initListener();
}
public PostProcessPane(Node... children) {
super(children);
initListener();
for (Node n : children) {
n.boundsInParentProperty().addListener(listener);
}
}
#Override
protected void layoutChildren() {
super.layoutChildren();
if (!modifiedChildren.isEmpty()) {
for (Consumer<Set<Node>> processor : postProcessors) {
processor.accept(modifiedChildrenUnmodifiable);
}
modifiedChildren.clear();
}
}
}
Usage
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle r1 = new Rectangle(200, 50, Color.BLUE);
Rectangle r2 = new Rectangle(200, 50, Color.RED);
Rectangle mark = new Rectangle(200, 50, Color.YELLOW);
Line line = new Line();
r1.setX(20);
r2.setX(380);
r2.setY(450);
PostProcessPane root = new PostProcessPane(line, r1, r2, mark);
root.getPostProcessors().add(changedNodes -> {
if (changedNodes.contains(r1) || changedNodes.contains(r2) || changedNodes.contains(mark)) {
Bounds bounds1 = r1.getBoundsInParent();
Bounds bounds2 = r2.getBoundsInParent();
// refresh line ends
line.setStartX(bounds1.getMinX() + bounds1.getWidth() / 2);
line.setStartY(bounds1.getMaxY());
line.setEndX(bounds2.getMinX() + bounds2.getWidth() / 2);
line.setEndY(bounds2.getMinY());
// recalculate mark position
mark.setX((line.getStartX() + line.getEndX() - mark.getWidth()) / 2);
mark.setY((line.getStartY() + line.getEndY() - mark.getHeight()) / 2);
}
});
// add some movement for the nodes
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(r1.xProperty(), r1.getX()),
new KeyValue(r1.yProperty(), r1.getY()),
new KeyValue(r2.xProperty(), r2.getX())),
new KeyFrame(Duration.seconds(1),
new KeyValue(r2.xProperty(), r1.getX())),
new KeyFrame(Duration.seconds(2),
new KeyValue(r1.xProperty(), r2.getX()),
new KeyValue(r1.yProperty(), r2.getY() / 2))
);
timeline.setAutoReverse(true);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

How do I run two processes separately from inside one EventHandler?

I am using JavaFx, and I have an EventHandler that runs when a button is clicked. The Handler is supposed to 1) put a progress bar on the screen, and AFTERWARDS make a new window. The window takes a little while to create, so the progress bar is there to let people know that the program did not just freeze.
Here is the code that contains the EventHandler
public HBox SelectionSection(){
final HBox hb = new HBox();
GridPane grid = new GridPane();
grid.setAlignment(Pos.BASELINE_LEFT);
hb.setStyle("-fx-background-color: linear-gradient(#CEF5FD 0%, #1864E2 100%);");
hb.setPrefSize(350, HEIGHT);
Button done = new Button("Create Data");
final CheckBox ch1 = new CheckBox("Class Stats");
ch1.setSelected(true);
final CheckBox ch2 = new CheckBox("Class Chart");
ch2.setSelected(true);
final CheckBox ch5 = new CheckBox("Interactive Stats");
final CheckBox ch3 = new CheckBox("Objectives Summary");
final CheckBox ch4 = new CheckBox("part 4");
final JFXDragAndDrop JFX = this;
CheckBox sdf = new CheckBox();
done.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent arg0) {
if(dir!=null){
grid.getChildren().add(p1);
new Excel(JFX, dir, ch1.isSelected(), ch2.isSelected(), ch3.isSelected(), ch4.isSelected(), ch5.isSelected());
}
else{
Platform.runLater(new Runnable(){
public void run(){
JFX.createError(2);
}
});
}
}
});
GridPane.setConstraints(ch1, 7, 7, 1, 1,HPos.LEFT, VPos.CENTER);
GridPane.setConstraints(ch2, 7, 8, 1, 1,HPos.LEFT, VPos.CENTER);
GridPane.setConstraints(ch5, 7, 9, 1, 1, HPos.LEFT, VPos.CENTER);
GridPane.setConstraints(ch3, 7, 10, 1, 1,HPos.LEFT, VPos.CENTER);
GridPane.setConstraints(done, 7, 16, 1, 1,HPos.CENTER, VPos.CENTER);
GridPane.setConstraints(p1,7,15,1,1,HPos.CENTER,VPos.CENTER);
grid.getChildren().addAll(ch1,ch2,ch3,ch5,done);
hb.getChildren().add(grid);
return hb;
}
Here is a picture of what it should look like. The loading bar should pop up as soon as you click the 'done' button. Instead, it finishes running Excel() before the loading bar pops up.
try this it may help you...!!
initialize some variables
#FXML
private ProgressBar bar;
int max = 1000000;
int i=0;
now perform event on button
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t)
{
loadAppConfigurationFile();
}
});
function definition
private void loadAppConfigurationFile() {
Task task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
for ( i=1; i<=max; i=i+10) {
if (isCancelled()) {
break;
}
// System.out.println("value of time - "+now.getMinutes());
//if(i==1)
//{
// Thread.sleep(pk);
//}
updateProgress(i, max);
//System.out.println("1");
DecimalFormat df = new DecimalFormat("#.00");
System.out.println("progress - "+df.format(bar.getProgress()));
Double abc = Double.parseDouble(df.format(bar.getProgress()));
if(abc==1.00)
{
System.out.println("at here");
Parent root;
try
{
URL url = getClass().getResource("Check.fxml");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(url);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
root = (Parent)fxmlLoader.load(url.openStream());
Stage stage = new Stage();
//sstage.initStyle(StageStyle.UNDECORATED);
// stage.setFullScreen(true);
stage.setTitle("Welcome User");
stage.setScene(new Scene(root, 631, 437));
stage.show();
}
catch(IOException ea)
{
System.out.println(ea.toString());
ea.printStackTrace();
}
}
}
return null;
}
};
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
}

Change between two FXML files in JavaFX2.2 on change Listener

i am new in JavaFX programming. I have an Application, with a simple login page as described in the example here, and i add a StringProperty to the actiontarget element. So when the text changes inside the actiontarget i want a new FXML file with a webview inside, to load from the FXMLLoader and be dipslayed on the screen. Below is the exception i get. I can load any other fxml file, without a webview inside it, without a problem. Thanks in advance.Code samples below
The exception :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
at com.sun.webpane.sg.prism.InvokerImpl.checkEventThread(InvokerImpl.java:33)
at com.sun.webpane.platform.WebPage.<init>(WebPage.java:189)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:57)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:51)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:704)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:691)
at javafx.scene.web.WebView.<init>(WebView.java:245)
at student.WebBrowser.<init>(WebBrowser.java:31)
at Login.Login.replaceSceneContent(Login.java:171)
at Login.Login.access$000(Login.java:66)
at Login.Login$2.changed(Login.java:143)
at Login.Login$2.changed(Login.java:137)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at Login.Client.run(Client.java:66)
First my listener:
// Add change listener
sp.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
if(t1.equalsIgnoreCase("user authenticated successfully")){
try {
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
System.out.println("everything ok");
} catch (Exception ex) {
System.out.println("something went wrong");
ex.printStackTrace();
}
}
}
});
Second my method : replaceSceneContent(String fxml, Stage stage)
private Parent replaceSceneContent(String fxml, Stage stage) throws Exception {
Parent page = (Parent) FXMLLoader.load(getClass().getResource("/FXML_Files/"+fxml), null, new JavaFXBuilderFactory());
Scene scene = stage.getScene();
if (scene == null) {
scene = new Scene(page, 700, 450);
stage.setScene(scene);
} else {
stage.getScene().setRoot(page);
}
if(fxml.equalsIgnoreCase("Student.fxml")){
Pane spane = (Pane) page.lookup("#pane");
WebBrowser wb = new WebBrowser();
spane.getChildren().add(wb);
}
return page;
}
And my WebBrowser class similar to the example in NetBeans7.2:
public class WebBrowser extends Pane {
public WebBrowser() {
WebView view;
final WebEngine eng;
view = new WebView();
view.setMinSize(10, 10);
view.setPrefSize(500, 400);
eng = view.getEngine();
eng.load("http://www.oracle.com/us/index.html");
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
final TextField locationField = new TextField("http://www.oracle.com/us/index.html");
locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Go");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.load(locationField.getText().startsWith("http://") ? locationField.getText() :
"http://" + locationField.getText());
}
};
goButton.setOnAction(goAction);
locationField.setOnAction(goAction);
eng.locationProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
locationField.setText(newValue);
}
});
GridPane grid = new GridPane();
ButtonsEvents be = new ButtonsEvents();
TilePane tp = be;
tp.setAlignment(Pos.CENTER);
grid.setVgap(5);
grid.setHgap(5);
GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES);
GridPane.setConstraints(goButton,1,0);
GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(tp, 0, 2, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.NEVER);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.ALWAYS, HPos.CENTER, true)
);
grid.getChildren().addAll(locationField, goButton,view, tp);
getChildren().add(grid);
}
#Override
protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
As stated in the exception, changes in the JavaFX scene graph can only be made in the JavaFX application thread. To make sure your code is run in this thread, try the following:
Platform.runLater(new Runnable() {
#Override
public void run() {
// This method is invoked on JavaFX thread
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
}
});
The javafx manager will run this code at some point in the future on the correct thread.
More information:
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

Resources