Image Slideshow using JFrame - java

I have a problem with a task that seemed to be pretty easy. I have to create a program that will show images (.jpg,.png and .gif) consecutively. Images have to be the contents of some files, that is given as an argument to the program. When I have to load the images separately, it works, but the issue occurs when I load them one after another with a sleep between them.
Here is my code:
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new MyFrame(args[0],Integer.parseInt(args[1]));
//First argument is path to file with images, second - amount of time (in seconds) which every image has to stay on the screen until the next one appears
}
});
}
}
import java.io.File;
import javax.swing.*;
public class MyFrame extends JFrame{
public MyFrame(String path, int time){
super("Obrazki");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
MyPanel panel = new MyPanel();
panel.setVisible(true);
this.add(panel);
pack();
File file = new File(path);
String[] tabs = file.list();
for(int i=0; i<tabs.length; i++)
{
panel.loadImage(path+"\\"+tabs[i]);
this.repaint();
try {
Thread.sleep(time*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import javax.swing.JPanel;
public class MyPanel extends JPanel
{
Image img;
public void loadImage(String s)
{
img = Toolkit.getDefaultToolkit().getImage(s);
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(img, 1);
while(!tracker.checkID(1)) {
try {
tracker.waitForID(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.repaint();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(this.img, 0, 0, this.getSize().width, this.getSize().height, this);
}
}

but the issue is when i load them one after another with sleep time between them.
You are causing the Event Dispatch Thread (EDT) to sleep, which means the GUI can't respond to events or repaint itself. Read the section from the Swing tutorial on Concurrency for more information.
Don't use Thread.sleep() when code is executing on the EDT.
For animation you can:
use a SwingWorker (with Thread.sleep())and publish the Icon you want to paint, or
use a Swing Timer. The tutorial also has a section on How to Use Swing Timers.

Related

Java drawing a circle using awt,JFrame with Thread(Runnable) on Netbeans (Mac)

https://github.com/terryaa/KOSTA_MAC/tree/master/Java/NetBeans/day13_01_15/src/ex1
What I'm trying to do is to draw circles, but one circle on a canvas at a time and then moving on to drawing next circle using Runnable join. It should draw a circle using .start() and the other .start() shouldn't start until formal .start()'s drawing circle is done.
In linked page's package, Ex3_Canvas1 class has main and use Runnable MyThread0 class to draw a circle using basic .start() and .join() and it does perfectly what I want.
I created NetBean's automatic JFrame class Ex2_CanvasDemo and tried to do the same and failed. JFrame window pops up after drawing a full circle and then shows creating of next circle. What I want is that the window should first appear and it shows creation of both circles ,not simulataneously but sequently, like Ex3_Canvas1.
I guess it's because main thread waits for th(Ex2_CanvasDemo) to finish so window doesn't apply for changes. But shouldn't Ex1_Canvas1 should do the same? Is this differences due to automatically generated code by netbeans? How can I do the same as Ex1_Canvas1 in Ex2_CanvasDemo.
I tried making a Runnable class and used in Ex2_CanvasDemo but failed also..
Any help?
I'm using jdk 8 and netbeans8 on mac.
--Thread part of Ex2_CanvasDemo--
public Ex2_CanvasDemo() {
initComponents();
Thread th=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<370;i+=10){
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
arcNUm=i;
System.out.println("circle"+arcNUm);
canvas1.repaint();
}
}
});
th.start();
try {
th.join();
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
th=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<370;i+=10){
System.out.println("circle"+i);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
arcNum2=i;
canvas2.repaint();
}
}
});
th.start();
// try {
// th.join();
// } catch (InterruptedException ex) {
// Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
// }
}
Caveat
Animation is hard, good animation is really hard. You need to repeat this to yourself, because animation done right is, really hard.
What you need to know...
Animation is basically the illusion of change over time. Very rarely do you want to perform linear animation, animation is normally done over a period of time, as it allows for performance difference in the platform to be smoothed out in away which is not as harsh to the user.
Swing is single threaded and not thread safe. This means that you should not block the event dispatching thread and that you must only update the UI from within the context of the event dispatching thread.
See Concurrency in Swing for more details
This makes life a little difficult, as you can't simply run a linear loop in the EDT, as this will block the UI and it's difficult to do it from a Thread because it's a mess of synchronisation.
One of the simplest solutions is to make use of the available API and use a Swing Timer, which acts as a pseudo loop, putting in a small delay between call backs, in which you can perform some operations
Example...
While there are a number of ways you might approach this, I've set up a simple List which contains a bunch of Animatables which "do stuff" and then simply run one after the other in serial.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
TestPane testPane = new TestPane();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(testPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testPane.play();
}
});
}
});
}
public class TestPane extends JPanel {
private List<Animatable> animations;
private Animatable animation;
private Timer timer;
public TestPane() {
animations = new ArrayList<>(25);
animations.add(new CircleAnimation(Color.RED));
animations.add(new CircleAnimation(Color.BLUE));
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (animation == null) {
animation = animations.remove(0);
}
if (animation.update(getBounds())) {
if (animations.isEmpty()) {
((Timer)e.getSource()).stop();
} else {
animation = animations.remove(0);
}
}
repaint();
}
});
}
public void play() {
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (animation != null) {
Graphics2D g2d = (Graphics2D) g.create();
animation.paint(g2d);
g2d.dispose();
}
}
}
public interface Animatable {
public boolean update(Rectangle bounds);
public void paint(Graphics2D g2d);
}
public class CircleAnimation implements Animatable {
private Color color;
private Ellipse2D circle;
private double delta = -1;
public CircleAnimation(Color color) {
this.color = color;
}
#Override
public boolean update(Rectangle bounds) {
if (circle == null) {
circle = new Ellipse2D.Double(bounds.width, (bounds.height / 2) - 10, 20, 20);
}
Rectangle rect = circle.getBounds();
rect.x += delta;
circle.setFrame(rect);
return rect.x + 20 < bounds.x;
}
#Override
public void paint(Graphics2D g2d) {
if (circle == null) {
return;
}
g2d.setColor(color);
g2d.fill(circle);
}
}
}

Java wait code until JFrame keyPressed

I'm using a JFrame and I wanted to display an image and pause the code until the user presses ANY key. After that key being pressed the image would close and the code would continue running.
What I did:
Created a flag
final boolean[] flag = {true};
Added a addKeyListener to the JFrame object that would change the flag
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
flag[0] = false;
}
});
Wait loop until flagged
while (flag[0]){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This is working, but I understand that it is a bit resourceful.
Is there any other way of making the wait loop? Is there any listener of the listener?
2nd try, using CountDownLatch:
Set the latch
final CountDownLatch latch = new CountDownLatch(1);
CountDown
for (JFrame frame : framesList) {
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
latch.countDown();
}
});
Wait
latch.await();
So, you want to display an image and have the execution stop until the window is closed. This just screams modal dialog to me. A modal dialog will stop the code execution from where it is made visible, it will do it in such away so as not to block the Event Dispatching Thread and make your entire problem come to a screaming halt and hang the program. See How to use dialogs for more details...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
BufferedImage img = ImageIO.read(...);
ImageShower.show(null, img);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public static class ImageShower extends JPanel {
private JLabel label = new JLabel();
public ImageShower() {
setLayout(new BorderLayout());
add(label);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
am.put("close", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Window window = SwingUtilities.windowForComponent(ImageShower.this);
if (window != null) {
window.dispose();
}
}
});
}
public void setImage(Image img) {
label.setIcon(new ImageIcon(img));
}
public static void show(Component owner, Image img) {
Window parent = null;
if (owner != null) {
parent = SwingUtilities.windowForComponent(owner);
}
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
Window window = SwingUtilities.windowForComponent(btn);
if (window != null) {
window.dispose();
}
}
});
JDialog dialog = new JDialog(parent, Dialog.ModalityType.APPLICATION_MODAL);
ImageShower shower = new ImageShower();
shower.setImage(img);
dialog.add(shower);
dialog.add(close, BorderLayout.SOUTH);
dialog.getRootPane().setDefaultButton(close);
dialog.pack();
dialog.setLocationRelativeTo(owner);
dialog.setVisible(true);
}
}
}
"But wait, may images are large and take time to load and I don't want to freeze the UI while the load"...
Okay, for that, I'd look towards using a SwingWorker, which can load the image in the background but which provides simple methods for ensuring the the image is displayed within the context of the EDT properly...
public class ImageLoadAndShow extends SwingWorker<Void, Image> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
publish(img);
return null;
}
#Override
protected void process(List<Image> chunks) {
Image img = chunks.get(chunks.size() - 1);
ImageShower.show(null, img);
}
}
Not, if the image fails to load, you won't know about it, as the doInBackground method will pass the Exception out of the method. You'd need to use a combination of a PropertyChangeListener and the SwingWorkers get method to trap it, just remember, get is blocking, so calling it inside the context of the EDT will block until the worker completes
"But I need to carry out other operations when the dialog is closed"
There are a few ways you might be able to achieve this, depending on what it is you want to do, for this example, I've stuck with the SwingWorker, because it was easy to copy and paste the basic structure, but you could use a Runnable wrapped in a Thread
public class ImageLoadShowAndWait extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
ImageShower.show(null, img);
}
});
return null;
}
}
Now, if none of that does what you want...then I'd like to know what it is you're actually doing :P, have a look at Foxtrot which provides an API which allows you to execute code asynchronisly within the EDT without blocking it (entirly), but which will stop the code execution at the point it's called until it completes
The thing is that I wanted it to close the JFrame when ANY key is pressed
KeyListener is going to give you issues, maybe not today, maybe not tomorrow, but it will blow up in your face eventually. The example I've provide binds the Escape key to dispose of the window. It also makes the "Close" button the default button, which provides Space and/or Enter keys as well and a nice visual queue to the user.
If you want to use KeyListener, that's up to you, but your core problem doesn't seem to revolve around it, but the ability to display a window and pause the code execution till it's closed

JFrame doesn't appear with red background

I have strange problem when running my Java program.
It's designed to:
run external app specified in bat file and display fullscreen wallpaper
"hide" wallpaper for some time when buttons combination pressed
warn user that 5sec left so he can save work
when timeout occures display again fullscreen wallpaper and do some other stuff from bats
quit program when button combination pressed
warinng user is realized as displaying fullscreen red box for 200ms
I'm using visible function to do this.
It shows standard fullscreen frame discaring color settings. but only when I comment frame.setUndecorated(true). when uncommented I see only icon in taskbar.
On the other hand when I launch (Using BlueJ) only function visible
the red frame is displayed for specified amount of time. Simply standalone function works perfectly (in my oppinion) even if frame.setUndecorated(true) is used .
what can be wrong that I can't launch that red frame in fullscreen?
olympicApp class:
import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.Graphics;
import java.awt.image.*;
import java.io.*;
import java.io.IOException;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JComponent;
public class olympicApp extends JComponent {
alertApp alert;
BufferedImage img;
public olympicApp()
{
try
{
img = ImageIO.read(new File("wallpaper.jpg"));
}
catch (IOException e)
{
}
}
public void paint(Graphics g)
{
g.drawImage(img, 0, 0, null);
}
public Dimension getPreferredSize()
{
if (img == null)
{
return new Dimension(200,200);
}
else
{
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
public static void visible()
{
JFrame frame = new JFrame();
frame.getContentPane().setBackground(Color.red);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.dispose();
//frame.setUndecorated(true);
frame.setAlwaysOnTop(true);
frame.pack();
frame.setVisible(true);
try
{
frame.setVisible(true);
Thread.sleep(500);
frame.setVisible(false);
}
catch(Exception ex)
{
}
frame.setAlwaysOnTop(false);
frame.setVisible(false);
}
public static void main(String[] args)
{
//alertApp reminder = new alertApp();
try
{
Process process = Runtime.getRuntime().exec("start-lv.bat");
Thread.sleep(500);
}
catch (IOException | InterruptedException e)
{
}
JFrame f = new JFrame("olympic");
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setUndecorated(true);
f.setAlwaysOnTop(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.add(new olympicApp());
f.pack();
f.setVisible(true);
f.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent kevt)
{
if(kevt.getKeyChar()=='l')
{
if(kevt.isAltDown())
{
f.setAlwaysOnTop(false);
f.setVisible(false);
try
{
Thread.sleep(5*1000);
visible();
Thread.sleep(5*1000);
//Process process = Runtime.getRuntime().exec("saving.bat");
Thread.sleep(500);
f.setAlwaysOnTop(true);
f.setVisible(true);
Process process2 = Runtime.getRuntime().exec("kopia.bat");
}
catch(IOException | InterruptedException e)
{
}
}
}
if(kevt.getKeyChar()=='q')
{
if(kevt.isAltDown())
{
System.exit(0);
}
}
}
public void keyTyped(KeyEvent kevt)
{
}
public void keyReleased(KeyEvent kevt)
{
}
});
}
}
I would imagine that you're going to have to do something in here...
public class alertApp {
public static void main(String[] args) {
// Sample loop to flash every 2 seconds
}
}
This is called when your program starts and where you should put the code to get the program running
I would also strogly recommend that you take a look at Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
As well as Concurrency in Swing and How to use Swing Timers which will help you solve other potential issues
I put neccessary code in visible funciton. I can see the frame but color information is discarted. Thanks for Concurrency and Timers, but I think the problem isn't connected to thread.sleep()
Then you didn't read the links...
public void keyPressed(KeyEvent kevt) {
if (kevt.getKeyChar() == 'l') {
if (kevt.isAltDown()) {
f.setAlwaysOnTop(false);
f.setVisible(false);
try {
Thread.sleep(5 * 1000);
visible();
Thread.sleep(5 * 1000);
//Process process = Runtime.getRuntime().exec("saving.bat");
Thread.sleep(500);
f.setAlwaysOnTop(true);
f.setVisible(true);
Process process2 = Runtime.getRuntime().exec("kopia.bat");
} catch (IOException | InterruptedException e) {
}
}
}
keyPressed is executed within the context of the Event Dispatching Thread, so when you call Thread.sleep, it prevents the EDT from processing the Event Queue, preventing it from painting or responding to other events

Why is this JLabel's ImageIcon not updating?

SSCCE, as small as I could get it with keeping all the logic in the same order:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) {
new Test();
}
BufferedImage img = null; // <-- needs this scope
JFrame mainWindow = new JFrame();
JLabel mainImage = new JLabel();
public Test() {
mainWindow.add(mainImage);
mainWindow.setVisible(true);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// step 5
mainImage.addMouseListener(new MouseListener() {
#Override public void mouseClicked(MouseEvent e) {
dostuff();
}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
});
dostuff();
}
private void dostuff() {
// step 1
try {
JFileChooser fc = new JFileChooser();
fc.showOpenDialog(null);
File file = fc.getSelectedFile();
img = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//step 2
mainImage.setIcon(new ImageIcon(img));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
Graphics2D g = img.createGraphics();
g.setColor(new Color(0xFFFF0000));
g.drawOval(10, 10, 10, 10);
try{Thread.sleep(2000);}catch(Exception e){}
// step 3
BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
for (int i = 10 ; i < 20 ; i++) {
for (int j = 10 ; j < 20 ; j++) {
img2.setRGB(i,j,0xFF0000FF);
}
}
// step 4
mainImage.setIcon(new ImageIcon(img2));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
}
}
It should be obvious what I'm trying to do, and compiling will show that it's not doing that. But I want this post to have a question mark so here is the problem description and question:
What I want to happen:
The program loads, and the user is prompted to select a file (an image).
Upon a selecting an image, that image is displayed in a JFrame, and some Graphics2D drawings happen on it. (I included the sleep() because these drawings take a while in the actual program)
When the drawings are finished, a new image is created and it is also drawn on.
The new image replaces the old image in the JFrame
When the user clicks on the image, they are prompted to select a new image, and we repeat from step 1.
What is happening:
Steps one through four work fine. On step five, after selecting an image, the JFrame is populated by a black rectangle the size of the user-selected image, with the image from step three overlayed in the top left corner, step two does not happen, and steps three through five appear to work just fine.
What's more, in my actual program, I can tell that the new user-selected-image is doing its job just fine by the output that step three produces.
So the question is, how can I fix later repetitions of step two such that the appropriate image is shown in the JFrame?
EDIT: This imgur album shows step by step the results I am getting.
http://imgur.com/a/xW051
EDIT2: updated without Thread.sleep()
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) {
new Test();
}
BufferedImage img = null; // <-- needs this scope
JFrame mainWindow = new JFrame();
JLabel mainImage = new JLabel();
public Test() {
mainWindow.add(mainImage);
mainWindow.setVisible(true);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// step 5
mainImage.addMouseListener(new MouseListener() {
#Override public void mouseClicked(MouseEvent e) {
dostuff();
}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
});
dostuff();
}
private void dostuff() {
// step 1
try {
JFileChooser fc = new JFileChooser();
fc.showOpenDialog(null);
File file = fc.getSelectedFile();
img = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//step 2
mainImage.setIcon(new ImageIcon(img));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
Graphics2D g = img.createGraphics();
g.setColor(new Color(0xFFFF0000));
for (int h = 0 ; h < 0xFF ; h++) {
for (int i = 0 ; i < img.getWidth() ; i++) {
for (int j = 0 ; j < img.getHeight()/2 ; j++) {
img.setRGB(i,j,0x88FF0000 + h);
}
}
mainImage.repaint();
}
// step 3
BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
for (int i = 10 ; i < 20 ; i++) {
for (int j = 10 ; j < 20 ; j++) {
img2.setRGB(i,j,0xFF0000FF);
}
}
// step 4
mainImage.setIcon(new ImageIcon(img2));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
}
}
Edit:
If I then load a 1000x1000 image, I see a 1000x1000 black square...
I would guess the black images are because you are causing the Event Dispatch Thread (EDT) to sleep. so although the frame is resizing with the pack() statement the newly loaded image is not being painted.
Don't use Thread.sleep() in code that is executing on the EDT. All code executed from a listener executes on the EDT. Instead you should be using a Swing Timer to schedule the event to update the image of the label.
Edit 2:
Read the section from the Swing tutorial on Concurrency. In general all event code executes on the EDT. When the EDT is blocked, Swing components can't be repainted.
mainImage.setIcon(new ImageIcon(img));
The above statement will cause repaint() to be invoked on the "mainImage" label. The repaint() request is passed to the RepaintManager and a painting request is added to the end of the EDT.
So the label will be painted AFTER all the code in the "doStuff()" method has finished executing.
However you also invoke
mainImage.setIcon(new ImageIcon(img2));
at the end of the method. So by the time the image actually gets painted its Icon has been changed a second time so only the second Icon is painted.
In between those two statements, after reading the image the frame is resized when the pack() method is invoked. I believe this is because the packing of the frame results in OS level painting since a frame is an OS widget not a Swing component. In other words the frame can be resized even if the components on the frame are not repainted.
If you want a responsive GUI then you can't execute long running code on the EDT. In the case of the second SSCCE the "for loop" you added takes a long time
to run which is effectively blocking the EDT and preventing the newly read Icon from being painted.
Therefore long running tasks should be executed on a separate Thread. The concurrency tutorial will explain how you can use a SwingWorker for long running tasks.

display a sequence of images by a thread in java

Here is what I'm trying to do - making long story short:
Building a window (will call him MainWindow) with buttons at top and a picture on the center of it. I want to give the user the options to chose what to do (the buttons) while changing the center picture every couple of sec. Part of the options given to the user is 'pause' and 'resume' - controlling the sequence.
Basically trying to update GUI (MainWindow) by a Thread. This Thread will RUN WHILE boolean 'playSequence' will be true.
Can someone explain why can't I get it to work..
Here is the Code:
package SpecializedControls;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import DataEntities.SgiImage;
public class SgiImagePanel extends JPanel implements Runnable {
private List<SgiImage> seqImageList;
private JLabel lastImage;
private boolean playSequence;
public SgiImagePanel (){}
public SgiImagePanel (List<SgiImage> sequenceList)
{
seqImageList = sequenceList ;
}
#Override
public void run() {
// TODO Auto-generated method stub
while(playSequence)
{
for (SgiImage image : seqImageList)
{
display(image);
try {
Thread.sleep(3000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void display(SgiImage image)
{
reset();
JLabel picLabel = new JLabel(new ImageIcon(image.getImage()));
add(picLabel);
lastImage = picLabel;
}
public void display(List<SgiImage> sequenceList)
{
if(sequenceList==null)
return;
playSequence = true;
SgiImagePanel seq = new SgiImagePanel(sequenceList);
Thread thread = new Thread(seq);
thread.start();
}
public void reset(){
if (lastImage != null)
{
remove(lastImage);
lastImage = null;
}
}
public void pause() {
playSequence = false;
}
public void resume(){
playSequence = true;
}
}
Don't directly use Threads for this. There's no need, and it carries risk if you call code in a background thread that changes Swing state without care.
Use a Swing Timer for your animation loop.
Display your images as ImageIcons in a JLabel.
One of the main problems with your code is that you keep creating a bunch of new JLabels needlessly and dangerously. One JLabel is all you need and all you want. So instead, create the image displaying JLabel just once and then swap icons via its setIcon(...) method.
Read in your image Icon just once, and save it in a variable or collection.
You can easily pause a Swing Timer by simply calling its stop() method, and can restart it just as easily by calling start().
Try to load the image with this code:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel{
private BufferedImage image;
public ImagePanel() {
try {
image = ImageIO.read(new File("image name and path"));
} catch (IOException ex) {
// handle exception...
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null); // see javadoc for more info on the parameters
}
}

Categories

Resources