JButton disable issue
I have a small swing app, which have couple of buttons. One of the button involves a five minute processing. During this time, I have disabled the button and removed the action Listener. Even though the button remains faded during the process, If the user clicks on the button ( while remaining faded) , the proces starts all over again, after the end of the current process.
button.removeActionListerner(listener)
button.setEnabled(false)
paint(getGraphics())
processData()
button.setEnabled(true)
button.addActionListener(listener)
paint(getGraphics())
Any ideas ? Thanks
# 1
Well, you are doing something wrong, it works fine in this example:
http://forum.java.sun.com/thread.jspa?forumID=57&threadID=621226
Click on the "Start in New Thread" button and then click on the disable buttons. Nothing happens.
If you need further help then you need to create a [url http://homepage1.nifty.com/algafield/sscce.html]Short, Self Contained, Compilable and Executable, Example Program[/url] (SSCCE) that demonstrates the incorrect behaviour, because I can't guess exactly what you are doing based on the information provided.
Don't forget to use the [url http://forum.java.sun.com/help.jspa?sec=formatting]Code Formatting Tags[/url] so the posted code retains its original formatting.
# 2
button.removeActionListerner(listener) // totally unnecessary if using next line
button.setEnabled(false)
paint(getGraphics()) // most likely unnecessary. use repaint(), but probably not even that is needed
processData() // This should start a separate thread which resets the button when done
button.setEnabled(true)
button.addActionListener(listener) // totally unnecessary if using previous line
paint(getGraphics()) // most likely unnecessary. use repaint(), but probably not even that is needed
# 3
I added the remove listener only after setEnabled was not working. Does the setEnabled(boolean) works for JButtons?
# 4
> I added the remove listener only after setEnabled was
> not working.
> Does the setEnabled(boolean) works for JButtons?
best way to find out is to try this in a simple app,... have one button disable another, and see if it works.
Otherwise, your best bet is to follow the advice of bsampieri.
# 5
I dont want to use the thread. Let the process run as a single thread. Without using the thread I want to try to disable the Button. Thanks
# 6
> I dont want to use the thread. Let the process run as
> a single thread.
Why not? This may be the reason that disabling the button doesn't work.
Below is a simple app that demonstrates disabling buttons:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
class BriefSwing extends JFrame implements ActionListener
{
private JPanel myPane = new JPanel();
private JButton myBtn1 = new JButton("Button 1");
private JButton myBtn2 = new JButton("Button 2");
public BriefSwing()
{
super("Brief Swing App");
createWidgets();
getContentPane().add(myPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
private void createWidgets()
{
myPane.add(myBtn1);
myPane.add(myBtn2);
myBtn1.addActionListener(this);
myBtn2.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
// myBtn == the button that was pushed.
JButton myBtn = (JButton)e.getSource();
JButton otherBtn; // the "other" button (of course!)
if (myBtn.equals(myBtn1))
{
otherBtn = myBtn2;
}
else
{
otherBtn = myBtn1;
}
// this toggles the enabled setting of the other button
otherBtn.setEnabled(!otherBtn.isEnabled());
}
private static void createAndShowGUI()
{
BriefSwing bSwing = new BriefSwing();
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{ public void run() { createAndShowGUI();}});
}
}
# 7
Dont want to use the thread, because I wanted to keep it simple. The context I am using this appication wont have any threading issues too.I will create a simplified version of mine and post it later. Thanks for all the help.
# 8
> Dont want to use the thread, because I wanted to keep
> it simple. The context I am using this appication
> wont have any threading issues too.
Well, there's your problem. You may think that it's simpler to do it that way, but you are running into the problem that the repaint manager doesn't necessarily get a chance to repaint because your "long-time-taking method" is taking over the event dispatch thread, thus not allowing it to repaint.
This is why you should actually be using a separate thread. It may seem harder, but you aren't going to get the desired results otherwise.
Granted, I would expect likewise that clicking on the button again at that point would have no effect until it returned.
# 9
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class TestFrame extends JFrame {
private JButton jButton1 = new JButton();
private JButton jButton2 = new JButton();
public TestFrame() {
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.getContentPane().setLayout( null );
this.setSize( new Dimension(400, 300) );
this.setTitle( "TestFrame" );
jButton1.setText("jButton1");
jButton1.setBounds(new Rectangle(50, 85, 120, 20));
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jButton2.setText("jButton2");
jButton2.setBounds(new Rectangle(220, 85, 135, 20));
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton2_actionPerformed(e);
}
});
this.getContentPane().add(jButton2, null);
this.getContentPane().add(jButton1, null);
}
private void jButton1_actionPerformed(ActionEvent e) {
System.out.println("******************************button 1 was pressed****************************");
this.jButton1.setEnabled(false);
this.jButton2.setEnabled(false);
someLongProcessing();
this.jButton2.setEnabled(true);
this.jButton1.setEnabled(true);
}
private void jButton2_actionPerformed(ActionEvent e) {
System.out.println("**************button 2 was pressed******************");
this.jButton2.setEnabled(false);
this.jButton1.setEnabled(false);
someLongProcessing();
this.jButton2.setEnabled(true);
this.jButton1.setEnabled(true);
}
private void someLongProcessing(){
int i = 0;
for (double d = 1.0 ; d < 10000000.00; d += 0.1){
i++;
if ( i == 10000000){
System.out.println("");
i = 0;
}
}
}
public static void main(String args[]){
TestFrame tf = new TestFrame();
tf.setVisible(true);
}
}
click on the button 1 couple of times. Even though I call setEnabled(false) in the event handler, it seems like it is ignoring the setEnabled(false)
# 10
It's not ignoring it. You can try all you want, but you are not going to get consistent results the way you expect without starting a separate thread to do your processing work. You just will not.
My last post explains why this is the case. If you don't want to believe me, I'm not going to lose sleep over it.
# 11
> click on the button 1 couple of times. Even though I
> call setEnabled(false) in the event handler, it seems
> like it is ignoring the setEnabled(false)
Let's step through the method that handles the button click, one line at the time:
this.jButton1.setEnabled(false);
You disable a button. What ends up happening here in the end is that a request to repaint the UI is added to the Event Queue. This means that the UI is not repainted immediately, it will be repainted as soon as the event thread has time to pick up the request from the queue. But right now the event thread is busy executing the jButton1_actionPerformed method, so the UI cannot be repainted. => You do not see the button disabled.
this.jButton2.setEnabled(false);
You disable another button. See above.
someLongProcessing();
Now you start a process that apparently takes a long time, and you let that process run in the event thread. Remember that you have now two repaint requests pending in the event queue, but since you are still using the event thread they haven't been processed yet. ==> Your UI has still not been repainted ==> The buttons still look enabled.
this.jButton2.setEnabled(true);
Now you enable a button again. As before, this will result in another repaint request being added to the queue. Of course, the event thread still hasn't had time to process any of the requests, because it is still busy in your jButton1_actionPerformed method.
this.jButton1.setEnabled(true);
You enable the second button. See above.
And now, finally, the event-thread is ready to process the paint requests that have been added to the queue. At last your UI is repainted, but of course by now the buttons are enabled again!
You HAVE to use a separate thread for this to accomplish what you want. There is no way around it.
# 12
Thought this would be a good time to learn and try to use a background thread in Swing. Never done this before, but the code works. If no one else finds fault with it (not unlikely given my novice status), you might want to try to use something like this in your program.
Please anyone, let me know if this is messed up or if it is ok. Thanks!
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class SwingBckGround extends JFrame implements ActionListener
{
private JPanel mainPanel = new JPanel();
private JPanel buttonPanel = new JPanel();
private JButton noThreadButton = new JButton("No Background Thread");
private JButton yesThreadButton = new JButton("With Background Thread");
private JPanel redPanel = new JPanel();
private JLabel redPanelLabel = new JLabel("Press Buttons to turn panel red");
private Color redPanelBkGrndColor;
public SwingBckGround()
{
super("Brief Swing App");
createWidgets();
getContentPane().add(mainPanel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
private void createWidgets()
{
// create buttonPanel
int edgeSize = 10;
FlowLayout fLayO = new FlowLayout();
fLayO.setHgap(2* edgeSize);
buttonPanel.setLayout(fLayO);
buttonPanel.add(noThreadButton);
buttonPanel.add(yesThreadButton);
buttonPanel.setBorder(BorderFactory.createEmptyBorder(edgeSize, edgeSize, edgeSize, edgeSize));
noThreadButton.addActionListener(this);
yesThreadButton.addActionListener(this);
// create redPanel
redPanel.setPreferredSize(new Dimension(40, 80));
redPanel.add(redPanelLabel);
// create mainPanel
mainPanel.setLayout(new BorderLayout());
mainPanel.add(buttonPanel, BorderLayout.NORTH);
mainPanel.add(redPanel, BorderLayout.SOUTH);
}
// any method that will take some time to finish
private void busyWork()
{
int loopSize = 1000;
for (int i = 0; i < loopSize; i++)
{
for (int j = 0; j < loopSize; j++)
{
for (int k = 0; k < loopSize; k++)
{
int foo = i + j + k;
}
}
}
}
public void actionPerformed(ActionEvent e)
{
JButton myBtn = (JButton)e.getSource();
if (myBtn.equals(noThreadButton))
{
yesThreadButton.setEnabled(false);
redPanelBkGrndColor = redPanel.getBackground();
redPanel.setBackground(Color.red);
busyWork();
yesThreadButton.setEnabled(true);
redPanel.setBackground(redPanelBkGrndColor);
}
else if (myBtn.equals(yesThreadButton)) // the if not needed but here for clarity
{
noThreadButton.setEnabled(false); // disable the other button
redPanelBkGrndColor = redPanel.getBackground(); // store original panel color
redPanel.setBackground(Color.red); // set redpanel to red
// create our background thread. Needs Java 6 to work
SwingWorker<Void, Void> mySW = new SwingWorker<Void, Void>()
{
@Override // do our busy work here in the background
protected Void doInBackground() throws Exception
{
busyWork();
return null;
}
@Override
public void done()
{
// when done reset button to enabled, and
// change redPanel color back to original color
// I'm not sure if tricky GUI calls here would require a
// call through the Swing event thread with
// SwingUtilities.invokeLater(new Runnable()....?
noThreadButton.setEnabled(true);
redPanel.setBackground(redPanelBkGrndColor);
}
};
mySW.execute(); // lets call our SwingWorker object here
}
}
private static void createAndShowGUI()
{
SwingBckGround swngBckGrnd = new SwingBckGround();
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{ public void run() { createAndShowGUI();}});
}
}
# 13
Try changing your jButton2_actionPerformed method to:
private void jButton2_actionPerformed(ActionEvent e) {
System.out.println("**************button 2 was pressed******************");
this.jButton2.setEnabled(false);
this.jButton1.setEnabled(false);
SwingWorker<Void, Void> mySW = new SwingWorker<Void, Void>()
{
@Override
protected Void doInBackground() throws Exception
{
someLongProcessing();
return null;
}
@Override
public void done()
{
resetButtons();
}
};
mySW.execute();
}
private void resetButtons()
{
jButton1.setEnabled(true);
jButton2.setEnabled(true);
}
# 14
Hey, it won't compile in Java 1.4!! What gives?!? (had to find a fault in it ;-)
Otherwise, I think it illustrates the point quite well. Seeing how to do it without SwingWorker for older versions of Java might be good. Which I'll throw in there for the heck of it:
else if (myBtn.equals(yesThreadButton)) // the if not needed but here for clarity
{
noThreadButton.setEnabled(false); // disable the other button
redPanelBkGrndColor = redPanel.getBackground(); // store original panel color
redPanel.setBackground(Color.red); // set redpanel to red
// create our background thread.
Thread t = new Thread(new Runnable()
{
public void run()
{
busyWork();
// when done reset button to enabled, and
// change redPanel color back to original color
// Gonna do it on the EDT...
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
noThreadButton.setEnabled(true);
redPanel.setBackground(redPanelBkGrndColor);
}
});
}
});
t.start();
}
# 15
> Hey, it won't compile in Java 1.4!! What gives?!?
> (had to find a fault in it ;-)
>
> Otherwise, I think it illustrates the point quite
> well. Seeing how to do it without SwingWorker for
> older versions of Java might be good. Which I'll
> throw in there for the heck of it:
Thanks for the post. Your way seems simpler. Is SwingWorker better or safer? Again, I'm a novice w/ threads, so I appreciate anything you can tell me.
/Pete
# 16
> Thought this would be a good time to learn and try to use a background thread in Swing.I gave a simple example way back in reply 1 that shows the difference between using a separate Thread and not using a Thread.
# 17
> I gave a simple example way back in reply 1 that
> shows the difference between using a separate Thread
> and not using a Thread.
I hadn't clicked on your link before, but I see it now. Thanks for pointing it out. It's much better than my example (of course) and allows for user interaction with the GUI during processing of the separate thread.
A question for you: If I want to call a swing routine from within a background thread, should I always call it using SwingUtilities.invokeLater(new Runnable() {....});?
Thanks
/Pete
# 18
> If I want to call a swing routine from within a background thread....In general you use the invokeLater when you change the state of a visible Swing component.[url http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#exceptions]Exceptions to the
# 19
> In general you use the invokeLater when you change
> the state of a visible Swing component.
>
> [url
> http://java.sun.com/products/jfc/tsc/articles/threads/
> threads1.html#exceptions]Exceptions to the Rule[/url]
Excellent reference, thanks.
/Pete