I have a piece of code designed to take a screenshot of a node in JavaFX:
public BufferedImage getSnapshot(final Node... hideNodes) {
Window window = getScene().getWindow();
Bounds b = localToScene(getBoundsInLocal());
int x = (int) Math.round(window.getX() + getScene().getX() + b.getMinX());
int y = (int) Math.round(window.getY() + getScene().getY() + b.getMinY());
int w = (int) Math.round(b.getWidth());
int h = (int) Math.round(b.getHeight());
try {
Robot robot = new Robot();
for(Node node : hideNodes) {
node.setOpacity(0);
node.getParent().requestLayout();
}
BufferedImage image = robot.createScreenCapture(new java.awt.Rectangle(x, y, w, h));
for(Node node : hideNodes) {
node.setOpacity(1);
node.getParent().requestLayout();
}
return image;
}
catch(AWTException ex) {
return null;
}
}
It has a twist, and that is it should hide the given nodes before taking the screenshot (in case they overlap with the node, which in some cases is definite.)
However, I'm stuck finding a way to force a redraw to include the opacity change before taking the screenshot - the only reference I found was to requestLayout()
, but no joy there.
What method(s) should I call to force and wait for a redraw to complete?
We’ve compiled some possible ways to force install a Windows Update by eliminating issues causing the delay. 1. Restart the Windows Update Service This service handles the delivery of software updates to Windows devices. Your PC may fail to automatically download or install a new update if the service is malfunctioning or inactive.
You may be unable to force a Windows Update if Microsoft places a Safeguard Hold on your PC. A “safeguard hold” is a technique used to temporarily prevent users from installing an unstable or potentially harmful update. So, how do you identify a safeguard hold?
Instead of grabbing the layout and forcing that to update, you could just steal a line from LayoutElement (which never seems to have any problem like this) - LayoutRebuilder.MarkLayoutForRebuild (transform as RectTransform) which achieves the same effect and doesn't require you to find or store anything extra, as people have done above.
The forceUpdate method. Vue offers us a built-in method called forceUpdate (), by using this method inside vue instance we can force update the component. In the above example, we have attached an update method to the button element. Inside the update method we have added a this.$forceUpdate () method, so that when we click on a button, ...
I find your code quite strange:
node.setOpacity(0)
to make it invisible, rather than node.setVisible(false)
? BufferedImage
rather than a JavaFX Image
? Perhaps there are reasons for these things which I don't understand, but I'd just do it this way:
public Image takeSnapshot(Scene scene, final Node... hideNodes) {
for (Node node: hideNodes) node.setVisible(false);
Image image = scene.snapshot(null);
for (Node node: hideNodes) node.setVisible(true);
return image;
}
I created a small sample app which uses the above routine.
The primary window includes a group with a circle and a rectangle. When a snapshot command is issued, the rectangle is hidden in the primary, a snapshot of the primary is taken, then the rectangle is made visible in the primary again.
To answer your question's title about forcing a UI update - you can't really. The JavaFX application thread and JavaFX rendering thread are to be treated as two separate things. What you need to do is run your processing on the JavaFX application thread, seed control back to the JavaFX system, wait for it to do it's rendering, then examine the results. The scene.snapshot
method will take care of that synchronization for you so you don't need to worry about it.
If, for whatever reason, scene.snapshot
won't work for you and you wanted to maintain something similar to your original strategy, then what you would do is:
Platform.runLater
call and take your robotic snapshot in the runLater body.Platform.runLater
command to get back on the JavaFX application thread.This should work as it will allow the JavaFX system to perform another pulse which performs a rendering layout of the screen with the opacity changes before your robot actually takes the snapshot. An alternate mechanism is to use a JavaFX AnimationTimer
which will provide you with a callback whenever a JavaFX pulse occurs. Maintaining proper synchronization of all of this between the AWT and JavaFX threads, would be annoying.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With