I want to make a GUI application in which I want to do something continuously (i.e. in while loop) in a different thread, until the user presses a button. In this other thread, I am accessing GUI elements and hence I have to use SwingUtilities.invokeLater() for this thread. But since this thread never ends, the Event Dispatching Thread's event handler for the button would never be called. How to handle this situation ?

Recommended Answers

All 13 Replies

Can you use the SwingUtilities method ONLY for the GUI interface calls and let your code run on its own, non EDT, thread?

Can you make a small simple program that compiles and executes to demo the problem?

Here'e the code. I want the label of the button to alternate between the 3 icons until the user presses the button.

import javax.swing.*;
import java.awt.event.*;
import java.util.*;

class tha extends JFrame
{
	private JButton button;
	tha()
	{
		button=new JButton("Button");
		button.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				System.out.println("hello");
				}
		});		
		add(button);
		setVisible(true);
		pack();
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		keepDrawing();
	}
	void keepDrawing(){
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				try{while (true){
					button.setIcon(new ImageIcon("../Desktop/images/kya/32x32/process1.png"));
					Thread.sleep(50);
					button.setIcon(new ImageIcon("../Desktop/images/kya/32x32/process2.png"));
					Thread.sleep(50);
					button.setIcon(new ImageIcon("../Desktop/images/kya/32x32/process3.png"));
					Thread.sleep(50);}
				}catch(Exception e){System.out.println(e);}
			}		
		});
	}					
	public static void main(String aa[])
	{
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
			new tha();
			}
		});
	} 
}

You haven't moved the code out of the EDT. ONLY use the EDT when changing the labels. For the sleep do that in your thread.
Its more efficient to get the images ONE time outside of the loop and then reference them inside the loop when they are needed.

Never - that is NEVER - use sleep anywhere near Swing. Your while(true) loop runs on the EDT and never returns, thus blocking everything.
For that kind of timing use a javax.swing.timer
Use it to call your button changing method every x millisecs. In that method just change the icon to the next icon and return immediately.

You haven't moved the code out of the EDT

That's because the invokeLater() function puts the code (inside run ?) in the EDT queue. Isn't it ?

if I change the label's icons in an infinite while loop inside the EDT thread, it will block any other user interaction. I mean, how to get the information inisde the while loop that the user has clicked the button ?

Update : Sorry, didn't see the new post

If you read the API for the Timer class you will see:

the action handlers for Timers can safely perform operations on Swing components.

James's suggestion for using a Timer is the way to go.
I have gotten your code to work by modifying it in the way I suggested. The only addition was to add a thread for the loop to run on. You must NOT put any long running code on the EDT!!! Make a thread to handle that. The Timer is a natural for your application.

how to get the information inside the while loop

Wrong question. Don't use an infinite loop at all. Use a timer. You don't actually have a long-running task, you just have a task that has to be run regularly.

Thanks everyone. If I use a timer, then I will be calling a function (or multiple functions). say every 1 sec. But I need to do different things on each call and Timer triggers the same function (or the same set of functions) every 1 sec. How to achieve that ?

@Norm1 : If you got my code working without a timer, can u please post it. Can't come up with the correct logic myself.

But I need to do different things on each call

There are ways to control logic flow: if else statement or switch statement for example

I put each of the GUI calls into SwingUtilities invoke methods and put the loop itself in its own thread.

I need to do different things on each call and Timer triggers the same function (or the same set of functions) every 1 sec. How to achieve that ?

Simple switch with a var to tell which icon we need to set each time

int step = 1;
...
public void actionPerformed(ActionEvent e) { // called by Timer
  switch (step ) {
   case 1: {
     // set 1st icon
     step = 2;   
     break;
   }
   case 2: {
     // set 2nd icon
     step = 3;   
     break;
   }
   // etc
}

or (better) have the icons pre-loaded into an array of ImageIcons, then

int iconNum = 0;

public void actionPerformed(ActionEvent e) { // called by Timer
  button.setIcon(imageIcons[iconNum ])
  iconNum ++;
  if (iconNum >= imageIcons.length) iconNum = 0
}

Thanks for the logic. Timers are definitely a cleaner solution than than using sleep(). But just out of curiosity, I tried to do this without blocking the EDT and using Thread.sleep() as NormR1 suggested. Here is the code :

class tha extends JFrame
{
	private JButton button;
	int a=0;
	ImageIcon[] icons;
	Thread thread;
	tha()
	{
		button=new JButton("Button");
		button.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				thread.interrupt();				
				System.out.println("hello");
				}
		});		
		add(button);
		setVisible(true);
		pack();
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		icons=new ImageIcon[]{new ImageIcon("./Desktop/images/kya/32x32/process1.png"),new ImageIcon("./Desktop/images/kya/32x32/process2.png"),new ImageIcon("./Desktop/images/kya/32x32/process3.png")};
		thread=new Thread(){
			public void run(){
					  try{
					  while (true){
					  
						SwingUtilities.invokeLater(new Runnable(){
							public void run()
							{
								button.setIcon(icons[a++]);
							}
						});
						Thread.sleep(100);
						if (a==3)
							a=0;
					   }
					   
					}catch(Exception e){System.out.println(e+"Exiting the thread");}
			}
		};
		thread.start();		
	}
	
	public static void main(String aa[])
	{
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
			new tha();
			}
		});
	} 
}

Is there anything wrong with this ? Instead of using interrupt(), a flag could be checked in the while loop, but that won't be better, I think ?

Sure you can create a new thread and sleep in that and use invokeLater, and that should work. But it's a pretty tortuous way to achieve a result for which the swing timer was created, and which timer does in far less code.
Swing timer is actually the class used internally by swing for things like popping up mouse-over tooltips. It's designed to be very efficient and its, obviously, totally integrated into the whole swing infrastructure.
To stop or start your animated icons, just stop or start the timer.
so
No, there;s nothing wrong with it, but it's definitely the second-best solution.
J

Thanks.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.