1

I'm trying to randomly generate a string and constantly update a JTextArea. I know the program gets hung up in the runTest() method in infinite loop. I'm trying to loop and display this result until the user hits a stop button. Any advice? Thanks

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import java.awt.BorderLayout;
    import java.awt.Panel;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Random;

public class MyApplication extends JFrame {
    private JTextArea textArea;
    private JButton b;
    private String toChange = "";

    // Called from non-UI thread
    private void runQueries() {
        while (true) {
            runTest();
            updateProgress();
        }
    }

    public void runTest() {

        while (true) {

            if (toChange.length() > 10) {
                toChange = "";
            }
            Random rand = new Random();
            toChange += rand.nextInt(10);
        }
    }

    private void updateProgress() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                textArea.append(toChange);
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MyApplication app = new MyApplication();
                app.setVisible(true);
            }
        });
    }

    private MyApplication() {

        this.setLayout(null);
        this.setResizable(false);
        this.setLocation(100, 100);
        this.setSize(900, 600);

        final Panel controlPanel = new Panel();
        controlPanel.setLayout(new BorderLayout());
        controlPanel.setSize(600, 200);
        controlPanel.setLocation(50, 50);
        setDefaultCloseOperation(this.EXIT_ON_CLOSE);

        textArea = new JTextArea("test");
        textArea.setSize(100, 100);
        textArea.setLocation(200, 200);
        this.add(textArea);

        JButton b = new JButton("Run query");
        b.setSize(100, 75);
        b.setLocation(100, 50);
        this.add(b);

        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Thread queryThread = new Thread() {
                    public void run() {
                        runQueries();

                    }
                };
                queryThread.start();

            }
        });

    }

}
Java Noob
  • 67
  • 1
  • 6
  • I think you want a repeating task on a new thread. Give me a minute to grab the code. – Joehot200 Nov 14 '14 at 14:47
  • Make `Thread queryThread` a **class** variable and do `queryThread.interrupt()` in the `actionPerformed()` method of your "Stop" button. See [Java Thread Primitive Deprecation](https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html) for more information. – PM 77-1 Nov 14 '14 at 14:52
  • 2
    Java GUIs have to work on different OS', screen size, screen resolution etc. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). – Andrew Thompson Nov 14 '14 at 14:56
  • See the example shown [here](http://stackoverflow.com/q/24958793/1795530). Hopefully if you read the whole topic you'll learn lot of things about concurrency in Swing. Also [Swing Timer](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) which also can be started and stopped might be used as an alternative to SwingWorker. – dic19 Nov 14 '14 at 15:06

3 Answers3

3
int initialDelay = 1000; // start after 1 seconds
    int period = 1000;        // repeat every 1 seconds
    Timer timer = new Timer();
    TimerTask task = new TimerTask() {
      public void run() {
         if (toChange.length() > 10) {
            toChange = "";
        }
        Random rand = new Random();
        toChange += rand.nextInt(10);
    };
    timer.scheduleAtFixedRate(task, initialDelay, period);

You're going to want something like the above I Just did. Otherwise, you are simply freezing your thread, and therefore, updateProgress(); is never called.

On that note, why do you even need a while (true) loop in the runTest() method? It is constantly being run and updated anyway by the while true loop in runQueries(), meaning that the while (true) loop appears to have no meaning.

Clarification: I am saying that you should put your runTest() and updateProgress() in a delayed task, other than a while true loop - Otherwise, you are just holding up the other task by putting them in the same threads.

However, changing this should also fix the issue:

b.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
           int initialDelay = 100; // start after 0.1 seconds
           int period = 100;        // repeat every 0.1 seconds
        {
            Timer timer = new Timer();
            TimerTask task = new TimerTask() {
            public void run() {
                runTest();
            };
        timer.scheduleAtFixedRate(task, initialDelay, period);
        }
        {
            Timer timer = new Timer();
            TimerTask task = new TimerTask() {
            public void run() {
                updateProgress();
            };
        timer.scheduleAtFixedRate(task, initialDelay, period);
        }
    }
    });

}

And runTest()..

public void runTest() {
        if (toChange.length() > 10) {
            toChange = "";
        }
        Random rand = new Random();
        toChange += rand.nextInt(10);
}

and updateProgress()..

private void updateProgress() {
    textArea.append(toChange);
}

Note that I was typing the code in the SO editor, not in the IDE. Sorry for any syntax errors.

Joehot200
  • 1,070
  • 15
  • 44
  • I'm struggling to understand this, could you elaborate more using my given example? Where should the Timer be created? Thanks – Java Noob Nov 14 '14 at 15:20
  • @JavaNoob I added some clarification plus a solution I did not earlier realize. – Joehot200 Nov 14 '14 at 15:28
  • Ah, the solution displays once then freezes the program. – Java Noob Nov 14 '14 at 15:34
  • @JavaNoob Try now. It should constantly run updateProgress() and runTest() on separate threads. – Joehot200 Nov 14 '14 at 15:44
  • I see what you're trying to achieve here but I'm getting compliation errrors... are these the correct imports? import java.util.TimerTask; import javax.swing.Timer; – Java Noob Nov 14 '14 at 15:54
  • @JavaNoob If you've got skype, then add me at "joehot200" (the UK one), I should be able to help you more. The code not working will be because I did not write the code in an IDE, so it will probably have syntax errors. What are the compilation errors (you should be able to fix them yourself, i'm guessing)? – Joehot200 Nov 14 '14 at 15:58
1

One of the most important things that I cannot stress enough is that all GUI elements in Java should be run on the EventDispatchThread, failing to do so can cause unpredictable and faulty behavior.

read up on it here: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

From what I can tell by your current code you are livelocking your application by freezing it in a constant loop.

What you will want to do is create a separate thread that upon creation keeps updating the TextArea in the EDT but keep this thread referenced in your main application. Once the user presses the button (this will be on the EDT) it should tell your main thread to kill your separate thread causing the updating to stop.

If you get stuck feel free to ask for more detailed steps.

MSB
  • 854
  • 7
  • 24
0

try this.

 private void runQueries() {
    while (!stop) {
        runTest();
        updateProgress();
    }
}

make stop is true when you want quite, for example hit stop button. then

public void runTest() {

    while (true) {

        if (toChange.length() > 10) {
            toChange = "";
            break;  
        }
        Random rand = new Random();
        toChange += rand.nextInt(10);
    }
}

if you dont break the loop, your updateProgress will not call..

subash
  • 3,116
  • 3
  • 18
  • 22