How to refresh/reload an image in Swing GUI as the real image file changes?

259 views Asked by At

I am trying to implement Remote FrameBuffer Protocol using Java socket programming. I have a server side program that takes screenshot of the entire screen using robot and store it in BufferedImage .Then I converted it into a byte array and sending it to the client .

Objective : To display the entire screen of the server side machine in a Swing GUI of the client side.

Problem i am facing : i am able to send the image in bytes from server and receive it from the server by the client (client.java) and convert it into a jpg image (output.jpg) using ImageIO and put that image in a Swing frame.

But i am able to see the first image in the Swing and whenever the image gets updated ,the image in the Swing is not updating or refreshing .

What I want : I want the image to refresh and show updated image every time the server sends the image data .

client.java

package remoteclient;

import java.lang.*;
import javax.imageio.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.net.*;
import java.io.*;

public class client  {

   
    public static void main(String args[])throws Exception{  
        Socket s=new Socket("localhost",5900);  
        DataInputStream din=new DataInputStream(s.getInputStream());  
        DataOutputStream dout=new DataOutputStream(s.getOutputStream());  
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
      
        int width=0,height=0;
        try {
            width = din.readInt(); //getting width and height from server thru socket.
            height = din.readInt(); 
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        JFrame f = new JFrame("Client");
        JLabel label = new JLabel();
       

        f.setSize(width, height);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         f.setVisible(true);

        boolean continueLoop = true;
        

        while(continueLoop)
        {
            try {
                
                int len = din.readInt();
                byte[] imageInByte = new byte[len];
                            
                System.out.println(len);

                din.readFully(imageInByte);

                System.out.println(imageInByte);
                
                ByteArrayInputStream bis = new ByteArrayInputStream(imageInByte);
                BufferedImage bImage2 = ImageIO.read(bis);
             //   Image im1 = bImage2.getScaledInstance(width,height, Image.SCALE_SMOOTH);

                
                ImageIO.write(bImage2, "jpg", new File("output.jpg") );
                bImage2 = ImageIO.read(new File("output.jpg"));
                label.setIcon(new ImageIcon(im1));
              
                ImageIcon icon = new ImageIcon(bImage2);
                icon.getImage().flush();
                label.setIcon( icon );
                f.getContentPane().add(label, BorderLayout.CENTER);
                
                f.pack();

           
            
           
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

  
    }
    }  


What I want : I want the image to refresh and show updated image every time the server sends the image data .

1

There are 1 answers

10
pfurbacher On

Updated code with comments about demo code that should be removed from your working code:

Here's an example, using default UIManager icons, and SwingWorker, as noted in the comments to the original posting. You would instead use images from your server connection.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public class SwingLabelWithUpdatedImage {

    public static void main(String args[]) throws Exception {

        final JLabel label = new JLabel("", SwingConstants.CENTER);

        final JFrame frame = new JFrame("Client");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(label, BorderLayout.CENTER);
        final Dimension preferredSize = new Dimension(200, 100);
        frame.setPreferredSize(preferredSize);
        frame.setVisible(true);
        frame.pack();

        final ImageUpdateWorker task = new ImageUpdateWorker(label);
        task.execute();
    }

    public static class ImageUpdateWorker extends SwingWorker<Void, IconInfo> {
        // iconInfoList is not need in your code. It's here so I can
        // supply a dummy set of icons to demonstrate UI updates.
        final List<IconInfo> iconInfoList;
        private JLabel label;

        ImageUpdateWorker(JLabel label) {
            this.label = label;
            // Delete this in your code
            this.iconInfoList = initIconInfoList();
        }

        @Override
        public Void doInBackground() {
            boolean isTrue = true;
            while (isTrue) {
                // Put your socket code to read the next icon from a server.
                // You don't need to do the ImageIO.write(), ImageIO.read() dance,
                // unless you must save the icon to disk. In that case, you don't need
                // to read it back in.

                // Here, I just rotate the iconInfoList to make it
                // appear as though a new icon was received.
                // Your code will not have any need to do this.
                Collections.rotate(iconInfoList, -1);
                // Just publish the icon you create from the image
                // you receive from your remote server.
                publish(iconInfoList.get(0));
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void process(List<IconInfo> icons) {
            // You might check for an empty list.
            // @kleopatra's suggestion to get the last icon is correct.
            // See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/interim.html
            IconInfo iconInfo = icons.get(icons.size() - 1);
            label.setIcon(iconInfo.icon);
            // Your code will not do this 
            label.setText(iconInfo.name);
            // You can get the icon dimensions just from the icon, 
            // so you don't really need the IconInfo class.
            label.setSize(iconInfo.dimension);
        }

        /** Demo code only. It doesn't belong in your working code.
         */
        protected List<IconInfo> initIconInfoList() {
            // Just a quick way to get some icons; don't need to
            // fetch from a server just to demonstrate how to
            // refresh the UI.
            List<IconInfo> iconInfoList = UIManager.getDefaults().keySet().stream()
                .filter(this::isIconKey)
                .map(IconInfo::new)
                .filter(iconInfo -> iconInfo.icon != null)
                .collect(Collectors.toList());

            return iconInfoList;
        }

        /** Demo code only. It doesn't belong in your working code.
         */
        protected boolean isIconKey(Object key) {
            return String.class.isAssignableFrom(key.getClass())
                && ((String) key).toLowerCase().contains("icon");
        }
    }

    /** This is just a convenience to convey 
     * the icon and its UIManager key (i.e., name). 
     * Your remote server doesn't supply a name, 
     * so you don't really need this class.
     * It's just to make the demo more expressive.
     */
    public static class IconInfo {
        final private String name;
        final private Icon icon;
        final private Dimension dimension;

        IconInfo(Object name) {
            this.name = name.toString();
            icon = UIManager.getIcon(name);
            dimension = icon == null
                ? new Dimension(32, 32)
                : new Dimension(icon.getIconWidth(), icon.getIconHeight());
        }
    }

}