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

[628 byte] By [KURIANMATHEWa] at [2007-11-27 5:23:40]
# 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.

camickra at 2007-7-12 11:49:36 > top of Java-index,Desktop,Core GUI APIs...
# 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

bsampieria at 2007-7-12 11:49:36 > top of Java-index,Desktop,Core GUI APIs...
# 3
I added the remove listener only after setEnabled was not working. Does the setEnabled(boolean) works for JButtons?
KURIANMATHEWa at 2007-7-12 11:49:36 > top of Java-index,Desktop,Core GUI APIs...
# 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.

petes1234a at 2007-7-12 11:49:36 > top of Java-index,Desktop,Core GUI APIs...
# 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
KURIANMATHEWa at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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();}});

}

}

petes1234a at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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.
KURIANMATHEWa at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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.

bsampieria at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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)

KURIANMATHEWa at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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.

bsampieria at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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.

Torgila at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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();}});

}

}

petes1234a at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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);

}

petes1234a at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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();

}

bsampieria at 2007-7-12 11:49:37 > top of Java-index,Desktop,Core GUI APIs...
# 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

petes1234a at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...
# 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.
camickra at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...
# 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

petes1234a at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...
# 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
camickra at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...
# 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

petes1234a at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...
# 20
Thank you folks.
KURIANMATHEWa at 2007-7-21 21:27:54 > top of Java-index,Desktop,Core GUI APIs...