Reputation: 431
I'm facing a problem when getting a snapshot of JavaFX TableView. I want to generate an image file of table without showing the stage. Everything works fine except the parent column labels disappear when the stage is not shown.
I have regular table with sample Person( name, age, email->(primary, secondary) ) data:
TableView<Person> personTable = new TableView<>();
TableColumn<Person, String> nameColumn = new TableColumn<>( "Name" );
TableColumn<Person, Integer> ageColumn = new TableColumn<>( "Age" );
TableColumn<Person, String> emailColumn = new TableColumn<>( "Email" );
TableColumn<Person, String> primaryEmailColumn = new TableColumn<>( "Primary" );
TableColumn<Person, String> secondaryEmailColumn = new TableColumn<>( "Secondary" );
nameColumn.setCellValueFactory( cellDataFeatures -> cellDataFeatures.getValue().nameProperty() );
ageColumn.setCellValueFactory( cellDataFeatures -> cellDataFeatures.getValue().ageProperty().asObject() );
primaryEmailColumn.setCellValueFactory( cellDataFeatures -> cellDataFeatures.getValue().primaryEmailProperty() );
secondaryEmailColumn.setCellValueFactory( cellDataFeatures -> cellDataFeatures.getValue().secondaryEmailProperty() );
emailColumn.getColumns().addAll( primaryEmailColumn, secondaryEmailColumn );
personTable.getColumns().addAll( nameColumn, ageColumn, emailColumn );
personTable.setItems( generatePersonList() );
Generating image file:
Scene scene = new Scene( personTable );
saveAsPng( personTable, "test2.png" );
As you can see the email's column label("Email") is not visible.
I can hack around this by showing the stage, taking the image and hiding the stage:
Scene scene = new Scene( personTable );
stage.setScene( scene );
stage.show();
saveAsPng( personTable, "test2.png" );
stage.close();
My aim is to generate second image(with "Email" label) without showing the stage.
I assumed that only the columns that have values associated with them have the graphics shown, so I tried the following, but to no avail:
emailColumn.setGraphic( new Label( "Test" ) );
emailColumn.setVisible( true );
emailColumn.getGraphic().setVisible( true );
saveAsPNG:
private void saveAsPng(Parent container, String path)
{
File file = new File(path);
WritableImage img = container.snapshot( null, null );
RenderedImage renderedImage = SwingFXUtils.fromFXImage(img, null);
try {
ImageIO.write(renderedImage,"png", file);
}
catch (Exception e) {
e.printStackTrace();
}
}
Thanks!
Upvotes: 3
Views: 589
Reputation: 45456
Whenever you are doing operations on a Scene
that is not added to a Stage
, you are missing the JavaFX layout calculations (applying CSS and the layout pass) that occur as part of a pulse when a regular scene is shown.
In your case, as the scene is not shown, you need to actually force the layout by manually triggering the CSS application and layout pass before you do the snapshot.
You can easily do that by calling node.applyCss()
and node.layout
, according the JavaFX JavaDoc:
public final void applyCss()
If required, apply styles to this Node and its children, if any. This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() to size a Node before the next pulse, or if the Scene is not in a Stage.
Provided that the Node's Scene is not null, CSS is applied to this Node regardless of whether this Node's CSS state is clean. CSS styles are applied from the top‑most parent of this Node whose CSS state is other than clean, which may affect the styling of other nodes. This method is a no-op if the Node is not in a Scene. The Scene does not have to be in a Stage.
This method does not invoke the Parent.layout() method. Typically, the caller will use the following sequence of operations.
parentNode.applyCss(); parentNode.layout();
In your case, this will work:
Scene scene = new Scene( personTable );
personTable.applyCss();
personTable.layout();
saveAsPng( personTable, "test2.png" );
Note that if you don't set any size for the scene nor the table, the layout will use its pref size, so it should be convenient to give a pref width to each of the columns before (probably you are already doing it).
Upvotes: 1