Creating new Thread to update JLabel and setIcon() in said separate Thread - java

Trying to get an image from another application sending an array of bytes through socket, translating it to a BufferedImage and setting a JLabel in the GUI that updates every 3 seconds.
I tried looking it up on forums but questions regarding graphical update are recurrent to me and I never seem to get it right -- there's at least 6 update methods in java for graphical interface and every one I tried won't work.
I know the problem isn't in the connection to the client because I can easily save the image I receive with ImageIO.write() and it updates every 3 seconds to the image I was expecting to receive. I just can't have Java updating the JLabel correctly without having to go to forums and ask people. Such a complex task I guess. Here is the code: http://pastebin.com/95nMGLvZ. I am doing this project in Netbeans so there's a lot of stuff in there that is unnecessary to read as it does not directly relate to the problem.
I think the answer lies in creating a separate thread to update my JLabel from the ever-changing BufferedImage that is received from the ObjectInputStream. Anyone mind giving me a hand at this? What is better for my code? SwingWorker, Threading (wtf is setDaemon(flag)?), Runnable, Timer, invokeLater? Tried all of this. Not correctly apparently.
EDIT1:
Tried your answer immibis:
public void startRunning() {
try {
server = new ServerSocket(666, 10);
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
Thread thr = new Thread(new Runnable() {
#Override
public void run() {
try {
input = new ObjectInputStream(connection.getInputStream());
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
});
thr.start();
System.out.println(!connection.isInputShutdown());
while (connection.isConnected()) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
jLabel_screen.setIcon(new ImageIcon(bi));
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
It does not work. It says byte[] byteImage = (byte[]) input.readObject(); line has a NullPointerException. The only value that can be null is the return from readObject(), meaning either the input was not initialized correctly or the connection is not synchronized. I hope it's the first option because I wouldn't know how to handle the last.
EDIT2:
Tried your answer blazetopher:
public void startRunning() throws IOException {
server = new ServerSocket(666, 10);
try {
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
input = new ObjectInputStream(connection.getInputStream());
while (true) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
SwingUtilities.invokeLater(new Runnable() {//<-----------
#Override
public void run() {
jLabel_screen.setIcon(new ImageIcon(bi));
}
});
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (EOFException eofException) {
networkStatus("Connection Closed. :(");
} finally {
input.close();
connection.close();
}
}
Using SwingUtilities.invokeLater didn't work either. At least the program runs and can even save the image but still can't update the JLabel. Am I running out of options here?
EDIT3:
Tried Jordan's code:
#Override
public void paint(Graphics g) {
g.drawImage(biGlobal, 0, 0, null);
}
The GUI kind of crashed and was "drawing" the components just when I had my mouse cursor hovering it. When I started the code, it did not crashed (+1) but it did not draw anything, even when I try to hover the cursor into where the BufferedImage should be painted. Maybe I should add revalidate() or repaint after calling the Overwritten paint(getGraphics()) inside the startRunning() method?
EDIT4: the while(true) that the code is actually in may be the problem but when I use a SwingTimer it gets out of sync with the client and crashes after first cycle. Any alternatives to this?

Generally speaking you have a producer/consumer pattern. Something is producing images and something wants to consume images. Normally, the consumer would wait on the producer to tell it something has been produced, but in this case, we can use a observer pattern instead, having the producer notify the consumer that something was been produced (instead of waiting for it)
We need someway for the producer to communicate with the consumer...
public interface PictureConsumer {
public void newPicture(BufferedImage img);
}
You would create an implementation of this in your UI code, this would then set the icon property of the JLabel
Now, we need something to produce the images...
public class PictureProducer extends SwingWorker<Object, BufferedImage> {
private PictureConsumer consumer;
public PictureProducer(PictureConsumer consumer) {
this.consumer = consumer;
}
#Override
protected void process(List<BufferedImage> chunks) {
// Really only interested in the last image
BufferedImage img = chunks.get(chunks.size() - 1);
consumer.newPicture(img);
}
#Override
protected Object doInBackground() throws Exception {
/*
This whole setup worries me. Why is this program acting as the
server? Why aren't we pooling the image producer?
*/
try (ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(666, 10)) {
try (Socket socket = server.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
// Using `while (true)` is a bad idea, relying on the fact
// that an exception would be thrown when the connection is closed is
// a bad idea.
while (!socket.isClosed()) {
// Generally, I'd discourage using an ObjectInputStream, this
// is just me, but you could just as easily read directly from
// the ByteArrayInputStream...assuming the sender was sending
// the data as a byte stream ;)
byte[] bytes = (byte[]) ois.readObject();
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
BufferedImage img = ImageIO.read(bis);
publish(img);
}
}
}
}
}
return null;
}
#Override
protected void done() {
try {
get();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Image Producer has failed: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
See Worker Threads and SwingWorker for more details
You can a reverse example of this (where some server is producing images and a client is consuming them) here

To update your label, you want to ensure you're using the EDT thread, so use SwingUtilities.invokeLater from the code where you're receiving the BufferedImage (which would ideally be in a separate "worker" thread:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Update your label here
}
});

What is it you are attempting to accomplish.there might be a better way all together.
For example replace the JLabel with a JPanel then use that JPanel paint method
#Override
public void paint(Graphics g) {
g.drawImage(Img, xcord, ycord, null);
}
Then make that JPanel implement runnable and do your updates in that run method.
JPanel class
public class GraphicsPanel extends JPanel{
private BufferedImage img;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D G2D = (Graphics2D) g;
G2D.drawImage(img, 0, 0, null);
}
public void setImg(BufferedImage img) {
this.img = img;
}
}
then make sure this panel is visible from wherever you wish to call its methods.
this will look something like this
GraphicsPanel graphicsPanel = new GraphicsPanel();
boolean running;
BufferedImage srcImage;
public void run(){
while(running){
graphicsPanel.setImg(srcImage);
graphicsPanel.repaint();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Related

Needing JAVA help in animation

I'm new in Java (still learning) all the other works have gone well but only this animation gives my headache and the coffee won't even help=(!
I should make an animation of Javaman (10 gif pictures named as T1, T2,...T10) I should use Thread, MediaTracker-class and addImage-method. Then I should specify the speed of the animation by using sleep-method (I used join-method if that's right??).
(MY JAVA CODE GOES LIKE THIS)
import java.applet.Applet;
import java.awt.*;
public class Animaatio extends Applet implements Runnable {
Image[] images = null;
MediaTracker tracker = null;
int current = 0;
Thread animThread;
#Override
public void init() {
// Creating a new media tracker, to track loading images
tracker = new MediaTracker(this);
// Creating an array of ten images
images = new Image[10];
// Downloading the images
for (int i = 0; i < 10; i++) {
// Loading the images
images[i] = getImage(getCodeBase(), (i + 1) + "T.gif");
tracker.addImage(images[i], 0);
}
try {
tracker.waitForAll();
} catch (InterruptedException e) {
}
}
#Override
public void start() {
if (animThread == null) {
animThread = new Thread(this);
animThread.start();
}
try {
animThread.join();
} catch (InterruptedException e) {
}
}
#Override
public void paint(Graphics g) {
g.drawImage(images[current++], 0, 0, this);
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
}
}
The problem is that I don't see any animation just an empty applet viewer which just keeps running. Problem might be caused if the images are storaged in the wrong place? If someone could wreally help me, I would be really thankful for my knight=).

`Graphics.drawImage()` won't draw

Very simply, I could not draw this image.
public class RenderMap extends JPanel {
static BufferedImage brick;
static BufferedImage groundb;
public static void main(String[] args) {
JFrame window = new JFrame("Super Mario");
RenderMap content = new RenderMap();
window.setContentPane(content);
window.setBackground(Color.WHITE);
window.setSize(1200, 800);
window.setLocation(100,0);
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
window.setResizable(false);
window.setVisible(true);
try {
brick = ImageIO.read(new File("SuperMario/brick.png"));
} catch (IOException e) {
}
try {
URL url = new URL("SuperMario/brick.png");
brick = ImageIO.read(url);
} catch (IOException e) {
}
try {
groundb = ImageIO.read(new File("SuperMario/ground.png"));
} catch (IOException e) {
}
try {
URL url = new URL("SuperMario/ground.png");
groundb = ImageIO.read(url);
} catch (IOException e) {
}
}
public Ground ground;
public RenderMap() {}
public void paintComponent(Graphics g) {
if(ground == null) {
ground = new Ground();
}
ground.draw(g);
}
public class Ground implements ImageObserver {
Ground(){}
void draw(Graphics g) {
g.drawImage(groundb, 0, 0, 1200, 800, this);
g.fillOval( 8, 8, 16, 16);
}
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
// TODO Auto-generated method stub
return false;
}
}
}
This draws the oval, but not the image. I tried changing to another image(that is functioning in another program) and it still won't work, so I know it's not the image's problem.
As suspected, the images are not being loaded properly.
java.net.MalformedURLException: no protocol: SuperMario/brick.png
at java.net.URL.<init>(URL.java:586)
at java.net.URL.<init>(URL.java:483)
at java.net.URL.<init>(URL.java:432)
at RenderMap.main(RenderMap.java:40)
java.net.MalformedURLException: no protocol: SuperMario/ground.png
at java.net.URL.<init>(URL.java:586)
at java.net.URL.<init>(URL.java:483)
at java.net.URL.<init>(URL.java:432)
at RenderMap.main(RenderMap.java:51)
It's failing at the load-by-URL function calls because you passed in an invalid URL. Correct URL format should be like how you access them from a web browser, like: http://someplace.com/SuperMarioFolder/brick.png
To load the images, choose only one way to read them. Either by:
URL - Remove the File blocks and make the file accessible via full URL.
try {
URL url = new URL("http://www.proper-url.com/SuperMario/ground.png");
groundb = ImageIO.read(url);
} catch (IOException e) { }
File - Remove the URL blocks. This will only allow the program to access local files.
try {
groundb = ImageIO.read(new File("SuperMario/ground.png"));
} catch (IOException e) {
}
For your project's purpose, I believe option#2 would suffice.
Moving forward, you may want to use an IDE's debugging feature to go through code execution step-by-step. If you're not using one, you may want to IntelliJ and Eclipse.

Java SWT updating canvas from another thread

I'm developing a Java application that does basically:
Starts a server in a new thread. This server opens a socket connection and waits for messages.
Creates a Shell with a Canvas (in the main thread) and whenever server receives a message, I need to draw a line using the points contained in it.
I've already read some threads about this issue (on updating SWT from another thread) and I've tried to create a method called by my server whenever it receives a message:
public static void doPaint(final Canvas canvas, final Draw draw){
if (canvas != null && draw != null){
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
canvas.addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
System.out.println("Printing line....");
e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_WHITE));
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_BLACK));
e.gc.setLineWidth(3);
e.gc.drawLine((int)draw.getStartX(),(int)draw.getEndX(),(int)draw.getStartY(),(int)draw.getEndY());
}
});
}
});
}
}
EDIT
This is the loop that waits for socket messages and triggers the actions:
while(isConnected()){
try{
System.out.println("Waiting for connection...");
Socket server = getServerSocket().accept();
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream(), Const.ENC_UTF8));
String inputLine;
while(((inputLine = in.readLine()) != null)){
processInput(inputLine, canvas);
}
}catch(IOException ioex){
System.out.println(ioex.getMessage());
ioex.printStackTrace();
}catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
and my processInput method is:
protected void processInput(String inputLine, Canvas canvas){
//get draw from input stream
Draw draw = DrawUtil.getDrawFromInput(inputLine);
//draw points on live-mode
BBCanvas.doPaint(canvas, draw);
//append draw node to XML file
DrawUtil.writeDrawToFile(draw);
}
... but the runnable in the doPaint method is never executed. Any hints?
EDIT 2
This is the code that creates the main shell:
public MainWindow(BBServer server) {
this.server = server;
Display display = new Display();
this.mainShell = createShell(display);
getMainShell().open();
while (!getMainShell().isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}

Why does calling the main method of a different class not work correctly?

I'm in the process of writing a game. The game plays background music while it is running. This works fine, and I've decided to add a main menu, as their are three types of this game:
Single Player
Two Player
Online
When I run these classes individually (which have their own main methods - obviously), they work perfectly fine. However, in my Welcome Menu class, which is responsible for the main menu (all necessary imports are there, just not shown here):
public class WelcomeMenu implements ActionListener {
public void setButtonBG(JButton button, String imgPath) throws IOException //this method is reponsible for setting images to their corresponding JButton(s)
{
BufferedImage img = ImageIO.read(ClassLoader.getSystemResource(imgPath));
ImageIcon sp = new ImageIcon(img);
button.setIcon(sp);
button.setBorderPainted(false);
}
private JFrame welcomeWindow = new JFrame("Tic-Tac-Toe");
private JButton singlePlayerButton = new JButton();
private JButton twoPlayerButton = new JButton();
private JButton onlineButton = new JButton();
public WelcomeMenu() throws IOException
{
//START OF CONSTRUCTOR
//Main window is being sized, default way to close, and internal layout
welcomeWindow.setSize(600, 420);
welcomeWindow.setLayout(new CardLayout());
//Object res = this.getClass().getResource("/");
//System.out.println(res);
BufferedImage bf = ImageIO.read(ClassLoader.getSystemResource("images/mainMenuBG.jpg"));
welcomeWindow.setContentPane(new backImage(bf)); // adding created component to the JFrame using the backImage class
welcomeWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
welcomeWindow.setLocationRelativeTo(null);
welcomeWindow.setResizable(false);
welcomeWindow.setVisible(true);
//setting the icon
try
{
java.net.URL url = ClassLoader.getSystemResource("images/icon.png");
Toolkit kit = Toolkit.getDefaultToolkit();
Image img = kit.createImage(url);
welcomeWindow.setIconImage(img);
}
catch(NullPointerException n)
{
System.out.println("Image could not be fetched.");
}
//adding custom buttons
//ImageIcon singlePlayer = new ImageIcon("images/singlePlayerButton.jpg");
//setting sizes
singlePlayerButton.setSize(387, 72);
twoPlayerButton.setSize(387, 72);
onlineButton.setSize(387, 72);
//setting background images to buttons
setButtonBG(singlePlayerButton, "images/sPlayerButton.jpg");
setButtonBG(twoPlayerButton, "images/tPlayerButton.jpg");
setButtonBG(onlineButton, "images/mPlayerButton.jpg");
//adding listeners
singlePlayerButton.addActionListener(this);
twoPlayerButton.addActionListener(this);
onlineButton.addActionListener(this);
//adding the custom buttons
welcomeWindow.add(singlePlayerButton);
welcomeWindow.add(twoPlayerButton);
welcomeWindow.add(onlineButton);
//setting locations and visibility
singlePlayerButton.setLocation(110, 90);
singlePlayerButton.setVisible(true);
twoPlayerButton.setLocation(110, 182);
twoPlayerButton.setVisible(true);
onlineButton.setLocation(110, 274);
onlineButton.setVisible(true);
//END OF CONSTRUCTOR
}
public static TicTacToeTP spg;
//All actions are done here
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == singlePlayerButton)
{
System.out.println("<LOG> SINGLE PLAYER GAME REQUESTED");
JOptionPane.showMessageDialog(welcomeWindow, "This game mode has not been implemented yet.");
}
if(e.getSource() == twoPlayerButton)
{
System.out.println("<LOG> TWO PLAYER GAME REQUESTED");
try
{
//spg = new TicTacToeTP("images/black-squareMod_RED.jpg");
//spg.playBackgroundSong();
TicTacToeTP.main(null);
}
catch(IOException io)
{
System.out.println("IO EXCEPTION!");
}
welcomeWindow.setVisible(false);
welcomeWindow.dispose();
}
if(e.getSource() == onlineButton)
{
System.out.println("<LOG> ONLINE GAME REQUESTED");
JOptionPane.showMessageDialog(welcomeWindow, "This game mode has not been implemented yet.");
}
}
public static void main(String[] args) throws IOException
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex)
{
}
}
});
new WelcomeMenu();
}
}
... if I click the Two Player button, for example, it plays the audio ONLY. None of my other components load. Just an empty JFrame. Notice how in the actionPerformed() method, I tried both TicTacToeTP.main(null) and (commented out, now) instantiating a new TicTacToeTP object AND calling the playBackgroundSong() method. If I eliminate this methods call, and just instantiate the object, it works fine - but no music.
Why is this happening, and how can I fix it?
Here is the playBackgroundSong() method:
private Player p = null;
//private InputStream fis = null;
public void playBackgroundSong() //responsible for playing background music
{
//PausablePlayer p = null;
InputStream fis = null;
ArrayList<InputStream> stream = new ArrayList<InputStream>(); //this ArrayList contains multiple audio files that the method will loop through >> defined below
stream.add(ClassLoader.getSystemResourceAsStream("resources/01 Intro.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Basic space - The XX - Instrumental.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Mirrors [ Upbeat Electronic Instrumental ] Spence Mills HQ Free Beat Download 2012.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Static [ Aggressive Dark Pop Hip Hop Rap Instrumental ] Spence Mills Free Beat Download Link 2012 HD.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/System Shock 2 soundtrack Med Sci 1.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/System Shock 2 Soundtrack Ops 2.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/01 Intro.mp3"));
Collections.shuffle(stream);
for(int i = 0; i < stream.size(); i++)
{
try
{
fis = stream.get(i);
}
catch (NullPointerException ex)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ex);
}
try
{
p = new Player(fis);
}
catch (JavaLayerException ex)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ex);
}
try
{
p.play();
}
catch (JavaLayerException ee)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ee);
}
}
playBackgroundSong();
}
You appear to be playing a long-running bit of code, playBackgroundSong(), on the Swing event dispatch thread or EDT. This thread is responsible for painting the GUI and interacting and responding to user input, and if it gets tied up, the program essentially freezes. This might not have been an issue when you called this method in the main method -- basically off of the Swing event thread, but is an issue when it is specifically called on the event dispatch thread. A possible solution: play your music in a background thread. A SwingWorker might work well for you, and there are decent tutorials on the use of these and the EDT. Google "Concurrency in Swing", and check out what will likely be the first hit for more.
As an aside: you usually don't want to call another class's main method. Instead create an instance of the other class and use it.
Edit You state:
Thanks. Looking at this part: docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html Seems to explain what I want to do, correct? I am reading it all, by the way
Actually you could go even simpler. Since you're not waiting for a result from your playBackgroundSong(), you could possibly just call it in its own simple thread by just wrapping it in a Runnable and then putting that in a Thread:
new Thread(new Runnable() {
public void run() {
playBackgroundSong();
}
}).start();

Swing invokeLater never shows up, invokeAndWait throws error. What can I do?

I have this code:
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
dialog.handleDownload();
} catch (IOException io) {
io.printStackTrace();
}
}
});
} catch(Exception io) { io.printStackTrace(); }
in the handleDownload I'm reading an inputstream, calculating a progress bar's value, and setting it to that. So, when I click a button, a new JFrame opens up and does all the stuff I wrote above.
If I have the dialog.handleDownload by itself ( in no SwingUtilities method ), it freezes until the operation is finished. If I add it in a invokeLater it's closed very fast ( I can't see anything, and the operation is not finished ). If I add it in a invokeAndWait I get the invokeAndWait cannot be called from the event dispatcher thread error. What should I do?
It looks like you could make use of SwingWorker. This allows you to defer an expensive operation to a background thread (keeping your GUI responsive) and when the operation is finished, do some stuff to the GUI.
Edit: Example
Here's a bit more complex example that shows how to use the basics of SwingWorker but also how to publish/process intermediate results.
public static void main(String[] args) {
final int SIZE = 1024*1024; //1 MiB
//simulates downloading a 1 MiB file
final InputStream in = new InputStream() {
int read = 0;
public int read() throws IOException {
if ( read == SIZE ) {
return -1;
} else {
if ( read % 200 == 0 ) {
try { Thread.sleep(1); } catch ( InterruptedException e ) {}
}
read++;
return 5;
}
}
};
final JProgressBar progress = new JProgressBar(0, SIZE);
final JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button.setText("Working...");
SwingWorker<byte[], Integer> worker = new SwingWorker<byte[], Integer>() {
#Override
protected byte[] doInBackground() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
for ( int read = -1; (read = in.read(buff)) != -1; ) {
baos.write(buff, 0, read);
publish(read);
}
return baos.toByteArray();
}
#Override
protected void process(List<Integer> chunks) {
int total = 0;
for ( Integer amtRead : chunks ) {
total += amtRead;
}
progress.setValue(progress.getValue() + total);
}
#Override
protected void done() {
try {
byte[] data = get();
button.setText("Read " + data.length + " bytes");
} catch (Exception e) {
e.printStackTrace();
}
}
};
worker.execute();
}
});
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(button, BorderLayout.NORTH);
frame.add(progress, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); frame.setVisible(true);
}
Edit: Changed the example to drive a progress bar as if a download were taking place.
If you are doing that in response to a button click, you are already in the event thread so invokeAndWait is actually going in the wrong direction.
You need to start a new thread to execute the handleDownload thread that is NOT a the event dispatch thread--BUT
When running in your new thread, be sure that any GUI updates use invokeAndWait or preferably invokeLater to get back to the EDT.
The simple rules to remember:
Any thread handed to you by Swing is the EDT, so do all the GUI stuff on it you want
Do ALL updates of GUI elements on the EDT (ONLY).
Do anything that takes a long time on a non-EDT thread (Start a new thread).
Use invokeLater to get back to the EDT from a non-EDT thread
You shouldn't be accessing your inputStream in the event thread. Spawn a new thread which actually does the bulk of the handleDownload() work, then make the last operation performed by that thread be to call SwingUtilities.invokeLater() with the code that actually shows and populates the dialog.
What does "handleDownload" do? Time consuming things should not be done in the event dispatcher thread. If something is consuming lots of CPU cycles in the event dispatcher thread, then the display will freeze until it's done. You are far better off in a case like that invoking a normal thread (not using SwingUtilities) to do the processing outside of the event dispatcher thread, and in that thread using SwingUtilities.invokeLater to send back notifications that things have changed (like updating a progress bar) at regular intervals.
What it sounds like you need is a SwingWorker. This will allow you to have the file download take place in a separate thread that doesn't bother the EDT.
Your code would look something like this:
class Downloader extends SwingWorker<String, Void> {
#Override
public String doInBackground() {
dialog.handleDownload();
return "done";
}
#Override
protected void done() {
try {
someLabel.setText(get());
} catch (Exception ignore) {
}
}
}

Categories