-1

The purpose of my program is to do time-consuming file operations that are executed inside a for loop. That's why I would like the program to show the current operation through a text field. I don't know how to explain it in a simpler way. I can't post the whole program because it would be too long.

I can't find a way to update a JTextField field while a loop is running inside a method.

For example, in a loop like:

void Test() {
    ArrayList<String> asList = new ArrayList<>();
    // Load asList.
    for (int jj=0; jj < asList.size(); jj++) {
      // Miscellaneous instructions working on files.
      TxtLog.setText(jj + " " + asList.get(jj));
    }
}

The TxtLog field is never updated.

How do I get it to update while running the for loop?

EDIT: I solved with SwingWorker associated with a Thread.sleep!

19
  • 5
    Use javax.swing.Timer and give Swing a chance to update the display before setting the new text. Don’t use a loop for such task. Commented Oct 20 at 11:47
  • 1
    Hello ZioCrick, welcome, you have two problems. The first is that a for loop executes in a time that is instantaneous to human perception, so if the list contains: { “a”, ‘b’, “c” }, even though TxtLog will receive and display all the letters, you will only see the last one. On the other hand, you will probably need to call TxtLog.repaint() to update its display (always assuming that asList is not empty) Commented Oct 20 at 12:08
  • 4
    Do you want the JTextField to display the result of the Miscellaneous instructions working on files at the end of each loop iteration? If yes, then I think you also need to give the user time to read that result, no? Or does each miscellaneous instruction take a long time to complete? By the way, I suggest that you take the tour and read How to Ask. Commented Oct 20 at 12:13
  • 3
    If the text field is never updated, then: 1) you have no entries in the ArrayList or 2) you don't have a reference to the text field that is visible on the frame. Post a proper minimal reproducible example to demonstrate the problem. Commented Oct 20 at 14:06
  • 2
    GUI components should be manipulated only on the Event Dispatch Thread. If your Test() method is not already running there (and it probably shouldn't be) then you can use SwingUtilities.invokeAndWait() to perform the update on the EDT and delay continuing until that has completed. Commented Oct 20 at 14:56

2 Answers 2

0

As I mentioned in the comments, using Thread.sleep() causes the user interface to “freeze” for the duration of that call, so it is always advisable to use a Timer.

void test() {

     // just to test the code 
   TxtLog = new JTextField();
   JFrame xxxx = new JFrame();
   xxxx.setBounds( 100, 100, 400, 400 );
   TxtLog.setBounds( 100, 100, 400, 400 );
   xxxx.add( TxtLog );
   xxxx.setVisible( true );
     // end

     // replace "Miscellaneous instructions working on files" to test
   List<String> asList = List.of( "Hello", "world", "Byebye baby" );

     // we instantiate our *Timer* passing it the time between
     // different executions as a parameter.
   timer = new Timer( 2000, ( e ) -> {
      TxtLog.setText( jj + " -> " + asList.get( jj ) );

        // we verify that the index does not exceed the capacity of our
        // list. When it does, we stop the *Timer*.
      if( ++jj == asList.size() ) {
         timer.stop();
      }
   } );

     // we set the time it takes for the *Timer* to start. If we do not
     // specify it, this parameter will automatically be set to the
     // value between executions (in our case, “2000”).
   timer.setInitialDelay( 0 );
   timer.start();
}
Sign up to request clarification or add additional context in comments.

Comments

0

Ok Marce Puente, I will try also the timer.

However, the example that solved the problem for me is the following.

package SwingworkerSample;
// Java program to illustrate working of SwingWorker.
import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.util.List; 
import java.util.concurrent.ExecutionException; 
import javax.swing.*; 
 
public class SwingWorkerSample { 
  private static JLabel statusLabel; 
  private static JFrame mainFrame; 
 
  public static void swingWorkerSample () {
    mainFrame = new JFrame("Swing Worker"); 
    mainFrame.setSize(400, 400); 
    mainFrame.setLayout(new GridLayout(2,1)); 
    mainFrame.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent e) { 
        System.exit(0); 
      } 
    }); 
    statusLabel = new JLabel("Not Completed", JLabel.CENTER); 
    mainFrame.add(statusLabel); 
    JButton btn = new JButton("Start counter"); 
    btn.setPreferredSize(new Dimension(5,5)); 
    btn.addActionListener(new ActionListener() { 
      @Override
      public void actionPerformed(ActionEvent e) { 
        System.out.println("Button clicked, thread started"); 
        startThread();  
      } 
    }); 
    mainFrame.add(btn); 
    mainFrame.setVisible(true); 
  }
  private static void startThread() { 
    SwingWorker sw1 = new SwingWorker() {
      @Override
      protected String doInBackground() throws Exception {
        // define what thread will do here 
        for ( int i=10; i>=0; i-- ) { 
          Thread.sleep(1000); 
          System.out.println("Value in thread : " + i); 
          publish(i); 
        } 
        String res = "Finished Execution"; 
        return res; 
      }
      @Override
      protected void process(List chunks) { 
        // define what the event dispatch thread  
        // will do with the intermediate results received 
        // while the thread is executing 
        int val = (int) chunks.get(chunks.size()-1); 
        statusLabel.setText(String.valueOf(val)); 
      } 
      @Override
      protected void done() { 
        // this method is called when the background  
        // thread finishes execution 
        try {
          String statusMsg = (String) get(); 
          System.out.println("Inside done function"); 
          statusLabel.setText(statusMsg); 
        } catch (InterruptedException e) { 
          e.printStackTrace(); 
        } catch (ExecutionException e) { 
          e.printStackTrace();
        }
      }
    }; 
    sw1.execute();      // executes the swingworker on worker thread 
  } 
  public static void main(String[] args) { 
    swingWorkerSample();
  }
}

1 Comment

Your code is fine (+1), but it may behave in a way that is not desired (only you know that). To illustrate this, add another button that displays a message in the console, change Thread.sleep(1000); to Thread.sleep(10000);, and try pressing the second button during the timeout...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.