IS there any way to export Stage or some other component like BorderPane with visual components into PDF file? I want when I click a button to export the component into PDF file? IS there any example?
You can use PDFBox libreries in your javafx project . this code take a snapshot from a node , make an image file , then create a pdf file and insert the image in the pdf file . you need this .jar http://www-us.apache.org/dist/pdfbox/2.0.8/pdfbox-app-2.0.8.jar . with pdfbox you can pass strings of javafx to text in pdf , you can do many things ( change font , size .... )
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Button;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
public class NodeToPdf extends Application {
#Override
public void start(Stage primaryStage) {
CategoryAxis xAxis = new CategoryAxis();
xAxis.setLabel("x");
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("y");
BarChart bar = new BarChart(xAxis, yAxis);
bar.setMaxSize(300, 300);
bar.setTitle("Bar node" );
bar.setTranslateY(-100);
Button btn = new Button();
btn.setTranslateY(100);
btn.setText("To Pdf'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
WritableImage nodeshot = bar.snapshot(new SnapshotParameters(), null);
File file = new File("chart.png");
try {
ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", file);
} catch (IOException e) {
}
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
PDImageXObject pdimage;
PDPageContentStream content;
try {
pdimage = PDImageXObject.createFromFile("chart.png",doc);
content = new PDPageContentStream(doc, page);
content.drawImage(pdimage, 100, 100);
content.close();
doc.addPage(page);
doc.save("pdf_file.pdf");
doc.close();
file.delete();
} catch (IOException ex) {
Logger.getLogger(NodeToPdf.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
root.getChildren().add(bar);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
you can send a node to a printer and save it as pdf with a virtual printer
public class PrinterNodetExample extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("To Printer!");
PrinterJob job = PrinterJob.createPrinterJob();
if(job != null){
job.showPrintDialog(primaryStage);
job.printPage(root);
job.endJob();
}
}
});
primaryStage.setTitle("Printer");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}}
First guess is that you could simply save the Scene root to the image and then include it to the PDF. See Node#snapshot method for details.
Related
So I've made a checkbox that applies a scale transition to a rectangle when checked. But the problem is that the transition keeps going even after I uncheck the checkbox. Any ideas on how to make it stop after un-checking?
checkbox.setOnAction(e -> {
ScaleTransition scaleT = new ScaleTransition(Duration.seconds(5), rectangle);
scaleT.setAutoReverse(true);
scaleT.setCycleCount(Timeline.INDEFINITE);
scaleT.setToX(2);
scaleT.setToY(2);
scaleT.play();
});
To control the animation, you need to define the transistion(with INDEFINITE cycle count) outside the CheckBox listener/action. Then you can just play/pause the animation as you required.
Below is the quick demo:
import javafx.animation.ScaleTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ScaleTransitionDemo extends Application {
#Override
public void start(Stage stage) {
Shape rectangle = new Rectangle(50, 50, Color.BLUE);
ScaleTransition transition = new ScaleTransition(Duration.seconds(1), rectangle);
transition.setDuration(Duration.seconds(1));
transition.setAutoReverse(true);
transition.setCycleCount(Timeline.INDEFINITE);
transition.setToX(3);
transition.setToY(3);
CheckBox checkBox = new CheckBox("Animate");
checkBox.selectedProperty().addListener((obs, old, selected) -> {
if (selected) {
transition.play();
} else {
transition.pause();
}
});
StackPane pane = new StackPane(rectangle);
VBox.setVgrow(pane, Priority.ALWAYS);
VBox root = new VBox(20, checkBox, pane);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 300, 300);
stage.setScene(scene);
stage.setTitle("Scale transition");
stage.show();
}
public static void main(String[] args) {
launch();
}
}
checking whether checkbox is selected or not with .isSelected() method . In this approach , scaled node will back to xy = 1 scale if checkbox is unchecked , but it will be disabled until transition ends .You can adjust setDuration . I've changed it just for gif recording. This is a single class javafx app you can try .
App.java
public class App extends Application {
#Override
public void start(Stage stage) {
Shape rectangle = new Rectangle(50, 50, Color.BLUE);
ScaleTransition scaleT = new ScaleTransition(Duration.seconds(1), rectangle);
CheckBox checkBox = new CheckBox("scale");
checkBox.setOnAction(e -> {
if (checkBox.isSelected()) {
scaleT.setDuration(Duration.seconds(1));
scaleT.setAutoReverse(true);
scaleT.setCycleCount(Timeline.INDEFINITE);
scaleT.setToX(2);
scaleT.setToY(2);
scaleT.play();
} else {
scaleT.setDuration(scaleT.getCurrentTime());
scaleT.stop();
scaleT.setCycleCount(1);
scaleT.setToX(1);
scaleT.setToY(1);
scaleT.play();
checkBox.setDisable(true);
scaleT.setOnFinished((t) -> {
checkBox.setDisable(false);
});
}
});
var scene = new Scene(new HBox(50, rectangle, checkBox), 640, 480);
stage.setScene(scene);
stage.setTitle("scale transition");
stage.show();
}
public static void main(String[] args) {
launch();
}
}
How to do that in JavaFX?
The popup shows up when the mouse enters a node. When the mouse enters the showing popup, the popup obscures the mouse from the node. Then the node fire exit event. How to make the popup ignore the mouse events?
code
package sample;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
primaryStage.setScene(new Scene(root, 300, 275));
Label labelNode = new Label("Label Node");
labelNode.setPrefHeight(200);
labelNode.styleProperty().set("-fx-background-color: orange");
Popup popup = new Popup();
popup.getScene().getRoot().setMouseTransparent(true);
AnchorPane popContent =new AnchorPane();
popContent.styleProperty().set("-fx-background-color: red");
popContent.setPrefHeight(100);
popContent.getChildren().add(new Label("Popup content"));
popup.getContent().add(popContent);
labelNode.setOnMouseEntered(event->{
Point3D point3D = labelNode.localToScene(event.getX(), event.getY(), 0);
popup.show(primaryStage, point3D.getX()-5, point3D.getY()-5);
});
labelNode.setOnMouseExited(event->{
popup.hide();
});
root.getChildren().add(labelNode);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Please try moving the cursor in to "yellow" several times.
Solution:
Keep two boolean nodeExited and popupExited statuses. Hide popup when both are true.
package sample;
import javafx.application.Application;
import javafx.geometry.Point3D;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
public class Main extends Application {
boolean nodeExited = false;
boolean popupExited = false;
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
primaryStage.setScene(new Scene(root, 300, 275));
Label labelNode = new Label("Label Node");
labelNode.setPrefHeight(200);
labelNode.styleProperty().set("-fx-background-color: orange");
Popup popup = new Popup();
popup.getScene().getRoot().setMouseTransparent(true);
AnchorPane popContent = new AnchorPane();
popContent.styleProperty().set("-fx-background-color: red");
popContent.setPrefHeight(100);
popContent.getChildren().add(new Label("Popup content"));
popup.getContent().add(popContent);
popup.getScene().setOnMouseEntered(event -> {
popupExited = false;
});
popup.getScene().setOnMouseExited(event -> {
popupExited = true;
if (nodeExited)
popup.hide();
});
labelNode.setOnMouseEntered(event -> {
nodeExited = false;
Point3D point3D = labelNode.localToScene(event.getX(), event.getY(), 0);
popup.show(primaryStage, point3D.getX() - 5, point3D.getY() - 5);
});
labelNode.setOnMouseExited(event -> {
nodeExited = true;
if (popupExited)
popup.hide();
});
root.getChildren().add(labelNode);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I intend to change the images of birds over the time by adding key frames in a for loop to the timeline object. It turns out that only the first image is displayed. Could someone point out where part I got it wrong. Thanks in advance.
Besides, I noticed that I have to reset the counter "index" to 0 after for loop otherwise it generates java.lang.ArrayIndexOutOfBoundsException.
package application;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.effect.BoxBlur;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
public class Main extends Application
{
int index=0;
#Override
public void start(Stage primaryStage) {
try {
ImageView bgV = new ImageView();
Image img_BG = new Image(Main.class.getResourceAsStream("background.png"));
bgV.setImage(img_BG);
bgV.setEffect(new BoxBlur());
bgV.setOpacity(0.5);
ImageView t1V = new ImageView();
Image img_t1 = new Image(Main.class.getResourceAsStream(
"t1.png"
));
t1V.setImage(img_t1);
ImageView t2V = new ImageView();
Image img_t2 = new Image(Main.class.getResourceAsStream(
"t2.png"
));
t2V.setImage(img_t2);
ImageView t3V = new ImageView();
Image img_t3 = new Image(Main.class.getResourceAsStream(
"t3.png"
));
t3V.setImage(img_t3);
Group foreground = new Group(t1V,t2V,t3V);
t1V.setTranslateX(20);
t1V.setTranslateY(200);
t2V.setTranslateX(300);
t2V.setTranslateY(200);
t3V.setTranslateX(550);
t3V.setTranslateY(200);
foreground.setEffect(new DropShadow());
String[]
birdFiles = {"b1.png", "b2.png", "b3.png", "b4.png", "b5.png", "b6.png"};
double[] ds = { 300, 600, 900, 1200, 1500, 1800};
ImageView birdV = new ImageView(new Image(Main.class.getResourceAsStream(birdFiles[0])));
Group birds = new Group(birdV);
birds.setTranslateX(img_BG.getWidth()-100);
Timeline timeline = new Timeline();
timeline.setCycleCount(
Animation.INDEFINITE
);
KeyFrame[] kframs = new KeyFrame[birdFiles.length];
for( index=0; index<birdFiles.length; index++)
{
EventHandler<ActionEvent>
onFishined = new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent arg0)
{
birds.getChildren().setAll(new ImageView(new Image(Main.class.getResourceAsStream(birdFiles[index]))));
}
};
Duration duration = Duration.millis(ds[index]);
KeyFrame
kf = new KeyFrame(duration, onFishined,null,null );
timeline.getKeyFrames().add(kf);
}//End for i
index = 0;
timeline.play();
Group root = new Group(bgV,foreground,birds);
Scene scene = new Scene(root,img_BG.getWidth(), img_BG.getHeight());
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
you don't have to declare your index-field outside. this also causes your problem: whenever the handle method is called, it will reference your field: index which you set to 0 after your loop.
therefor you can declare a new field as finaland pass it to the handler:
for (int index = 0; index < birdFiles.length; index++) {
final int birdIndex = index;
EventHandler<ActionEvent> onFishined = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
birds.getChildren().setAll(new ImageView(new Image(Main.class.getResourceAsStream(birdFiles[birdIndex]))));
}
};
...
}
I have 2 screens in application. First screen(MainController class) which opens with running application from Eclipse.
And second screen(SecondController class) which opens on button located in first screen.
How can I make some kind of 'Back' button in second screen which will show back first screen?
I'm creating the visual part of the application in JavaFX Scene Builder if it matters.
Here is a small example, containing to screens, to show how you can achieve what you are looking for
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.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TwoScreensWithInterchange extends Application {
#Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(loadScreenOne(), 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public VBox loadScreenOne()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Switch Screen");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenTwo());
}
});
Text text = new Text("Screen One");
vBox.getChildren().addAll(text, button);
vBox.setStyle("-fx-background-color: #8fbc8f;");
return vBox;
}
public VBox loadScreenTwo()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Back");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenOne());
}
});
Text text = new Text("Screen Two");
vBox.getChildren().addAll(text, button);
return vBox;
}
}
In the below code, I already have an mp4 video playing in mview3, as I click in the switch button, I want the fade-out effect as mview3 stops and some fade-in effect as mview2 starts.
swtch.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
time1 = mplayer3.getCurrentTime();
mplayer2.setStartTime(time1);
mplayer2.play();
secondaryLayout.getChildren().add(mview2);
secondaryLayout.getChildren().remove(mview3);
Please suggest any possible alternatives to have the effects enabled.
I made a little example for you, maybe you can have a look at this page, and read something about Trasition and its subclasses. In this example i used the FadeTransition and add the MediaView's on a StackPane, so you can fade out one View and fade in the other.
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import javafx.util.Duration;
public class VideoPlayer extends Application {
private static final String MEDIA_URL = "http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv";
#Override
public void start(Stage primaryStage) {
VBox root = new VBox();
Media media = new Media(MEDIA_URL);
Media yt = new Media(MEDIA_URL);
final MediaPlayer mediaPlayer = new MediaPlayer(media);
final MediaPlayer mediaPlayer1 = new MediaPlayer(yt);
MediaView mediaView2 = new MediaView(mediaPlayer1);
mediaView2.setOpacity(0.0);
MediaView mediaView3 = new MediaView(mediaPlayer);
//root.getChildren().add(mediaView);
Button play = new Button("Play");
play.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
mediaPlayer.play();
}
});
Pane hBox = new StackPane();
final FadeTransition fadeOut = new FadeTransition(Duration.millis(3000), mediaView3);
fadeOut.setFromValue(1.0);
fadeOut.setToValue(0.0);
final FadeTransition fadeIn = new FadeTransition(Duration.millis(3000), mediaView2);
fadeIn.setFromValue(0.0);
fadeIn.setToValue(1.0);
hBox.getChildren().add(mediaView3);
hBox.getChildren().add(mediaView2);
root.getChildren().add(play);
Button stopBtn = new Button("Stop");
stopBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Duration currentTime = mediaPlayer.getCurrentTime();
mediaPlayer.stop();
mediaPlayer1.setStartTime(currentTime);
fadeOut.play();
fadeIn.play();
mediaPlayer1.play();
}
});
root.getChildren().add(stopBtn);
root.getChildren().add(hBox);
Scene scene = new Scene(root, 1024, 768);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Hope it helps
Patrick