How to refresh web sourced images in GUI - java

I've created a simple GUI application in Java, that displays certain images sourced from the web in different sections.
What i'm having trouble doing, is refreshing the images after a certain period of time (specifically, 40 seconds). I need the application to get the image from the web again, and place it in one of three JPanels, every 40 seconds as long as the app is running.
This is the code used to display the images when the application starts up:
try {
BufferedImage cameraOne = ImageIO.read(new URL("IMAGE LINK HERE"));
BufferedImage cameraTwo = ImageIO.read(new URL("IMAGE LINK HERE"));
BufferedImage cameraThree = ImageIO.read(new URL("IMAGE LINK HERE"));
cPanel1 = new CameraPanel(new ImageIcon(cameraOne).getImage());
cPanel2 = new CameraPanel(new ImageIcon(cameraTwo).getImage());
cPanel3 = new CameraPanel(new ImageIcon(cameraThree).getImage());
} catch (MalformedURLException ex) {
throw new Error("One of the cameras you are trying to connect to doesn't exist!");
} catch (IOException ex) {
throw new Error("CCTV couldn't connect to the required camera feeds!");
}
This is the code i'm using to attempt to schedule the image refreshes:
final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
final Runnable refreshTask = new Runnable() {
public void run() {
// I need help here
}
};
final ScheduledFuture<?> refreshHandler = scheduler.scheduleAtFixedRate(refreshTask, 10, 40, TimeUnit.SECONDS);
This is the CameraPanel class, as seen in the code used to display the images when the application starts up:
class CameraPanel extends JPanel {
private Image img;
public CameraPanel(BufferedImage cameraImage) {
this(new ImageIcon(cameraImage).getImage());
}
public CameraPanel(Image img) {
this.img = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
Any solutions? Thanks.
The full class i've created can be found at http://pastebin.com/nRNHHkfx

Related

Java splash screen not working on Mac OSX but working on a PC

the code below is the implementation of a splash screen for a small Java application I have developed with Eclipse. The splash screen works perfectly well on a PC but not on a MAC. On MAC OSX, the frame first appears has a gray area during 2 seconds and then the image appears for the remaining of the 4 seconds. The image should normally appears right away and for a duration of 4 seconds. Do you have any idea why there is a delay before the image appears on a MAC while everything works well on a PC? PS: I have deployed the application as an executable Jar and I'm using Java 8 on all computers. Thank you.
public static void main(String[] args)
{
SplashScreen splSplashScreen = new SplashScreen();
//Main window
FenetrePrincipale fenetrePrincipale = new FenetrePrincipale();
}
public class SplashScreen extends JWindow
{
/**
* Numéro de série
*/
private static final long serialVersionUID = 1592663893301307318L;
private final static long TEMP_AFFICHAGE = 4000;
/**
* Constructeur par initialisation
* #param p_Frame Frame
* #param p_TempsAffichage Temps d'affichage en millisecondes
*/
public SplashScreen()
{
super(new Frame());
JLabel lblImage = new JLabel(new ImageIcon(this.getClass().getResource("/res/ui/splashScreen.jpg")));
Container container = this.getContentPane();
container.add(lblImage, BorderLayout.CENTER);
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension labelSize = lblImage.getPreferredSize();
this.setLocation(screenSize.width/2 - (labelSize.width/2), screenSize.height/2 - (labelSize.height/2));
this.setVisible(true);
try
{
Thread.sleep(TEMP_AFFICHAGE);
}
catch (InterruptedException ex)
{
ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
}
finally
{
this.setVisible(false);
}
}
}
Edit This is completely different from the original answer; I'm leaving the original answer below.
It looks like the initial JLabel render is dog slow on OSX - removing all the sleeps still leaves me with a ~2 second pause while java renders the label. So we change the rules.
First we create a JPanel class that takes a buffered image:
class ImgPanel extends JPanel {
private BufferedImage img;
public ImgPanel(BufferedImage img) {
this.img = img;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
}
}
We then change the splash screen constructor like so:
public SplashScreen()
{
BufferedImage img = null;
try {
img = ImageIO.read(this.getClass().getResource("/res/ui/splashScreen.jpg"));
} catch (Exception ex) {
}
ImgPanel panel = new ImgPanel(img);
Container container = this.getContentPane();
container.add(panel);
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(screenSize.width/2 - (img.getWidth()/2), screenSize.height/2 - (img.getHeight()/2));
this.setVisible(true);
}
This renders the image as soon as the panel appears without a pause.
Note - I've removed the entire sleep code to reduce confusion - the logic would be better captured in a separate SwingWorker to perform the hide of the splash screen and the display of the main screen:
class worker extends SwingWorker<String, Object> {
private final static long TEMP_AFFICHAGE = 4000;
private SplashScreen splash;
private FenetrePrincipale principale;
public worker(SplashScreen splash, FenetrePrincipale principale) {
this.splash = splash;
this.principale = principale;
this.splash.setVisible(true);
}
#Override
public String doInBackground() {
try
{
Thread.sleep(TEMP_AFFICHAGE);
}
catch (InterruptedException ex)
{
ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
}
return "";
}
#Override
protected void done() {
splash.setVisible(false);
principale.setVisible(true);
}
};
Then the main code looks like:
public static void main(String[] args)
{
SplashScreen splSplashScreen = new SplashScreen();
//Main window
FenetrePrincipale fenetrePrincipale = new FenetrePrincipale();
worker w = new worker(splSplashScreen, fenetrePrincipale);
w.execute();
}
Original answer - putting the thread for sleeping into a SwingWorker is still a good idea, as it would allow you to perform actual work before initialization.
Ok, a simple example of putting the sleep off the gui thread, using your splash code - this code comes after the this.setVisible(true), and replaces the try {} catch {} finally {} clause:
this.setVisible(true);
worker do_work = new worker(this);
do_work.execute();
}
class worker extends SwingWorker<String, Object> {
private SplashScreen parent;
public worker(SplashScreen parent) {
this.parent = parent;
}
#Override
public String doInBackground() {
try
{
Thread.sleep(TEMP_AFFICHAGE);
}
catch (InterruptedException ex)
{
ApplicationLogger.getInstance().severe(ex.getLocalizedMessage());
}
return "";
}
#Override
protected void done() {
parent.setVisible(false);
}
};
I just create a SwingWorker which does a sleep as doWorkInBackground, and once that is concluded, it closes the parent frame, which is the splash screen.

JavaFX Canvas is not drawing image

I am trying to take input from webcam and draw it in the canvas.
For this I wrote following block of code:
void addImageToCanvas(Image img){
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight());
}
If I pass only one Image in that function by hand, it works.
If I pass two Images then it only draws the last one.
But I am implementing thread and calling it continuously from the thread. The function is then comes to stand still on the line:
gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight());
The part of code from where the method is invoked:
public void run() {
try {
FrameGrabber grabber = new VideoInputFrameGrabber(cameraId);
CvMemStorage storage = CvMemStorage.create();
grabber.start();
IplImage grabbedImage;
BufferedImage bufImg;
int counter = 0;
while (primaryCameraChosen != null) {
grabbedImage = grabber.grab();
bufImg = grabbedImage.getBufferedImage();
addImageToCanvas(SwingFXUtils.toFXImage(bufImg, null));
try{
Thread.sleep(10000);
} catch(InterruptedException e){}
}
grabber.stop();
grabber.release();
} catch (Exception ex) {
}
}
Could you please tell me what I am doing wrong here? What is the solution? Thanks.
So you have actually two problems
1) If I pass two Images then it only draws the last one.
You are drawing images to the same X, Y coordinates, with the same height and width. The result is (like in real life if your paint something on an existing painting) that only the last drawing is visible as it hides the first one. This is the expected working.
Update after comment from OP:
The problem with this line: gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight()); is, that at the first Image it tries to draw the Image using the actual height and width of the Canvas, but most probably at this moment the Canvas itself was not drawn, so the actual height and width are both zero, therefore the picture gets drawn with zero width and height.
Two possible solutions are:
1) Only start the Thread when the Stage that contains the Canvas is actually showing. This will ensure that the Canvas has the correct height and width even at the first picture.
2) Listen to the width and height change of the canvas and redraw the Image if needed. This will ensure, that whenever the canvas is resized, the Image is redrawn (it has the advantage that the picture fits to the screen all the time so this one is the suggested way)
The example contains both approaches.
2) But I am implementing thread and calling it continuously from the
thread. The function is then comes to stand still on the line
We shall see the full code what can be wrong, but until you update the post to include every part that is needed to detect the problem, here is an example which updates a canvas with an Image from a background thread every 3 seconds:
public class Main extends Application {
Canvas canvas;
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
canvas = new Canvas();
Pane pane = new Pane();
pane.getChildren().add(canvas);
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
root.setCenter(pane);
Thread thread = new Thread(new Runnable() {
private ObjectProperty<Image> imageToBeDrawn = new SimpleObjectProperty<Image>(new Image(getClass().getResource("a.jpg").toExternalForm()));
#Override
public void run() {
// Option 2: Listen to the width and height property change of the canvas and redraw the image
canvas.widthProperty().addListener(event -> addImageToCanvas(imageToBeDrawn.get()));
canvas.heightProperty().addListener(event -> addImageToCanvas(imageToBeDrawn.get()));
imageToBeDrawn.addListener(event -> addImageToCanvas(imageToBeDrawn.get()));
while(true){
Random rand = new Random();
if(rand.nextBoolean())
imageToBeDrawn.set(new Image(getClass().getResource("a.jpg").toExternalForm()));
else
imageToBeDrawn.set(new Image(getClass().getResource("b.jpg").toExternalForm()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.setDaemon(true);
thread.start();
primaryStage.setScene(scene);
// Option 2: start the Thread only when the stage is showing
// primaryStage.showingProperty().addListener(new ChangeListener<Boolean>() {
//
// #Override
// public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// if(newValue)
// thread.start();
//
// }
// });
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
void addImageToCanvas(Image img){
Platform.runLater(new Runnable(){
#Override
public void run() {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight());
}});
}
public static void main(String[] args) {
launch(args);
}
}

Issue with drawing png file into swing GUI

I am facing an issue that I am not able to draw an png image into my swing GUI into JPanel (which I am adding into JScrollPane after).
Here is my code I am trying to use for this.
if(processCreated == true){
System.out.println("updating tree of organisms");
processCreated = false;
updateTree();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Svet.class.getName()).log(Level.SEVERE, null, ex);
}
tree = new File(path+"/out.png"); //File
try {
treeImage = ImageIO.read(tree); //BufferedImage
tp = new TreePane(treeImage.getScaledInstance( 500, 500 ,treeImage.SCALE_SMOOTH)); // my class, implementation below
treeOutput.add(tp); //adding "JPanel" with picture into GUI
treeOutput.repaint();
} catch (IOException ex) {
Logger.getLogger(Svet.class.getName()).log(Level.SEVERE, null, ex);
}
}
After this the JScrollPane remains empty. Is there anything wrong with my code? You can find what is what in comments.
Here is my TreePane class
public class TreePane extends JPanel{
Image img;
public TreePane( Image img ){
this.img = img;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
int imgX = 0;
int imgY = 0;
g.drawImage(img, imgX, imgY, this);
}
}
Very likely your TreePane is layed out to size 0,0 and therefore you'll never see anything. Try adding a breakpoint and inspecting in paintComponent - it might not even ever get called.
To fix, you should explicitly set the size of your TreePane before adding it to the JScrollPane.
tp = new TreePane(treeImage.getScaledInstance( 500, 500 ,treeImage.SCALE_SMOOTH)); // my class, implementation below
tp.setSize(myWidth, myHeight);
treeOutput.add(tp); //adding "JPanel" with picture into GUI
treeOutput.repaint();

Why does my Spaceship not appear?

This is a very simple problem to some people I'm assuming and I just can't see it. (I'm very amateur at Java).
This is some test code I wrote to try and troubleshoot why it's not working in my other project. For some reason my rocketshipStationary.png just won't show up when I load the Java Applet.
This is my code:
public class Test extends Applet {
public Image offScreen;
public Graphics d;
public BufferedImage rocketship;
public void init() {
setSize(854, 480);
offScreen = createImage(854,480);
d = offScreen.getGraphics();
try {
rocketship = ImageIO.read(new File("rocketshipStationary.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
d.clearRect(0, 0, 854, 480);
d.drawImage(rocketship, 100, 100, this);
d.drawImage(offScreen, 0, 0, this);
}
}
You should be getting a nice big stack trace that describes what happens. The bottom line is that 'applets and files do not play well together'.
Instead, either establish an URL to the image and use that for ImageIO, or alternately use the URL in the Applet.getImage(URL) method.

JavaFX 2.0+ WebView /WebEngine render web page to an image

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.

Categories

Resources