0

I've searched for a solution for my problem all over but I cannot find anything close. Here is my problem: I have just started to learn Swing in Java and I have an application that will click randomly between a min and max amount of time. The user enters a min and max time and clicks a button which starts this code:

    class CalcButtonListener implements ActionListener{

    @Override
    public void actionPerformed(ActionEvent arg0) {
        //get data from text fields and store as integers in milliseconds. 

        //create a robot and random number between min and max

        while(run == true){

            robot.mousePress(InputEvent.BUTTON1_MASK);
            robot.mouseRelease(InputEvent.BUTTON1_MASK);
            robot.delay(num.nextInt(max-min+1)+min);

            }
        }
    }
}

I've removed some code because it wasn't relevant to the issue. The problem is I have another button to set the variable run to false but once the first button is clicked, I can't click the second button or the exit button at the top for that matter.

I'm sure my mistake is a very basic one but I can't see it. Thank you for any replies that help me better understand the subject.

Edit: Changed the while loop from "while (run = true)" to "while (run == true)".

8 Answers8

4
 while(run == true){

"=" sets run to true "==" compares the value of run to true

Also you can just use

 while(run){
JNDPNT
  • 7,445
  • 2
  • 34
  • 40
  • You are very correct and in my program I caught it after I posted this and ended up changing it to a for loop for testing reasons. The problem is I can't break the loop later on; I believe it is answered below. Thanks for the reply. –  Dec 13 '11 at 19:45
3

Take a look at SwingWorker. And just do while(run)

bragboy
  • 34,892
  • 30
  • 114
  • 171
  • Since the comment indicates that a `Robot` will be making clicks, maybe it's not a good idea to do this off the EDT. – uckelman Dec 12 '11 at 10:18
2

Every event will be processed by a single thread called Event Dispatch thread(EDT). If you have an infinite call inside one of the events, EDT cannot process the next event in the event queue.

Prince John Wesley
  • 62,492
  • 12
  • 87
  • 94
2

UPDATE This answer is updated, because @uckelman pointed me out that, with the condition run = true, the stop button never breaks the while loop, because it's needed to change to run = false within the loop. Then I post a simple and alternative solution to this logic problem, to schedule a task repeatedly with a timer. For details, please check this SO question.

About the events for the buttons: if you have two buttons, one to start a loop and one to end the loop, just try this code:

    class CalcButtonListener implements ActionListener{

        private boolean run = true;
        private java.util.Timer timer = new java.util.Timer();
        private JButton start_loop, end_loop;

        //here the buttons initialization


        @Override
        public void actionPerformed(ActionEvent ae){
        if(ae.getSource()==start_loop){


            java.util.TimerTask task = new java.util.TimerTask() {
        @Override
        public void run() {
            doStuff();
        }
    };
    timer.schedule(task, java.util.Calendar.getInstance().getTime(), 500);//here the '500' means the time, 500 ms,
the task is repeatedly executed. 


        }

        if(ae.getSource()==end_loop){
            timer.cancel();//cancel the tasks scheduled
            System.out.println("Task cancelled!");
        }
    }

    private void doStuff(){
        robot.mousePress(InputEvent.BUTTON1_MASK);
            robot.mouseRelease(InputEvent.BUTTON1_MASK);
            robot.delay(num.nextInt(max-min+1)+min);


    }
    }

Now, the task doStuff() is scheduled to be executed each 500 ms.

Other info about java.util.Timer and java.util.TimerTask.

About your problem:

The problem is I have another button to set the variable run to false but once the first button is clicked, I can't click the second button or the exit button at the top for that matter.

As in a previous question, and in this page, it's written this:

Swing's single-thread rule says that Swing components can only be accessed by a single thread. This rule applies to both gets and sets, and the single thread is known as the event-dispatch thread.

The single-thread rule is a good match for UI components because they tend to be used in a single-threaded way anyway, with most actions being initiated by the user. Furthermore, building thread safe components is difficult and tedious: it's a good thing not to be doing if it can be avoided. But for all its benefits, the single-thread rule has far-reaching implications.

Swing components will generally not comply with the single-thread rule unless all their events are sent and received on the event-dispatch thread. For example, property-change events should be sent on the event-dispatch thread, and model-change events should be received on the event-dispatch thread.

For model-based components such as JTable and JTree, the single-thread rule implies that the model itself can only be accessed by the event-dispatch thread. For this reason, the model's methods must execute quickly and should never block, or the entire user interface will be unresponsive.

Then, if you develop your GUI using a single Thread, when a button event is executed, your GUI will freeze, waiting for the complete execution of the related button event. In your case, on a infinite loop, your GUI will always freezing.

My suggestion is to use, for your GUI, a SwingWorker, or extend the Thread class (then developing the GUI in a separate thread), or implement the Runnable interface. Another alternative is the using of a Timer from the javax.swing.Timer package.

You can read this old question of SO about SwingWorker: How do I use SwingWorker in Java?

A tutorial for SwingWorker : http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

A tutorial to make a Thread : http://docs.oracle.com/javase/tutorial/essential/concurrency/

A question about Timer: Update JPanel and attributes in a gui, with a user-specified timer?

A tutorial about Timer: http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Community
  • 1
  • 1
Alberto Solano
  • 7,972
  • 3
  • 38
  • 61
  • @mKorbel Thank you. I know the suggestion with the getSource() in a separate actionPerformed method, it isn't the best (it's better to do it through inner classes), but it's the first thing that came to my mind. :-) – Alberto Solano Dec 12 '11 at 11:51
  • This can't work, because `run` is never set to `false` within the `while (run)` loop. – uckelman Dec 13 '11 at 10:05
  • @uckelman Why this cannot work? ---> if(ae.getSource()==end_loop) run = false; – Alberto Solano Dec 13 '11 at 11:48
  • @AlbertoSolano: That assignment is after the while loop. Once control enters the while loop, your program will keep looping so long as `run` is true. But `run` is never assigned to inside the while loop. – uckelman Jan 03 '12 at 02:24
  • @uckelman You're right. Like you said,it's a logic problem. I will update the answer using a Timer to do the task repeatedly. – Alberto Solano Jan 03 '12 at 11:35
1

You should read about Swing timers:

http://java.sun.com/products/jfc/tsc/articles/timer/

That is, make your program event-driven. Swing applications already have a loop running inside them all the time, called the event loop. It doesn't help if you start another one.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
1

Be careful about method like while(something), this could make the program frezee, i recommend you to implement events listeners to avoid this problems...

melli-182
  • 1,216
  • 3
  • 16
  • 29
0
run=true;
while(run == true){

...

Jakub Matczak
  • 15,341
  • 5
  • 46
  • 64
0

while (run = true) is almost certainly not what you want. What that does is assigns true to run each time the loop condition is executed, which ensures that the loop will always continue.

What you were probably trying to say was while (run == true) which only tests whether run is true. Better is just to say while (run), which does the same thing.

If you're assigning to run from a different thread, note that you ought to make run a volatile member of your class. If you're not assigning to run somewhere else, then you have a logic bug, since there's no way to break out of the loop. In that case, you need to add a test inside the loop and set run to false when you want the loop to stop. (Or, you could have while (true) and just use a break inside the loop.)

uckelman
  • 25,298
  • 8
  • 64
  • 82