All these days I've been trying to access a Swing component (e.g.: JLabel) from a separate thread. I tried using "Thread", "Timer", "SwingWorker" but I couldn't find the solution.
- Using a thread results in stack overflow.
- Using a timer doesn't access the Swing component (e.g.: change the text property of a JLabel)
- Using a SwingWorker I could not do an infinite loop cause it seems to me that it's method must return a value so it cannot work infinite (cannot be "void").

Important note: the thread must not "freeze" the visual interface while looping.

Hope I was clear enough so that someone can help :).

I have a background operation which runs infinitely. I want to set JLabel's text from that thread as it would be the status of the operations in that thread or the progress? Did I miss something in the description?

I have a background operation which runs infinitely. I want to set JLabel's text from that thread as it would be the status of the operations in that thread or the progress?

final JLabel label = <reference to the label>;
final String text = <text to change to>;
SwingUtilities.invokeLater(
        new Runnable() {
            label.setText(text);
            label.validate();
            label.repaint();
        }
    );

Did I miss something in the description?

Yes, you missed stating what you wanted to do. As I said "access a component" means, essentially, nothing. It is what you want to do with it that makes a difference.

Edited 7 Years Ago by masijade: n/a

A mistake that is completely self correctable

final JLabel label = <reference to the label>;
final String text = <text to change to>;
SwingUtilities.invokeLater(
        new Runnable() {
            public void run() {
                label.setText(text);
                label.validate();
                label.repaint();
            }
        }
    );

@masijade,

Sorry, it's freezing the app and the status doesn't change. I'm new to threading in java, I might have not understood well.

ACTUALLY i try to check over and over if a the driveletter changed if I plug in the usb stick. It works in console (separate thread) but not with swing. So all I want is to continuosly change the status JLabel (called 'lbl_status') to see if the Drive Letter changes. That's my application.

public class detectUSB extends javax.swing.JFrame {
  
    public String status="..";


 
    public detectUSB() {
        initComponents();

        

        final JLabel label = lbl_status;
        final String text = this.status;

        SwingUtilities.invokeLater(
                new Runnable() {

            public void run() {

// loop indefinitely

    String[] letters = new String[]{ "A", "B", "C", "D", "E", "F", "G", "H", "I","J","K","L","M"};
    File[] drives = new File[letters.length];
    boolean[] isDrive = new boolean[letters.length];

    // init the file objects and the initial drive state
    for ( int i = 0; i < letters.length; ++i )
        {
        drives[i] = new File(letters[i]+":/");

        isDrive[i] = drives[i].canRead();
        }

     status="FindDrive: waiting for devices...";

        // check each drive
        for ( int i = 0; i < letters.length; ++i )
            {
            boolean pluggedIn = drives[i].canRead();

            // if the state has changed output a message
            if ( pluggedIn != isDrive[i] )
                {
                if ( pluggedIn )
                    status="Drive "+letters[i]+" has been plugged in";
                else
                    status="Drive "+letters[i]+" has been unplugged";

                isDrive[i] = pluggedIn;
                }

        try { Thread.sleep(100); }
        catch (InterruptedException e) {  }
            }
                              
        label.setText(text);
        label.validate();
        label.repaint();

     }
     
        
        }
    );


    }
}

I tried all day... however thanks for interest in helping me :)

Edited 7 Years Ago by Clawsy: n/a

Because I never said to put the entire thing into "invokeLater". InvokeLater should be used entirely as, and contain nothing more, than shown there. The rest of that should be running in another thread that is started alongside the rest of the application and simply given a reference to the label that it is to update so that it can invoke it in the call to SwingUtilities.

I assume you are starting a GUI out of your main method? If so, do that and keep a reference to this "status label", then start another thread with this stuff in it, passing this "status label" in the constructor, and at the spot where the label is to be updated, call invokeLater.

E.G.

class Checker extends java.util.TimerTask {
  private JLabel label;
  Checker(JLabel label) {
    this.label = label;
  }
  public void run() {
    //check the drives
    final String text = <status>;
    // call invokeLater
  }
}

public class myApp {
  public static JLabel createAndStartGui() {
    JLabel sLabel = new JLabel();
    // create the GUI, set it visible using the above reference
    return sLabel;
  }
  public static void main(String args) {
    JLabel l = createAndStartGui();
    java.util.Timer t = new java.util.Timer(true);  // start as Daemon
    TimerTask tt = new Checker(l);
    t.scheduleAtFixedRate(tt, 0, 100);  // do you really need to check 10 times per second
  }
}

I designed the application in NetBeans visually (drag-drop). I tried also with the thread. Nothing freezes but the JLabel doesn't update even if the threads works fine in the background. Below it's all the code for the main application class and the thread class.

//the main class

import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingUtilities.*;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * detectUSB.java
 *
 * Created on Oct 14, 2009, 9:09:45 PM
 */


public class detectUSB extends javax.swing.JFrame {
  
  

    usbThread thread = new usbThread();
   
    public detectUSB() {
        initComponents();

        thread.start();
        

        final JLabel label = lbl_status;
        final String text = thread.getThreadStatus();

        SwingUtilities.invokeLater(
                new Runnable() {

            public void run() {


        label.setText(text);
        label.validate();
        label.repaint();


     }
              
        }
    );


 }

  

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        lbl_status = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        jButton1.setText("jButton1");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jLabel1.setText("STATUS:");

        lbl_status.setText("No card connected...");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(lbl_status))
                    .addComponent(jButton1))
                .addContainerGap(240, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jButton1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 241, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(lbl_status))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>

    private void formWindowOpened(java.awt.event.WindowEvent evt) {                                  
        // TODO add your handling code here:

      
    }                                 
// THIS IS A BUTTON TO CHECK THE THREAD. THE THREAD WORKS FINE.
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        lbl_status.setText(thread.getThreadStatus());
    }                                        

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new detectUSB().setVisible(true);

            }
        });
    }

    // Variables declaration - do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel lbl_status;
    // End of variables declaration

}

THREAD CLASS:

//the thread class
import java.io.File;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


public class usbThread extends Thread{

    public String status=".....";
   

    usbThread()
    {
      
    }

    String getThreadStatus()
    { 
      return this.status;
    }

    @Override
         public void run() {

    String[] letters = new String[]{ "A", "B", "C", "D", "E", "F", "G", "H", "I","J","K","L","M"};
    File[] drives = new File[letters.length];
    boolean[] isDrive = new boolean[letters.length];

    // init the file objects and the initial drive state
    for ( int i = 0; i < letters.length; ++i )
        {
        drives[i] = new File(letters[i]+":/");

        isDrive[i] = drives[i].canRead();
        }

     this.status="FindDrive: waiting for devices...";

     // loop indefinitely
     while(true)
        {
      

        // check each drive
        for ( int i = 0; i < letters.length; ++i )
            {
            boolean pluggedIn = drives[i].canRead();

            // if the state has changed output a message
            if ( pluggedIn != isDrive[i] )
                {
                if ( pluggedIn )
                    this.status="Drive "+letters[i]+" has been plugged in";
                else
                    this.status="Drive "+letters[i]+" has been unplugged";

                isDrive[i] = pluggedIn;
                }

        try { Thread.sleep(100); }
        catch (InterruptedException e) { /* do nothing */ }
            }


     }
         }


}

Change this

usbThread thread = new usbThread();

    public detectUSB() {
        initComponents();

        thread.start();
        

        final JLabel label = lbl_status;
        final String text = thread.getThreadStatus();

        SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        label.setText(text);
                        label.validate();
                        label.repaint();
                    }
                }
            );
    }

to this

public detectUSB() {
        initComponents();
    }

and change this

public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new detectUSB().setVisible(true);
            }
        });
    }

to this

public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new detectUSB().setVisible(true);
            }
        });
        new usbThread(lbl_status).start();
    }

and change this

public class usbThread extends Thread{
    public String status=".....";
    usbThread()
    {
      
    }

    String getThreadStatus()
    { 
      return this.status;
    }

    @Override
    public void run() {
        ....
        this.status="FindDrive: waiting for devices...";
            ....
                this.status="Drive "+letters[i]+" has been plugged in";
            else
                this.status="Drive "+letters[i]+" has been unplugged";
                ....
            }
            try { Thread.sleep(100); }
            catch (InterruptedException e) { /* do nothing */ }
        }
    }

to this

public class usbThread extends Thread{
    private String status = ".....";
    private JLabel label;
    usbThread(JLabel label) {
        this.label = label;
    }

    private void postStatus() {
        SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        label.setText(status);
                        label.validate();
                        label.repaint();
                    }
                }
            );
    }

    @Override
    public void run() {
        ....
        this.status="FindDrive: waiting for devices...";
        postStatus();
            ....
                this.status="Drive "+letters[i]+" has been plugged in";
            else
                this.status="Drive "+letters[i]+" has been unplugged";
                ....
            }
            postStatus();
            try { Thread.sleep(100); }
            catch (InterruptedException e) { /* do nothing */ }
        }
    }

Edited 7 Years Ago by masijade: n/a

Comments
Patience of a Saint!

Thefact is the plaggedIn variable and isDrivev are always equal so you the code inside the for bloc is never executed:
if you try this:
if(drives.canRead()) {
this.status.............
}else{
this.status.......
}

THANK YOU SO MUCH FOR SOLVING MY PROBLEM. I guess not only me was searching for this. :)
One note for the master :) : when said to add "new usbThread(lbl_status).start();" to the main function. You cannot cause main is static (you get build error). So I just started it after " initComponents();".

THANK YOU SO MUCH!

Sorry, brainfart.

Starting after initComponents (if done within the SwingUtilities) is bad as you are kicking it off from the Swing Event Thread, and you should, really, avoid that, if possible. Try it this way. Remove the "detectUSB" constructor and then change the main as follows:

public static void main(String args[]) {
        detectUSB dusb = new detectUSB();
        SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    dusb.initComponents().setVisible(true);
                }
            });
        new usbThread(dusb.lbl_status).start();
    }

And, in any case, change the usbThread constructor as follows:

usbThread(JLabel label) {
        this.label = label;
        setDaemon(true);
    }

To ensure that the thread dies properly.

Edit: And, not intending to nitpick, it is standard Java programming convention to capitalise Classes so those class names should be DetectUSB and USBThread.

Edited 7 Years Ago by masijade: n/a

Yes. Thank you :). These are really good hints and valuable advices. (but I put " new usbThread(lbl_status).start();" in formWindowOpened event cause... again: Builder said 'non-static variable lbl_status cannot be referenced from a static context'... sorry :P). Thanks. Now I even optimized my application.

This question has already been answered. Start a new discussion instead.