user3646497
user3646497

Reputation: 1

Java GUI: Image will be overwritten, Path the same -> show it in the frame (image still the same)

I want to show a changing image on my frame. The imagepath is always the same, but the image will be getting overwritten every 10 seconds from another program. The problem is that the image is not changing when I overwrite it with another image with the same name. So in my understanding: Compiler looks every look in the path and gets the image -> when the image changed it will be changed on the frame!

I hope you understand my problem and somebody could help me.

    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.GridLayout;
    import java.io.File; 

    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;

    public class GUI extends JFrame{

        public ImageIcon imageBar;

        public JLabel labelimage1;
        private JLabel labelimage2;

        private JLabel bar1 = new JLabel();
        private JLabel bar2 = new JLabel();
        private JLabel bar3 = new JLabel();
        private JLabel bar4 = new JLabel();
        private JLabel bar5 = new JLabel();

        private JButton buttonBar1 = new JButton("1");
        private JButton buttonBar2 = new JButton("2");
        private JButton buttonBar3 = new JButton("3");
        private JButton buttonBar4 = new JButton("4");
        private JButton buttonBar5 = new JButton("5");

        private JPanel panel1 = new JPanel();
        private JPanel panel2 = new JPanel();
        private JPanel panel3 = new JPanel();

        private JFrame window = new JFrame("Interface");

        public GUI(){

            //set the layouts
            panel1.setLayout(new GridLayout(1, 2)); 
            panel2.setLayout(new GridLayout(2, 1));
            panel3.setLayout(new GridLayout(2, 5));

            //place Panel2 and Panel3 in the window
            panel1.add(panel2); 
            panel1.add(panel3);

            //----Panel2
            //refreshImage();


            //----Panel3        
            panel3.add(buttonBar1); //add the bars 1-5 on panel3
            panel3.add(buttonBar2);
            panel3.add(buttonBar3);
            panel3.add(buttonBar4);
            panel3.add(buttonBar5);

            //configure the frame
            window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            window.setVisible(true);
            window.setSize(800, 400);
            window.getContentPane().add(panel1);
        }


        public void refreshImage() {

            panel2.removeAll(); //delete the old panel
            //panel2.repaint();
            //panel2.revalidate()

            DrawImage pan = new DrawImage();

            panel2.add(pan);
            panel2.add(labelimage2);
        }
}


import javax.swing.ImageIcon;
import javax.swing.JPanel;


public class DrawImage extends JPanel implements ActionListener{

    private ImageIcon image;

    public DrawImage(){
        image = new ImageIcon("C:\\Users\\usuario\\Desktop\\image.png");
    }

    protected void paintComponent(Graphics g){
        super.paintComponent(g);
        image.paintIcon(this, g, 50, 50);
        repaint();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }

}

import java.io.File;

public class Main {

    public static void main(String[] args) {

        GUI Interface = new GUI(); 

        while(true)
        {
            Interface.refreshImage(); 
            try {
                        Thread.sleep(5000); //wait for 5000ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
        }

    }

}

Thank you very much!

Upvotes: 0

Views: 350

Answers (2)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

  1. Your while (true) loop risks typing up the Swing event thread locking your program. If it doesn't do that, then you risk unpredictable threading issues by making Swing calls off of the event Thread. These problems can be solved easily by your using a Swing Timer not a while true loop to do your swapping.
  2. Rather than removing and adding components, why not simply display images as ImageIcons within a single non-swapped JLabel.
  3. To swap images here, simply call setIcon(...) on the JLabel.

For an example of using a Swing Timer to swap images, please check out my answer to a similar question here.


For example:

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;

public class TimerImageSwapper {
   public static final String[] IMAGE_URLS = {
         "http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_01.png",
         "http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_02.png",
         "http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_04.png",
         "http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_08.png",
         "http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_05.png",
         "http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_01.png",
         "http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_06.png" };


   private ImageIcon[] icons = new ImageIcon[IMAGE_URLS.length];
   private JLabel mainLabel = new JLabel();

   private int iconIndex = 0;;

   public TimerImageSwapper(int timerDelay) throws IOException {
      for (int i = 0; i < icons.length; i++) {
         URL imgUrl = new URL(IMAGE_URLS[i]);
         BufferedImage image = ImageIO.read(imgUrl);
         icons[i] = new ImageIcon(image);
      }

      mainLabel.setIcon(icons[iconIndex]);

      new Timer(timerDelay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            iconIndex++;
            iconIndex %= IMAGE_URLS.length;
            mainLabel.setIcon(icons[iconIndex]);
         }
      }).start();
   }

   public Component getMainComponent() {
      return mainLabel;
   }

   private static void createAndShowGui() {
      TimerImageSwapper timerImageSwapper;
      try {
         timerImageSwapper = new TimerImageSwapper(5 * 1000);
         JFrame frame = new JFrame("Timer Image Swapper");
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         frame.getContentPane().add(timerImageSwapper.getMainComponent());
         frame.pack();
         frame.setLocationByPlatform(true);
         frame.setVisible(true);

      } catch (IOException e) {
         e.printStackTrace();
         System.exit(-1);
      }

   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Upvotes: 2

MadProgrammer
MadProgrammer

Reputation: 347214

The likely cause is Java is caching the image in memory, associated with the source name. So rather then trying to reload the image again, Java simply returns the cached version.

You could use ImageIcon#getImage#flush to force Java to reconstruct the image

Problems

  • You are calling refreshImage from a Thread other then the Event Dispatching Thread, this could cause issues with the updating of the components and cause rendering artifacts
  • You are forcefully removing the DrawImage pane and adding a new instance, rather the trying to reload the image
  • You're calling repaint within the paintComponent method, don't do this...

You should consider using a Swing Timer, which will allow you to schedule a regular update and be notified within the context of the Event Dispatching Thread.

You could provide a simple refresh method which flushes the current ImageIcon and schedule a repaint of the panel...or you could just use a JLabel and save your self the time

An example of Image#flush

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SlideShow {

    public ImageIcon imageBar;

    public static void main(String[] args) {
        new SlideShow();
    }

    public SlideShow() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                frame.add(new DrawImage());

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class DrawImage extends JPanel {

        private ImageIcon image;

        public DrawImage() {
            image = new ImageIcon("D:\\thumbs\\image.png");
            Timer timer = new Timer(5000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    refresh();
                }
            });
            timer.start();
        }

        public void refresh() {
            image.getImage().flush();
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(image.getImage(), 0, 0, this);
        }

    }
}

The problem with this, is because the image data is loaded in a background thread, it won't may no be available when the component is first repainted, which could make the component appear to flicker.

A better approach would be to use ImageIO.read, which will ensure that the image is fully loaded before the method returns, the draw back here is that could cause the application to "pause" momentary as the image is loaded, personally, I'd use the refresh method to stop the the Timer (or set the Timer to non-repeating), start a background Thread to load the image (using ImageIO.read) call repaint (which is thread safe) and restart the Timer...

Upvotes: 2

Related Questions