I'm new to JavaFX. I try to program a simple GUI but I face those problem whom might be related.
I set files with a File Chooser and want to do pretty basic operations:
save the last folder used
write the name of the selected file in the VBox
Here's my code (which compiles):
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static Stage primaryStageS;
public static Scene mainScene;
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene((new Test(primaryStage).getScene()));
primaryStageS = primaryStage;
primaryStage.setTitle("Parcel Manager Main Page");
primaryStage.initStyle(StageStyle.DECORATED);
VBox main = new VBox(new Label("Test program"));
mainScene = new Scene(main, 800, 600);
primaryStage.setScene((new Test(primaryStage)).getScene());
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public class Object1 {
String name;
public Object1(File f) throws IOException {
name = f.getName();
}
public String getName() {
return name;
}
}
public class Test {
Object1 collec;
String collecName;
File lastFolder;
Pane rootGroup;
public Test(Stage stage) {
setButtons(stage);
}
public void setButtons(Stage stageGoal) {
VBox vbox = new VBox();
Button b = getButton(stageGoal);
vbox.getChildren().addAll(b, new Label(getCollecName() == null ? "no name" : collecName));
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(vbox, 0, 0);
inputGridPane.getChildren().addAll(vbox);
rootGroup = new VBox(12);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12, 12, 12, 12));
}
public Button getButton(Stage stage) {
FileChooser fileChooserParcel = new FileChooser();
fileChooserParcel.setInitialDirectory(getLastFolder());
Button button = new Button("Select a File");
button.setOnAction(e -> {
File f = fileChooserParcel.showOpenDialog(stage);
if (f != null) {
try {
collec = new Object1(f);
} catch (IOException e1) {
e1.printStackTrace();
}
setLastFolder(f.getParentFile());
setCollecName(collec);
setButtons(stage); // tried to reload every buttons - doesn't work
stage.setWidth(stage.getWidth() + 0.0001); // found this dirty hack but doesn't work
}
});
return button;
}
public void setCollecName(Object1 o1) {
collecName = o1.getName();
}
public String getCollecName() {
return collecName;
}
public File getLastFolder() {
return lastFolder;
}
public void setLastFolder(File folder) {
System.out.println("set last folder: " + folder);
lastFolder = folder;
}
private Scene getScene() {
return new Scene(rootGroup, 800, 600);
}
}
}
I cannot refresh the Nodes, either to set a current Initial Directory or display the collecName on the VBox. I tried to regenerate them with reloading of objects or resizing the window, but nothing works. When I print the variables on console, I see that they changes. But haven't found any refresh method for any of my objects.
I bet it's a design program issue, but I have been moving things around for the last week and doesn't know how to fix this.
Thanks !
You are only setting the initial directory once. I guess you want to set it every time you click the button. So move that line of code to inside the handler.
Compare the below getButton() method with yours.
public Button getButton(Stage stage) {
FileChooser fileChooserParcel = new FileChooser();
Button button = new Button("Select a File");
button.setOnAction(e -> {
fileChooserParcel.setInitialDirectory(getLastFolder()); // CHANGE HERE.
File f = fileChooserParcel.showOpenDialog(stage);
if (f != null) {
try {
collec = new Object1(f);
} catch (IOException e1) {
e1.printStackTrace();
}
setLastFolder(f.getParentFile());
setCollecName(collec);
setButtons(stage); // tried to reload every buttons - doesn't work
stage.setWidth(stage.getWidth() + 0.0001); // found this dirty hack but doesn't work
}
});
return button;
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 12 months ago.
Improve this question
Here I have written the following code. I can't run the program, and the error mentioned below keeps appearing. I tried many probable solutions but in vain.
import java.beans.EventHandler;
import java.io.File;
import javafx.application.Application;
import javafx.collections.*;
import javafx.event.ActionEvent;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class FileChooser_1 extends Application {
// launch the application
public void start(Stage stage) {
try {
// title
stage.setTitle("Filechooser");
//File chooser create
FileChooser file_chooser = new FileChooser();
// define Label
Label lab = new Label("select file");
// Button new
Button b = new Button("open dialog");
// create Event Handler
EventHandler<ActionEvent> eve
= new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
// get file
File file = file_chooser.showOpenDialog(stage);
if (file != null) {
lab.setText(file.getAbsolutePath()
+ " selected");
}
}
};
b.setOnAction(event);
// create Button
Button b1 = new Button("save");
// Event Handler
EventHandler<ActionEvent> eve1
= new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
// get file
File file = file_chooser.showSaveDialog(stage);
if (file != null) {
lab.setText(file.getAbsolutePath()
+ " selected");
}
}
};
b1.setOnAction(eve1);
// VBox
VBox vbox = new VBox(30, label, button, button1);
// set Alignment
vbox.setAlignment(Pos.CENTER);
// create scene
Scene scene = new Scene(vbox, 800, 500);
// scene
stage.setScene(scene);
stage.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
// Main Method
public static void main(String args[]) {
launch(args);
}
}
I am getting the following error:
Error: Unable to initialize main class FileChooser_1
Caused by: java.lang.NoClassDefFoundError: Stage
It will be really nice if you can help me with this.
With some attention to detail, your code works. In particular, especially when just starting out,
Use Java naming conventions.
Use meaningful names; for example, instead of Button b, try Button openButton.
When using detailed comments, keep them up to date; note how meaningful names make some comments superfluous.
Use constants for consistency.
As #jewelsea notes, your program imports java.beans.EventHandler; it should import javafx.event.EventHandler.
As #jewelsea notes, "Only import classes you use."
Let the layout do the work.
I can't explain the error in your question; I see errors related to the incorrect import for EventHandler. If you're using an IDE, it may be reporting errors from a different compilation unit. When in doubt, do a clean build, move the code to a new file, or move to a different development environment, e.g. the command line. As a concrete example, this simple VersionCheck illustrates both a minimal ant script, invoked as ant run, and a simple shell script, invoked as .run.sh:
#!/bin/sh
JFX="--module-path /Users/Shared/javafx-sdk-17.0.1/lib --add-modules ALL-MODULE-PATH"
javac $JFX *.java && java $JFX VersionCheck
import javafx.event.EventHandler;
import java.io.File;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class FileChooser1 extends Application {
private static final int PADDING = 32;
#Override
public void start(Stage stage) {
// title
stage.setTitle("FileChooser");
//File chooser create
FileChooser fileChooser = new FileChooser();
// define Label
Label label = new Label("Select a file to open or save:");
// open Button
Button openButton = new Button("Open");
// open Event Handler
EventHandler<ActionEvent> openHandler = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// get file name
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
label.setText(file.getName() + " selected");
}
}
};
openButton.setOnAction(openHandler);
// create save button
Button saveButton = new Button("Save");
// save Event Handler
EventHandler<ActionEvent> saveHandler = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// save file
File file = fileChooser.showSaveDialog(stage);
if (file != null) {
label.setText(file.getName() + " selected");
}
}
};
saveButton.setOnAction(saveHandler);
// VBox
VBox vBox = new VBox(PADDING, label, openButton, saveButton);
// set Alignment
vBox.setAlignment(Pos.CENTER);
vBox.setPadding(new Insets(PADDING));
// create scene
Scene scene = new Scene(vBox);
// scene
stage.setScene(scene);
stage.show();
}
public static void main(String args[]) {
launch(args);
}
}
import java.io.File;
import java.util.Scanner;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class ShakespeareUI extends Application {
public String Quotes(String fileName) throws Exception{
File file = new File (fileName);
String line ="";
Scanner sc = new Scanner(file);
while(sc.hasNextLine()){
line+= sc.nextLine();
}
return line;
}
#Override // Override the start method in the Application class
public void start(Stage primaryStage)throws Exception {
BorderPane pane = new BorderPane();
// Top of Pane with Text
Pane paneForText = new Pane();
paneForText.setPadding(new Insets(0,0,5,0));
Text shText = new Text(25, 50,"Shakespeare Quotes");
shText.setFont(Font.font("Arial", 28));
paneForText.getChildren().add(shText);
pane.setTop(paneForText);
// Center of Border Pane with TextArea
TextArea taQuote = new TextArea();
taQuote.setPrefColumnCount(30);
taQuote.setPrefRowCount(5);
pane.setCenter(taQuote);
// Bottom of Pane with Buttons
HBox paneForButtons = new HBox(20);
Button btLear = new Button("King Lear");
Button btMacBeth = new Button("MacBeth");
Button btHamlet = new Button("Hamlet");
Button btRichard = new Button("Richard III");
Button btOthello = new Button("Othello");
pane.setBottom(paneForButtons);
paneForButtons.getChildren().addAll(btLear, btMacBeth, btHamlet, btRichard, btOthello );
paneForButtons.setAlignment(Pos.CENTER);
paneForButtons.setStyle("-fx-border-color: green");
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 455, 150);
primaryStage.setTitle("Deep Patel"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
////// Your code here that handles events when buttons are clicked
btLear.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
shText.setText(btLear.getText());
taQuote.setText(Quotes("lear.txt"));
}
});
btMacBeth.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
shText.setText(btMacBeth.getText());
}
});
btHamlet.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
shText.setText(btHamlet.getText());
}
});
btRichard.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
shText.setText(btRichard.getText());
}
});
btOthello.setOnAction(new EventHandler<ActionEvent>(){
#Override public void handle(ActionEvent e){
shText.setText(btOthello.getText());
}
});
}
/////////////////////////////////////////////////////
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
Hi, I am trying to run this code but there is error about exception. I have no idea what to do. Thanks in advance for any help. I tried to put exception in the override method, in the general method and then I just made new method and put exception there but still the same here
The error that I am getting is this:
ShakespeareUI.java:79: error: unreported Exception; must be caught or
declared to be thrown
The EventHandler method does not allow you to add a throws clause for non-runtime exceptions. Therefore you need to use try-catch to handle those exceptions even if you just handle them by rethrowing the exception as RuntimeException (which is not a good way to handle failed execution of code in most cases):
btLear.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
shText.setText(btLear.getText());
try {
taQuote.setText(Quotes("lear.txt"));
} catch (Exception ex) {
// TODO: handle exception in a differnt way???
throw new RuntimeException(ex);
}
}
});
Note that you should close any classes accessing files as soon as you're done with the reader/writer. (Scanner in this case):
public String Quotes(String fileName) throws Exception{
File file = new File (fileName);
StringBuilder builder = new StringBuilder(); // builder more efficient for concatenating multiple strings
try(Scanner sc = new Scanner(file)) { // try-with-resources automatically calls close on scanner
while(sc.hasNextLine()) {
builder.append(sc.nextLine());
}
return builder.toString();
}
}
I'm trying to display text in TextArea with delay in between each sentence, like you're having a conversation.
I tried using the sleep function but this doesn't work since the text only gets displayed when all methods stopped running.
What would be an efficiƫnt way to do this:
(Pseudo code)
textArea.appendText("Goodday sir, how are you doing?");
(0.5 second delay);
textArea.appendText("I'm fine thanks");
(1 second delay);
textArea.appendText("What can I do for you?");
getPlayerInput();
textArea.appendText("Sure, I'll take care of it.");
To clarify what I'm trying to do:
Display text in textArea with delays inbetween and be able to run functions in between.
As a variation on the timeline in the other answer, you can create a different KeyFrame for every message you want to display. This avoids the scenario of having "nested timelines", which I think would become unmanageable if you had more than two or three messages to display one after the other.
Here's a SSCCE using this idea:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Conversation extends Application {
private TextArea console ;
private TextField input ;
private BooleanProperty readyForInput ;
private Timeline createTimeline(String[] messages) {
Timeline timeline = new Timeline();
Duration delayBetweenMessages = Duration.seconds(1);
Duration frame = delayBetweenMessages ;
for (String msg : messages) {
timeline.getKeyFrames().add(new KeyFrame(frame, e -> console.appendText(msg+"\n")));
frame = frame.add(delayBetweenMessages);
}
timeline.statusProperty().addListener((obs, oldStatus, newStatus) -> {
readyForInput.set(newStatus != Animation.Status.RUNNING);
if (newStatus != Animation.Status.RUNNING) {
input.requestFocus();
}
});
return timeline ;
}
#Override
public void start(Stage primaryStage) {
readyForInput = new SimpleBooleanProperty(false);
console = new TextArea();
console.setEditable(false);
input = new TextField();
input.disableProperty().bind(readyForInput.not());
input.setOnAction(e -> {
String inputText = input.getText();
console.appendText("> "+inputText+"\n");
input.clear();
createTimeline(getMessages(inputText)).play();
});
BorderPane root = new BorderPane(console, input, null, null, null) ;
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
createTimeline(getMessages(null)).play();
}
private String[] getMessages(String input) {
if (input == null || input.isEmpty()) {
return new String[] {
"Goodday sir, how are you doing?",
"I'm fine thanks",
"What can I do for you?"
};
} else {
// AI logic here...
return new String[] { "Sure, I'll take care of it." };
}
}
public static void main(String[] args) {
launch(args);
}
}
you can use a Timeline's onFinished to make delayed actions in JavaFX
try the following code
package application;
import java.util.ArrayList;
import java.util.Iterator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
Timeline delay = new Timeline();
TextArea textArea = new TextArea();
boolean waitForInput = false;
Msg current;
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
root.getChildren().add(textArea);
Scene scene = new Scene(root, 500, 500);
ArrayList<Msg> msgs = new ArrayList<Msg>();
msgs.add(new Msg("Goodday sir, how are you doing?\n", Duration.seconds(1), false));
msgs.add(new Msg("i'm fine thanks!\n", Duration.seconds(2), false));
msgs.add(new Msg("What can I do for you?\n", Duration.seconds(0.1), true));
msgs.add(new Msg("Sure, I'll take care of it.\n", Duration.seconds(1), false));
msgs.add(new Msg("....", Duration.seconds(0.5), false));
msgs.add(new Msg("are you sure it's the only thing you need?\n", Duration.seconds(0.1), true));
msgs.add(new Msg("alright bye", Duration.seconds(0), true));
Iterator<Msg> it = msgs.iterator();
delay.getKeyFrames().setAll(new KeyFrame(Duration.seconds(0)));
delay.setOnFinished(e -> {
if (it.hasNext()) {
current = it.next();
delay.getKeyFrames().setAll(new KeyFrame(current.getDuration()));
delay.playFromStart();
textArea.appendText(current.getContent());
if (current.requiresInput()) {
waitForInput = true;
delay.pause();
}
}
});
delay.playFromStart();
primaryStage.setScene(scene);
primaryStage.show();
scene.addEventFilter(KeyEvent.KEY_PRESSED, e ->
{
if (waitForInput && e.getCode().equals(KeyCode.ENTER)) {
delay.play();
waitForInput = false;
}
});
scene.addEventFilter(KeyEvent.KEY_TYPED, e -> {
if (!waitForInput) {
e.consume();
}
});
}
public static void main(String[] args) {
launch(args);
}
class Msg {
private boolean requireInput;
private String content;
private Duration duration;
public Msg(String c, Duration d, boolean b) {
content = c;
duration = d;
requireInput = b;
}
public String getContent() {
return content;
}
public Duration getDuration() {
return duration;
}
public boolean requiresInput() {
return requireInput;
}
}
}
I'm loading a website on a JavaFX WebView and after a while taking a screenshot with something like:
WritableImage image = webView.snapshot(null, null);
If I'm looking at that WebView that works fine, but if it's hidden by being in a tab that is not in the foreground (I'm not sure about other cases of hiding it), then, the screenshot is of the appropriate site, but entirely blank.
How can I force the WebView to render even if not visible?
During this time, webView.isVisible() is true.
I found there's a method in WebView called isTreeReallyVisible() and currently it contains:
private boolean isTreeReallyVisible() {
if (getScene() == null) {
return false;
}
final Window window = getScene().getWindow();
if (window == null) {
return false;
}
boolean iconified = (window instanceof Stage) ? ((Stage)window).isIconified() : false;
return impl_isTreeVisible()
&& window.isShowing()
&& window.getWidth() > 0
&& window.getHeight() > 0
&& !iconified;
}
When the WebView is hidden by being in a non-foreground tab, impl_isTreeVisible() is false (all other factors in the return statement are true). That method is on Node and looks like this:
/**
* #treatAsPrivate implementation detail
* #deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
#Deprecated
public final boolean impl_isTreeVisible() {
return impl_treeVisibleProperty().get();
}
/**
* #treatAsPrivate implementation detail
* #deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
#Deprecated
protected final BooleanExpression impl_treeVisibleProperty() {
if (treeVisibleRO == null) {
treeVisibleRO = new TreeVisiblePropertyReadOnly();
}
return treeVisibleRO;
}
I could have overriden impl_treeVisibleProperty() to provide my own implementation, but WebView is final, so, I cannot inherit from it.
Another completely different situation to being minimized (iconified) or on a hidden tab is to have the stage completely hidden (as in, running in the tray bar). When in that mode, even if I can get rendering to happen, the WebView doesn't resize. I call webView.resize() and then take a screenshot and the screenshot is of the appropriate size but the actual rendered page is of whatever size the WebView was before.
Debugging this sizing behavior in shown and hidden stages, I found that eventually we get to Node.addToSceneDirtyList() that contains:
private void addToSceneDirtyList() {
Scene s = getScene();
if (s != null) {
s.addToDirtyList(this);
if (getSubScene() != null) {
getSubScene().setDirty(this);
}
}
}
When in hidden mode, getScene() returns null, unlike what happens when it's being show. That means that s.addToDirtyList(this) is never called. I'm not sure if this is the reason why it doesn't get properly resized.
There's a bug about this, a very old one, here: https://bugs.openjdk.java.net/browse/JDK-8087569 but I don't think that's the whole issue.
I'm doing this with Java 1.8.0_151. I tried 9.0.1 to see if it would behave differently as it is my understanding that WebKit was upgraded, but no, it's the same.
Reproducing Pablo's problem here: https://github.com/johanwitters/stackoverflow-javafx-webview.
Pablo suggested to override WebView and adjust some methods. That doesn't work given it's a final class and a private member. As an alternative, I've used javassist to rename a method and replace the code with the code that I want it to execute. I've "replaced" the contents of method handleStagePulse, as shown below.
public class WebViewChanges {
// public static String MY_WEBVIEW_CLASSNAME = WebView.class.getName();
public WebView newWebView() {
createSubclass();
return new WebView();
}
// https://www.ibm.com/developerworks/library/j-dyn0916/index.html
boolean created = false;
private void createSubclass() {
if (created) return;
created = true;
try
{
String methodName = "handleStagePulse";
// get the super class
CtClass webViewClass = ClassPool.getDefault().get("javafx.scene.web.WebView");
// get the method you want to override
CtMethod handleStagePulseMethod = webViewClass.getDeclaredMethod(methodName);
// Rename the previous handleStagePulse method
String newName = methodName+"Old";
handleStagePulseMethod.setName(newName);
// mnew.setBody(body.toString());
CtMethod newMethod = CtNewMethod.copy(handleStagePulseMethod, methodName, webViewClass, null);
String body = "{" +
" " + Scene.class.getName() + ".impl_setAllowPGAccess(true);\n" +
" " + "final " + NGWebView.class.getName() + " peer = impl_getPeer();\n" +
" " + "peer.update(); // creates new render queues\n" +
// " " + "if (page.isRepaintPending()) {\n" +
" " + " impl_markDirty(" + DirtyBits.class.getName() + ".WEBVIEW_VIEW);\n" +
// " " + "}\n" +
" " + Scene.class.getName() + ".impl_setAllowPGAccess(false);\n" +
"}\n";
System.out.println(body);
newMethod.setBody(body);
webViewClass.addMethod(newMethod);
CtMethod isTreeReallyVisibleMethod = webViewClass.getDeclaredMethod("isTreeReallyVisible");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
This snippet is called from the WebViewSample which opens 2 tabs. One with a "snapshot" button, another with the WebView. As Pablo pointed out, the tab with the WebView needs to be the second tab to be able to reproduce.
package com.johanw.stackoverflow;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
public class WebViewSample extends Application {
private Scene scene;
private TheBrowser theBrowser;
private void setLabel(Label label) {
label.setText("" + theBrowser.browser.isVisible());
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tabs");
Group root = new Group();
Scene scene = new Scene(root, 400, 250, Color.WHITE);
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
theBrowser = new TheBrowser();
{
Tab tab = new Tab();
tab.setText("Other tab");
HBox hbox0 = new HBox();
{
Button button = new Button("Screenshot");
button.addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
WritableImage image = theBrowser.getBrowser().snapshot(null, null);
File file = new File("test.png");
RenderedImage renderedImage = SwingFXUtils.fromFXImage(image, null);
try {
ImageIO.write(
renderedImage,
"png",
file);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
hbox0.getChildren().add(button);
hbox0.setAlignment(Pos.CENTER);
}
HBox hbox1 = new HBox();
Label visibleLabel = new Label("");
{
hbox1.getChildren().add(new Label("webView.isVisible() = "));
hbox1.getChildren().add(visibleLabel);
hbox1.setAlignment(Pos.CENTER);
setLabel(visibleLabel);
}
HBox hbox2 = new HBox();
{
Button button = new Button("Refresh");
button.addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
setLabel(visibleLabel);
}
});
hbox2.getChildren().add(button);
hbox2.setAlignment(Pos.CENTER);
}
VBox vbox = new VBox();
vbox.getChildren().addAll(hbox0);
vbox.getChildren().addAll(hbox1);
vbox.getChildren().addAll(hbox2);
tab.setContent(vbox);
tabPane.getTabs().add(tab);
}
{
Tab tab = new Tab();
tab.setText("Browser tab");
HBox hbox = new HBox();
hbox.getChildren().add(theBrowser);
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);
tabPane.getTabs().add(tab);
}
// bind to take available space
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setCenter(tabPane);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args){
launch(args);
}
}
class TheBrowser extends Region {
final WebView browser;
final WebEngine webEngine;
public TheBrowser() {
browser = new WebViewChanges().newWebView();
webEngine = browser.getEngine();
getStyleClass().add("browser");
webEngine.load("http://www.google.com");
getChildren().add(browser);
}
private Node createSpacer() {
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
return spacer;
}
#Override protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
}
#Override protected double computePrefWidth(double height) {
return 750;
}
#Override protected double computePrefHeight(double width) {
return 500;
}
public WebView getBrowser() {
return browser;
}
public WebEngine getWebEngine() {
return webEngine;
}
}
I've not succeeded in fixing Pablo's problem, but hopefully the suggestion to use javassist might help.
I'm sure: To be continued...
I would consider reloading page at the suitable moment using this command:
webView.getEngine().reload();
Also try to change parameter SnapshotParameters in method snapShot
If it would not work then I would consider storing Image in memory when WebView is being rendered on screen.
If you go by logical implementation of snapshot, only things that are visible on screen are taken as a snapshot.
For taking snapshot of the web view, you can either make it automatically visible by clicking on the tab inside which the view is rendered just before taking the snapshot. Or you can manually click on the tab and take the screenshot.
I think no API allows to take snapshot of hidden part as logically it will voilate the concept of hidden things.
Code for taking snapshot is already available with you. You can click, or Load the tab like:
You can add a selectionChangedListener or you can do the load just before the snapshot.
addItemTab.setOnSelectionChanged(event -> loadTabBasedFXML(addItemTab, "/view/AddItem.fxml"));
private void loadTabBasedFXML(Tab tab, String fxmlPath) {
try {
AnchorPane anchorPane = FXMLLoader.load(this.getClass().getResource(fxmlPath));
tab.setContent(anchorPane);
} catch (IOException e) {
}
}
I created a JavaFX application that is nearly completed. I exported it as a runnable JAR. When opening this JAR I only see a blank window. i followed some other answers from stackoverflow but I did not get it working.
It works only in the Eclipse IDE!
My screens controller:
package gui;
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
public class ScreensController extends StackPane {
private HashMap<String, Node> screens = new HashMap<>();
public static String sourcePath = "";
private CoreService coreService;
public ScreensController(){
super();
}
public void addScreen(String name, Node screen) {
screens.put(name, screen);
}
public boolean loadScreen(String name, String resource) {
System.out.println("ID: "+name);
System.out.println("Resource: "+resource);
String file = System.getProperty("user.dir")+"\\bin\\"+resource;
// System.out.println(file);
try {
FXMLLoader myLoader = new FXMLLoader();
File f = new File(file);
URL url = f.toURI().toURL();
myLoader.setLocation(url);
// System.out.println("Location: "+myLoader.getLocation());
Parent loadScreen = (Parent) myLoader.load();
ControlledScreen myScreenControler =
((ControlledScreen) myLoader.getController());
myScreenControler.setScreenParent(this);
addScreen(name, loadScreen);
System.out.println("Anzahl Screens: "+screens.size());
return true;
}catch(Exception e) {
System.out.println("Fehler beim Laden von "+file);
System.out.println(e.getMessage());
return false;
}
}
public boolean setScreen(final String name) {
#SuppressWarnings("unused")
Node screenToRemove;
if(screens.get(name) != null){ //screen loaded
if(!getChildren().isEmpty()){ //if there is more than one screen
getChildren().add(0, screens.get(name)); //add the screen
screenToRemove = getChildren().get(1);
getChildren().remove(1); //remove the displayed screen
}else{
getChildren().add(screens.get(name)); //no one else been displayed, then just show
}
return true;
}else {
System.out.println("Screen hasn't been loaded!!! \n");
return false;
}
}
public boolean unloadScreen(String name) {
if(screens.remove(name) == null) {
System.out.println("Screen didn't exist!!!");
return false;
} else {
return true;
}
}
public void print() {
Set<String> keys = screens.keySet();
Iterator<String> it = keys.iterator();
while (it.hasNext()){
System.out.println("Key: "+it.next());
}
}
public CoreService getCoreService(){
return this.coreService;
}
public void setCoreService(CoreService coreService){
this.coreService = coreService;
}
}
And here I use it:
package gui;
import java.util.Optional;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class MainMenu extends Application {
private Stage mainStage;
private static CoreService coreService;
public static final String MAIN_SCREEN = "main";
public static final String MAIN_SCREEN_FXML = "gui\\MainMenu.fxml";
#Override
public void start(Stage primaryStage) {
this.mainStage = primaryStage;
ScreensController mainContainer = new ScreensController();
mainContainer.loadScreen(MainMenu.MAIN_SCREEN, MainMenu.MAIN_SCREEN_FXML);
mainContainer.setCoreService(MainMenu.coreService);
mainContainer.setScreen(MainMenu.MAIN_SCREEN);
Group root = new Group();
root.getChildren().addAll(mainContainer);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(confirmCloseEventHandler);
primaryStage.show();
}
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
//Source: http://stackoverflow.com/questions/29710492/javafx-internal-close-request
Alert closeConfirmation = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to exit?"
);
Button exitButton = (Button) closeConfirmation.getDialogPane().lookupButton(
ButtonType.OK
);
exitButton.setText("Exit");
closeConfirmation.setHeaderText("Confirm Exit");
closeConfirmation.initModality(Modality.APPLICATION_MODAL);
closeConfirmation.initOwner(mainStage);
closeConfirmation.setX(mainStage.getX() + 150);
closeConfirmation.setY(mainStage.getY() - 300 + mainStage.getHeight());
Optional<ButtonType> closeResponse = closeConfirmation.showAndWait();
if (!ButtonType.OK.equals(closeResponse.get())) {
event.consume();
}
};
public static void main(String[] args, CoreService aService) {
// Weitergeben des CoreServices
coreService = aService;
launch(args);
}
}
I do not see where the error is.
When I start the program from command line it says that the MainMenu.fxml file could not been found.
In my application it is in the package gui. -> gui/MainMenu.fxml
Would be nice if someone find my error!
What the error message tells you, that the FXML file cannot be located.
You could try to:
Change this ...
public static final String MAIN_SCREEN_FXML = "gui\\MainMenu.fxml";
... to ...
public static final String MAIN_SCREEN_FXML = "/gui/MainMenu.fxml";
And to change this ...
FXMLLoader myLoader = new FXMLLoader();
File f = new File(file);
URL url = f.toURI().toURL();
myLoader.setLocation(url);
... to (and you don't need the variables file and f)...
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Some references:
I had an answer here on how to use getResource.
Also you can check the documentation of getResource()
And you can check this question about loading resources from a JAR