Question

I am trying to make a thread that reads the screen and displays it in a frame, this code is meant to run at 5fps, so far it reads the screen, but I am having trouble making the JFrame display the updating Image each "frame" or 200 mili-seconds. when I use repaint(); or revalidate();

public static void startScreenRecorder() 
{
    Thread screenThread = new Thread()
    {
        public synchronized void run()
        {
            long time;
            long lastFrameTime = 0;
            JFrame frame = new JFrame("Screen capture");
            ImagePanel panel = new ImagePanel(captureScreen());
            frame.add(panel);
            frame.setSize(300, 400);
            frame.setVisible(true);

            while (true) 
            {
                time = System.currentTimeMillis();
                while (time - lastFrameTime < 190) 
                {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                    }

                    time = System.currentTimeMillis();
                }                
                lastFrameTime = time;

                panel = new ImagePanel(captureScreen());
                panel.revalidate();
                panel.repaint();
                frame.revalidate();
                frame.repaint();
            }
        }
    };
    screenThread.start();
}
Was it helpful?

Solution 2

I wouldn't be surprised if your code shows no images at all since it ignores Swing threading rules:

  • All Swing code needs to be called on the Swing event dispatch thread (EDT) only.
  • All other long-running code needs to be called in a background thread. I assume that this means captureScreen().
  • You should never call Thread.sleep(...) on the Swing event thread unless you want to put your entire application to sleep.
  • Better perhaps to use a Swing Timer.
  • You create new ImagePanels but do nothing with them -- you never add them to the GUI for instance, except for the first JPanel. Note that if you change the object a variable refers to, here the panel variable, this will have absolutely no effect on instances of the object used elsewhere, there the JPanel displayed in the GUI.
  • Rather than create new JPanels, why not instead create ImageIcons with your images and swap a visualized JLabel's Icon with setIcon(...)?
  • Since you have a lot of background stuff going on, consider using a SwingWorker<Void, Icon> to do your work, and have it publish ImageIcons that are then displayed in the GUI's JLabel. If you did this, then you probably wouldn't use a Swing Timer since the timing would be done in the SwingWorker's background thread.

For example:

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

@SuppressWarnings("serial")
public class SwingWorkerEg extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = 400;
   private JLabel displayedLabel = new JLabel();

   public SwingWorkerEg() {
      setLayout(new BorderLayout());
      add(displayedLabel);
      try {
         MySwingWorker mySwingWorker = new MySwingWorker();
         mySwingWorker.execute();
      } catch (AWTException e) {
         e.printStackTrace();
      }
   }

   public void setLabelIcon(Icon icon) {
      displayedLabel.setIcon(icon);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   private class MySwingWorker extends SwingWorker<Void, Icon> {
      private final Rectangle SCREEN_RECT = new Rectangle(0, 0, PREF_W,
            PREF_H);
      private Robot robot = null;

      public MySwingWorker() throws AWTException {
         robot = new Robot();
      }

      @Override
      protected Void doInBackground() throws Exception {
         Timer utilTimer = new Timer();
         TimerTask task = new TimerTask() {

            @Override
            public void run() {
               BufferedImage capturedImage = captureScreen();
               publish(new ImageIcon(capturedImage));
            }

         };
         long delay = 200;
         utilTimer.scheduleAtFixedRate(task, delay, delay);
         return null;
      }

      @Override
      protected void process(List<Icon> chunks) {
         for (Icon icon : chunks) {
            setLabelIcon(icon);
         }
      }

      private BufferedImage captureScreen() {
         BufferedImage img = robot.createScreenCapture(SCREEN_RECT);
         return img;
      }

   }

   private static void createAndShowGui() {
      SwingWorkerEg mainPanel = new SwingWorkerEg();

      JFrame frame = new JFrame("SwingWorker Eg");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

Which would display...

enter image description here

OTHER TIPS

Don't use Thread.sleep() to attempt to control animation.

Animation should be done by using a Swing Timer. When you use a Timer the GUI is automatically updated on the EDT.

panel = new ImagePanel(captureScreen());

The above code doesn't do anything. It just creates a panel in memory. Nowhere to you actually add the panel to the GUI. Changing the reference of a variable does not update the GUI.

Instead you should probably add a JLabel to the frame (when you initially create the frame). Then when you have a new Image you just do:

label.setIcon( new ImageIcon( your screen capture ) );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top