Hi i'm using JavaFx WebView to create Screenshot of HTML pages and it works fine but i wanted to know is it possible to do this without launching the application in Graphical Windows!! I mean aren't there any more lightweight method to get the screenshot then this:
public class WebViewSample extends Application {
private Scene scene;
#Override
public void start(Stage stage) {
// create scene
scene = new Scene(new Browser(snapshot), 750, 500, Color.web("#666970"));
stage.setScene(scene);
// show stage
stage.show();
}
WritableImage snapshot;
public static void main(String[] args) {
launch(args);
System.err.println("launched!");
}
}
class Browser extends Region {
final ImageView selectedImage = new ImageView();
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
private final WritableImage snapshotImage;
public Browser(WritableImage snapshot) {
this.snapshotImage= snapshot;
// process page loading
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> ov,
State oldState, State newState) {
if (newState == State.SUCCEEDED) {
WritableImage newSnapshot = browser.snapshot(null, snapshotImage);
File file = new File("test2.png");
RenderedImage renderedImage = SwingFXUtils.fromFXImage(newSnapshot, null);
try {
ImageIO.write(renderedImage, "png", file);
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
}
}
);
// load the home page
webEngine.load("http://localhost/");
//add components
getChildren().add(browser);
}
}
For JavaFX 2.2 and below, there's not functionality of the kind.
Headless JavaFX applications are currently not possible, and a main JavaFX thread is mandatory.
The best you can do is read upon several workarounds for achieving this.
Related StackOverflow questions:
JavaFX for server-side image generation
Generating image at server side using Java FX
JavaFx in headless mode
How to test JavaFX 2 in a headless environment?
Related
I'm creating an JavaFX application with Scene Builder. I added a video at the beginning. So I wanna play video before my application start in fullscreen mode.
The Problem is when it is stopped I see only black screeen and nothing happened, I guess it is because video is fullscreen and it is not automatically closed.
I also have a bug before the video starts, some blink of my main window .I guess it is because video is placed in the controller that begins after my application starts.
How to close video or remove it after finish?
How to place video in main class?
Main Class
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
FXMLLoader loader = new
FXMLLoader(getClass().getResource("resources/fxml/card.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("src/card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER,
KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
Controller cont = Context.getInstance().getController();
if (!primaryStage.isMaximized()) {
cont.getBtnFont().setPrefWidth(20);
cont.getBtnPalette().setPrefWidth(20);
cont.getBtnQuestCards().setPrefWidth(20);
cont.getBtnNonQuestCards().setPrefWidth(20);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 80.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 70.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 300.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 200.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 80.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 70.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 300.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 200.0);
cont.getRectRandom().setWidth(1060);
cont.getRectRandom().setHeight(410);
cont.getRectRandomBack().setWidth(1060);
cont.getRectRandomBack().setHeight(410);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(200);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(200);
} else if (primaryStage.isMaximized()){
cont.getBtnFont().setPrefWidth(50);
cont.getBtnPalette().setPrefWidth(50);
cont.getBtnQuestCards().setPrefWidth(50);
cont.getBtnNonQuestCards().setPrefWidth(50);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 150.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 130.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 450.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 270.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 150.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 130.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 450.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 270.0);
cont.getRectRandom().setWidth(1160);
cont.getRectRandom().setHeight(760);
cont.getRectRandomBack().setWidth(1160);
cont.getRectRandomBack().setHeight(760);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(400);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(400);
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller CLass:
#FXML
public void initialize(URL location, ResourceBundle resources) {
String path = new File("src/card/resources/intro.mp4").getAbsolutePath();
me = new Media(new File(path).toURI().toString());
mp = new MediaPlayer(me);
media.setMediaPlayer(mp);
mp.setAutoPlay(true);
media.setSmooth(true);
}
I'm not sure about the blink you see, but it should be easy to react when the video stops.
The JavaFX MediaPlayer class provides a method statusProperty() which returns an object of type ReadOnlyObjectProperty<MediaPlayer.Status>.
The class ReadOnlyObjectProperty<T> implements the interface ObservableValue<T> which provides the method addListener(ChangeListener<? super T>).
The interface ChangeListener<T> is a "functional interface" which requires an implementation for the method void changed(ObservableValue<T> observable, T oldValue, T newValue).
So, putting it together, you should be able to create a listener which will react to the changing status of your MediaPlayer:
private void addStatusListener(MediaPlayer mp) {
ReadOnlyObjectProperty<MediaPlayer.Status> statusProperty = mp.
statusProperty();
statusProperty.addListener((v, o, n) -> playerStatusChanged(v, o, n));
}
private void playerStatusChanged(
ObservableValue<? extends MediaPlayer.Status> observable,
MediaPlayer.Status oldValue, MediaPlayer.Status newValue) {
if (oldValue == MediaPlayer.Status.PLAYING &&
newValue == MediaPlayer.Status.STOPPED) {
// TODO: CALL METHOD TO REACT TO VIDEO BEING STOPPED.
}
}
SOLUTION FOUND!
I made animation to opacity of MediaView:
#FXML public void initialize(URL location, ResourceBundle resources) {
mp = new MediaPlayer(new Media(this.getClass().getResource(MEDIA_URL).toExternalForm()));
media.setMediaPlayer(mp);
media.setSmooth(true);
mp.setAutoPlay(true);
Timeline tm = new Timeline(new KeyFrame(Duration.millis(3000), new KeyValue(media.opacityProperty(), 0.0)));
tm.setDelay(Duration.millis(5500));
tm.play();
}
When i create new stage with WebEngine that playing video from YouTube, after i close it - Youtube keeps playing on backgroung. If i use "Platform.exit" - its close all my JavaFX App, but i want to close only stage that been created for YouTube.
This is my class for YouTube player:
public class YouTube_player {
public YouTube_player(String url) {
final Group root = new Group();
Scene scene = new Scene(root, 820, 480);
final Stage stage = new Stage();
final WebView webView = new WebView();
final WebEngine webEngine = webView.getEngine();
webEngine.loadContent(url);
root.getChildren().add(webView);
stage.centerOnScreen();
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent event) {
//What i should put here to close only this stage.
//Platform.exit - closes all my stages.
//webEngine.getLoadWorker().cancel(); - dont stop Youtube )))
}
});
}
}
My Youtube player stage is creating after i clicking on button in 'mainstage':
b1.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
new YouTube_player(url_video_p1);
}
});
You cant dispose the webengine the only thing that you can do is to set the content of the webEngine to null
webView.getEngine().load(null);
Java 9, for me works:
webView.getEngine().load("");
I am trying to connect two classes. One is main swing class and other is javafx web view in a swing frame. This is run perfectly but do not show the exact output. My URL is coming from my swing frame class.
public Instruction() {
System.out.println("new url number is=="+newurl);
System.out.println("token number is=="+first.tokennum);
System.out.println("final link is="+FirstToken.finallink);
frame1.add(panel);
frame1.setSize(500,500);
frame1.setVisible(true);
Platform.runLater(new Runnable() {
#Override
public void run() {
//To change body of generated methods, choose Tools | Templates.
initfx(panel);
}
});
}
public void initfx(JFXPanel panel){
Scene scene = createscene();
panel.setScene(scene);
geturl(FirstToken.finallink);
}
public Scene createscene(){
Group root = new Group();
Scene scene=new Scene(root);
Text text = new Text();
text.setX(50);
text.setY(100);
root.getChildren().add(text);
return (scene);
}
public void geturl(String url){
WebView web=new WebView();
System.out.println("url is="+url);
web.getEngine().load(url);
}
You are not adding the WebView that you create to the scene graph. To fix it, have your geturl routine return your WebView and add that WebView to the group which forms the root of your scene.
If you don't understand, read the Oracle tutorial on integrating a JavaFX WebView into Swing and review the accompanied source.
Example code:
public WebView geturl(String url){
WebView webView = new WebView();
webView.getEngine().load(url);
return webView;
}
public void initfx(JFXPanel panel){
WebView webView = geturl(FirstToken.finallink);
Scene scene = createscene(webView);
panel.setScene(scene);
}
public Scene createscene(WebView webView){
Pane root = new Pane();
Scene scene = new Scene(root);
root.getChildren().add(webView);
return scene;
}
I've got problem with may e(fx)clipse application. I want to show a splash screen upon application startup. I successfully created class implementing StartupProgressTrackerService, and got my stateReached method invoked. However I've got problems with javafx itself. I want to create Stage with StageStyle.UNDECORATED. However when i invoke stage.show() method stage isn't rendered immediately and appears just after main window is created. It works fine e.g. with StageStyle.UTILITY. It also renders correctly when i use showAndWait() method, but it stops my app from loading until i close the stage.
Here is my code:
public class MyStartupProgressTrackerService implements StartupProgressTrackerService {
private Stage stage;
public MyStartupProgressTrackerService() {
}
#Override
public OSGiRV osgiApplicationLaunched(IApplicationContext applicationContext) {
applicationContext.applicationRunning();
return StartupProgressTrackerService.OSGiRV.CONTINUE;
}
#Override
public void stateReached(ProgressState state) {
if (DefaultProgressState.JAVAFX_INITIALIZED.equals(state)) {
stage = new Stage(StageStyle.UNDECORATED);
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
ImageView view = null;
try {
view = new ImageView(SPLASH_IMAGE);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BorderPane bp = new BorderPane();
bp.getChildren().add(view);
Scene scene = new Scene(bp, 400, 300);
stage.setScene(scene);
stage.show();
}
}
}
I found an ugly solution, but, at least, it works. I noticed that method stage.showAndWait() as a side effect finishes building all controls which haven't been rendered yet. So the trick is to initialize splash screen, and then create dummy stage, showAndWait() it and close() immediately. I know that this solution is far from ideal, so i would appreciate it if someone could show me alternate way to make it work :)
My code:
public void showSplash() {
splashScreen = createSplashScreen();
Stage stage2 = new Stage(StageStyle.TRANSPARENT);
splashScreen.show();
Platform.runLater(new Runnable() {
#Override
public void run() {
stage2.close();
}
});
stage2.showAndWait();
}
private Stage createSplashScreen() {
Stage stage = new Stage(StageStyle.UNDECORATED);
stage.setAlwaysOnTop(true);
VBox vbox = new VBox();
vbox.getChildren().add(new ImageView(splashImage));
Scene scene = new Scene(vbox, 400, 300);
stage.setScene(scene);
return stage;
}
I'm looking for a way to load up a page and save the rendering as an image just as you would do with CutyCapt (QT + webkit EXE to do just that).
At the moment and without JavaFX, I do it by calling an external process from java and rendering to file than loading that file into an ImageBuffer... Neither very optimized nor practical and even less cross platform...
Using JavaFX2+ I tried playing with the WebView & WebEngine:
public class WebComponentTrial extends Application {
private Scene scene;
#Override
public void start(final Stage primaryStage) throws Exception {
primaryStage.setTitle("Web View");
final Browser browser = new Browser();
scene = new Scene(browser, 1180, 800, Color.web("#666970"));
primaryStage.setScene(scene);
scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
class Browser extends Region {
static { // use system proxy settings when standalone application
// System.setProperty("java.net.useSystemProxies", "true");
}
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
getStyleClass().add("browser");
webEngine.load("http://www.google.com/");
getChildren().add(browser);
}
#Override
protected void layoutChildren() {
final double w = getWidth();
final double h = getHeight();
layoutInArea(browser, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
}
#Override
protected double computePrefWidth(final double height) {
return 800;
}
#Override
protected double computePrefHeight(final double width) {
return 600;
}
}
There is a deprecated method : renderToImage in Scene (see links below) that would do something that comes close and with which I'd might be able to work but it is deprecated...
It being deprecated in JavaFX seems to mean that there is no javadoc advertising the replacement method and because I don't have access to the code, I cannot see how it was done...
Here are a couple of sites where I found some information but nothing to render a webpage to an image:
http://tornorbye.blogspot.com/2010/02/how-to-render-javafx-node-into-image.html
canvasImage and saveImage(canvasImage, fc.getSelectedFile()) from this one :
http://javafx.com/samples/EffectsPlayground/src/Main.fx.html
Others:
http://download.oracle.com/javafx/2.0/webview/jfxpub-webview.htm
http://download.oracle.com/javafx/2.0/get_started/jfxpub-get_started.htm
http://fxexperience.com/2011/05/maps-in-javafx-2-0/
I have done this by launching JavaFX WebView on a Swing JFrame and JFXPanel. And then I use the paint() method on JFXPanel once the WebEngine status is SUCCEEDED.
You may follow this tutorial to make a WebView: Integrating JavaFX into Swing Applications
The code below demonstrate how I capture the rendered screen from JFXPanel.
public static void main(String args[]) {
jFrame = new JFrame("Demo Browser");
jfxPanel = new JFXPanel();
jFrame.add(jfxPanel);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
browser = new FXBrowser();
jfxPanel.setScene(browser.getScene());
jFrame.setSize((int) browser.getWebView().getWidth(), (int) browser.getWebView().getHeight());
browser.getWebEngine().getLoadWorker().stateProperty().addListener(
new ChangeListener() {
#Override
public void changed(ObservableValue observable,
Object oldValue, Object newValue) {
State oldState = (State) oldValue;
State newState = (State) newValue;
if (State.SUCCEEDED == newValue) {
captureView();
}
}
});
}
});
}
});
}
private static void captureView() {
BufferedImage bi = new BufferedImage(jfxPanel.getWidth(), jfxPanel.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bi.createGraphics();
jfxPanel.paint(graphics);
try {
ImageIO.write(bi, "PNG", new File("demo.png"));
} catch (IOException e) {
e.printStackTrace();
}
graphics.dispose();
bi.flush();
}
For JavaFX 2.2 users there is a much more clean and elegant solution based on JavaFX Node snapshot. You can take a look to JavaFX node documentation at:
http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html
Here is an example of taking a capture from a WebView Node (as webView)
File destFile = new File("test.png");
WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
RenderedImage renderedImage = SwingFXUtils.fromFXImage(snapshot, null);
try {
ImageIO.write(renderedImage, "png", destFile);
} catch (IOException ex) {
Logger.getLogger(GoogleMap.class.getName()).log(Level.SEVERE, null, ex);
}
I had no problems with this solution, and the webview is rendered perfectly in the PNG according to the node size. Any JavaFx node can be rendered and saved to a file with this method.
Hope this help!
Workaround posted by JavaFX engineers: Snapshot does not work with (invisible) WebView nodes.
When taking a snapshot of a scene that contains a WebView node, wait for at least 2 frames before issuing the snapshot command. This can be done by using a counter in an AnimationTimer to skip 2 pulses and take the snapshot on the 3rd pulse.
Once you have got your snapshot, you can convert the image to an awt BufferedImage and encode the image to format like png or jpg, using ImageIO.