0

I have a window that has various panels. On one panel, I have a JTextArea called dogTalk where I update its text. Upon user's click of a button, I want the text to add what I have mentioned below in setText.

I used the sleep method so that the user can read my updated text and the window can close automatically within 4 seconds. (I don't want the user to have the ability to close the window on close, hence I didn't use Jframe.EXIT_ON_CLOSE but used JFrame.DO_NOTHING_ON_CLOSE and used my automatic closing with the help of sleep and system.ext(0))

However, I noticed that the sleep method does not allow the dogTalk to get updated. It prints out "we're working", though, so I am guessing it's a problem with the window? I know that the sleep is causing the issue and not something else in my code because when I commented out the sleep and system.exit(0) and tested if my if statement is executing, I noticed the JTextArea did update with my statement just fine! Could you please help me?

if (e.getActionCommand().equals("buybone")) {

        System.out.println("We're working");
        dogTalk.setText(dogTalk.getText() + "\nWow bone very wow much thanks bye.");
        try
        {  
            TimeUnit.SECONDS.sleep(4);
        }
        catch ( InterruptedException e1 )
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

       System.exit(0);

}

user1217
  • 53
  • 1
  • 5
  • It's because you're calling `sleep` on the Event Dispatch thread (inside `actionPerformed`) – Vince May 21 '15 at 05:56
  • oh so i take it out from actionPerformed ? where would i put it then? – user1217 May 21 '15 at 06:26
  • You should spawn a new thread. The JVM exits when all non-daemon threads are no longer running. Spawn a new thread that waits for 4 seconds, then have it call `EventQueue.invokeLater(() -> frame.dispose())`, which should end the Event Dispatch Thread, removing the need for the nasty `System.exit` call – Vince May 21 '15 at 06:32

3 Answers3

1

Rather than calling System.exit, let the application gracefully die out. An application terminates when there are no non-daemon threads still alive. Daemon is just a flag used to determine whether the JVM should terminate if that thread is still running; the JVM will still terminate if non-daemon threads are running.

With that said, the problem is that you're calling sleep on the Event Dispatch Thread.

The EDT handles all updating and rendering of Swing and AWT components, as well as execute the behavior specified in event listeners (like ActionListener#actionPerformed(ActionEvent)). If you cause it to block (through sleeping or other forms of blocking), it won't be able to process updating and rendering. When you call setText, the EDT needs to be able to adjust the text. You're preventing this by forcing it to sleep.

How to fix

Spawn a new thread, have it wait 4 seconds, then have it dispose of your frame:

Java 8+

public void actionPerformed(ActionEvent e) {
     dogTalk.setText(...);
     new Thread(() -> {
          TimeUnit.SECONDS.sleep(4);
          frame.dispose();
     }).start();
}

Before Java 8

public void actionPerformed(ActionEvent e) {
    dogTalk.setText(...);
    new Thread(new Runnable() {
        public void run() {
            TimeUnit.SECONDS.sleep(4);
            frame.dispose();
        }
    }).start();
}
Vince
  • 14,470
  • 7
  • 39
  • 84
  • Hi, thank you so much for your detailed response! When I copy pasted your thing: new Thread(() -> { TimeUnit.SECONDS.sleep(4); frame.dispose(); }).start(); It gave me a syntax error and told me to delete these tokens. I think your }before ).start is confusing it (and me, I am not sure why we need that?) – user1217 May 23 '15 at 21:56
  • @user1217 It's a lambda expression, you need Java 8 as well as an IDE that supports it's features. I'll edit my answer, but you should really update your development environment – Vince May 23 '15 at 22:01
  • Ah I see. Sorry, I use what's required for class. Thanks for updating your response–Now I don't have any errors in my code and it's working!! Thanks SO MUCH!!!! I am honestly so grateful! – user1217 May 23 '15 at 22:48
0

Just right after dogTalk.setText(dogTalk.getText() + "\nWow bone very wow much thanks bye."); put the following code :

dogTalk.revalidate();
dogTalk.repaint();
  • Well you don't need repaint AND revalidate; they are both targeted for different situations (revalidate actually causes a repaint-like action, also updates component hierarchy after invalidating it). I'm on my phone, so no, I didn't try it. But you should look into what those methods do. `setText` calls `insertString` on it's document, which triggers the event listeners of the document. It should update on it's own – Vince May 21 '15 at 06:11
  • yeah, didnt work :( when I added those 2 statements, it was the same as before. thanks though :) – user1217 May 21 '15 at 06:19
  • So try the following code using a timer : Timer t= new Timer(2000, null); t.setRepeats(false); t.start(); Use this instead of using the sleep which sops for a while the main thread. Tell me if it gets better. – Normal design May 21 '15 at 06:24
  • unfortunately, when i do that, it doesnt pause for even a second but closes immediately after i press the button. – user1217 May 21 '15 at 06:30
0

the following code works well i used Frame.update function it worked. in your case you have to update panel i guess

           dogTalk.setText(dogTalk.getText() + "\nWow bone very wow much thanks bye.");
        frame.update(getGraphics());
        try
        {  
            TimeUnit.SECONDS.sleep(4);
        }
        catch ( InterruptedException e1 )
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.exit(0);

this is my full program

public class TestSleep extends JFrame implements ActionListener{

JTextArea are=new JTextArea("asdjkfh");

JButton button=new JButton("Submit done");

public TestSleep()
{
    are.setBounds(20, 20, 30, 10);
    button.setBounds(10, 50, 20, 20);
    this.add(are);
    this.add(button);
    button.addActionListener(this);

}
public static void main(String[] args)
{
    TestSleep sleep=new TestSleep();
    sleep.setLayout(new GridLayout());
    sleep.setVisible(true);
    sleep.setBounds(10, 10, 500, 280);
}
@Override
public void actionPerformed(ActionEvent e)
{
        System.out.println("Working");
        are.setText(are.getText() + "\nWow bone very wow much thanks bye.");
        this.update(getGraphics());
        try
        {  
            TimeUnit.SECONDS.sleep(4);
        }
        catch ( InterruptedException e1 )
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.exit(0);

}   

}

Jerome
  • 13
  • 1
  • 7
  • You shouldn't manually invoke the `update` method of components (it calls [`paint`](https://docs.oracle.com/javase/8/docs/api/javax/swing/JComponent.html#paint-java.awt.Graphics-), which shouldn't be called manually), nor [access the graphics of a component](http://stackoverflow.com/a/15991175/2398375) manually – Vince May 21 '15 at 19:27
  • But it *shouldn't* be called, as the documentation says. – Vince May 22 '15 at 05:54
  • can you give me link to that document ? – Jerome May 22 '15 at 06:07
  • I did, click `paint` in my first comment: "*Applications should not invoke paint directly*" – Vince May 22 '15 at 06:12
  • i did not get you ? you mean you used textarea.paint() ? I tried that too . when i set text to textarea and get text it gives me new text but it is not updated in textarea that is why i tried to paint frame itself then it worked. – Jerome May 22 '15 at 06:19
  • Nooo, I mean I linked you to the documentation in my first comment. [Here is the link again](https://docs.oracle.com/javase/8/docs/api/javax/swing/JComponent.html#paint-java.awt.Graphics-) – Vince May 22 '15 at 06:20