Changing a label during ActionEvent - java

I am trying to enable/disable a label when a button is pressed and i want to do this during the event and not after it. As you can see below, i try to enable/disable the two labels: lblKeyboard and lblGamepad.
They end up running after the "RemoteControl.run();" is executed but i want it to happen before that. Any way i can do that?
Thank you!
JButton btnGamepad = new JButton("Gamepad");
btnGamepad.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(cont_state == 0){
if(RemoteControl.findGamePad() == true){
cont_state = 1;
game_status = "on";
}
else{
game_status = "off";
key_status = "on";
JOptionPane.showMessageDialog(null, "Controller not found!");
cont_state = 0;
}
}
if(cont_state == 1){
System.out.println("CONNECTED GAMEPAD!");
lblKeyboard.disable();
lblGamepad.enable();
frame.repaint();
RemoteControl.run();
cont_state = 0;
}
}
});

ActionEvents are run on the EDT which is also responsible for painting. Once you change the labels state, Swing issues a request for repaiting the Label. The thing is that this request is posted on a queue and will be executed once the EDT is free and, as you can see, the EDT is busy running your code so no repainting for you! Depending on the nature of your code, you should consider using a SwingWorker or simply moving RemoteControl.run() to another Thread as in
new Thread(new Runnable() {
#override
public void run() {
RemoteControl.run();
}
}).start();

Code in an event listener executes on the Event Dispatch Thread (EDT) and the GUI can't repaint itself until all the code has finished executing. Read the section from the Swing tutorial on Concurrency for more information on the EDT.
Try wrapping your RemoteControl.run() code in a SwingUtilities.invokeLater(...). This will place the code at the end of the EDT, which might give Swing a changes to repaint the state of the two labels.
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
RemoteControl.run()
}
});
This assumes your code updates the GUI. If not, then just use a separate Thread.

Related

While loop to repeat actionPerformed of a JButton

I've got a JFrame with some JButtons. The "update" button (named JButton1) executes a number of queries which fill a form.
Now I'd like another JButton to be the "automatic update" (named JButton2), so I would like it to loop inside the JButton1ActionPerformed method, until JButton2 is no longer selected.
When I press JButton2 the window freezes.
Is my code right? Do you think I would need a stop button?
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if(jToggleButton2.isSelected()){
start = System.currentTimeMillis();
do{
do{
jButton1ActionPerformed(evt);
}
while(System.currentTimeMillis()-start>3000);
}
while(jToggleButton2.isSelected());
}
if(jToggleButton2.isSelected()){
jToggleButton2.setSelected(false);
}
}
Swing is:
Single threaded; this means that you should not perform any long running or blocking operations within the context of the Event Dispatching Thread
Not thread safe; this means you should not update the UI or anything the UI might depend on outside of the EDT. It's also risky to try and address UI elements outside the EDT, as the thread model might return false values
Without more context to your problem, I might suggest using a Swing Timer. Two main reasons:
The ActionListener is called within the context of the EDT, making it safe to update the UI from within in
You can specify a delay between updates, which reduces the risk of overloading the EDT which will cause performance issues
For example...
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if (jToggleButton2.isSelected()) {
// You could use an instance field instead and determine
// if the Timer is already running or not
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!jToggleButton2.isSelected()) {
((Timer) e.getSource()).stop();
return;
}
jButton1ActionPerformed(evt);
}
});
timer.start();
// Just want to point out that this doesn't make sense
// As if the button was selected, you'd still be in
// your previous loop
//if (jToggleButton2.isSelected()) {
// jToggleButton2.setSelected(false);
//}
}
}
Another solution might be to use a SwingWorker, which provides you with a means to perform long running or blocking operations off the EDT, but which also provides functionality to update the UI safely from within the EDT.
Take a look at:
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Swing Timers
for more details
Lets put aside that you should not crete Thread like I will show you (but use SwingWorker for example) you need to do something like this:
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
if(jToggleButton2.isSelected()){
new Thread(new Runnable(){
public void run(){
long start = null;
do{
start = System.currentTimeMillis();
jButton1ActionPerformed(evt);
}while(jToggleButton2.isSelected() &&jSystem.currentTimeMillis()-start>3000)
}
}).start();
}
Im not focusing on if your code is valid or not, its just shows how to run something in background, so you will not freeze your GUI.

I am a beginner and I am trying to make a card flip game in netbeans

On clicking an image I want another image to be displayed pause for one second and the original image to be restored and displayed againdisplayed again When I try to do it it action handler such as code given below it sets the image after sleep .I want the image to be displayed pause and the original to be displayed again how can I achieve that
private void jButton16ActionPerformed(java.awt.event.ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
try { //sleep 1 seconds
Thread.sleep(1000);
}
catch (InterruptedException e)
{
}
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg")); }
Swing is single threaded, you should never perform long running or blocking operations within the context of the Event Dispatching Thread
Swing is NOT thread safe, this means you should never update the UI (and anything the UI needs) outside of the context of the EDT
The simplest solution to your problem is to use a Swing Timer
private void jButton16ActionPerformed(java.awt.event.ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg"));
}
});
timer.setRepeats(false);
timer.start();
}
See Concurrency in Swing and How to use Swing Timers for more details
You should never invoke Thread.sleep() inside Event Dispatch Thread
because Thread.sleep() will block Event Dispatch Thread and your UI will be Freeze , instead you could use a Timer to achieve your goal
Refer below code,
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg"));
}
});
timer.setRepeats(false);
timer.start();

Updating buttons inside or outside of SwingWorker?

I want to disable a number of buttons/menu items of my GUI while a SwingWorker thread is running. The thread is started when a button is clicked.
It looks like the while loop in my code causes the CPU load to go up significantly. Did I get something wrong about how to determine if a SwingWorker thread is still running?
The CPU load's definitely lower when I update the buttons/menu items inside the SwingWorker thread. However, I felt like that shouldn't be the SwingWorker thread's job. Should it?
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
menu.setEnabled(false);
MySwingWorker worker = new MySwingWorker();
worker.execute();
while (true) {
if (worker.isCancelled() || worker.isDone()) {
menu.setEnabled(true);
break;
}
}
}
});
t.start();
}
});
Swing GUI objects should be constructed and manipulated only on the event dispatch thread (EDT). Doing so from t is incorrect. As suggested in examples cited here, condition your GIU elements on the EDT before starting the worker, and update the GUI in process() or done().

updating DefaultListModel on another thread

I have a program that need to update the content of JList, which is DefaultListModel on another thread. Since the number of contents may change from time to time, so I just clear all content and add new content into DefaultListModel when updating. But seems I ran into an issue that JFrame starts refreshing while my thread is doing update. I got exceptions like this
Exception in thread "AWT-EventQueue-0"
java.lang.ArrayIndexOutOfBoundsException: 3
Here is an example of the code
DefaultListModel model;
JList jList;
JScrollPane jScrollPane;
Thread thread;
public Frame() {
this.setTitle("ASM_SIMULATOR");
this.setBounds(100, 100, 500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().setLayout(null);
model = new DefaultListModel();
jList = new JList(model);
jScrollPane = new JScrollPane(jList);
jList.setBounds(50, 50, 300, 200);
jScrollPane.setBounds(50, 50, 300, 200);
this.getContentPane().add(jScrollPane);
this.setVisible(true);
thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
makeData();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
public void makeData() {
System.out.println("makeData()");
model.clear();
for (int i = 0; i < 20; i++) {
model.addElement((int) (Math.random() * 100));
}
}
public static void main(String[] args) {
new Frame();
}
You violate the basic "all Swing component should be accessed/modified on the Event Dispatch Thread (=EDT), and on the EDT only" twice in that code snippet.
Your main method should wrap the new Frame() call in an SwingUtilities#invokeLater or some similar method
Your model-update thread changes the model on a background thread. Updating the model will fire events which are received by the JList, on which the JList updates itself (again, on the wrong thread).
Two possible solutions:
create a new DefaultListModel on your background thread, and replace it in one go on the EDT.
keep updating the existing model, but make sure the updates happen on the EDT.
The basic answer is don't
Swing is not thread safe.
What you need to do is either use a SwingWorker to build the model and use its done/process method to apply it back to the view or use SwingUtilities.invokeLater to continue using your thread, but sync updates back to the Event Dispatching Thread
Have a read through Concurrency in Swing for details
you have issue with Concurrency in Swing
have to wrap model.addElement((int) (Math.random() * 100)); into invokeLater
correct way could be start workers Thread from Runnable#Thread, or use SwingWorker
output from SwingWorkers methods publish() and process() could be doen on EDT
Unfortunately it's not that simple. Only the GUI thread should be allowed to update the GUI, so any other thread needs to forward any updates to the GUI thread via SwingUtilities.InvokeLater. In your case you can probably just wrap the entire makeData method, since all it does is update the GUI:
thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
SwingUtilities.InvokeLater(new Runnable() {
public void run() {
makeData();
}
});
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Note that now the code of makeData will execute on the GUI thread. In other cases when you're doing other time-consuming work that does not involve the GUI you should use InvokeLater in a more fine-grain manner to keep the UI thread as free as possible.
Edit: Looking more carefully at your code, I noticed that all you're doing is a timed update of the GUI every 200 ms. You can do this much easier with javax.swing.Timer:
int delay = 200; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
makeData();
}
};
new Timer(delay, taskPerformer).start();

Wait for AWT repaint to finish

I have a Frame that is all one color, but has some text in the corner. I set the color, actually read the color from the monitor, and then do some computations based on those measurements.
The problem is, calling repaint() causes the Frame to be painted after I do the measurements. I'm assuming this is due to repaint() delegating to the EDT, but I'm getting incorrect results due to the measurements occurring before/during the actual painting work.
My initial thought was to put a listener on paint completion, but I repaint to update the text much more frequently than I do for the color and I don't want to listen to those events. How can I wait for the actual painting task to finish before taking my measurement?
Amazing what you can find...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
PaintEvent pe = (PaintEvent) event;
String type = "";
if (pe.getID() == PaintEvent.PAINT) {
type = "PAINT";
} else if (pe.getID() == PaintEvent.PAINT_FIRST) {
type = "PAINT_FIRST";
} else if (pe.getID() == PaintEvent.PAINT_LAST) {
type = "PAINT_LAST";
} else if (pe.getID() == PaintEvent.UPDATE) {
type = "UPDATE";
}
System.out.println(type + "; pe.UpdateRec = " + pe.getUpdateRect() + "; pe.component = " + pe.getComponent());
}
}, AWTEvent.PAINT_EVENT_MASK);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
Now, because repaint requests can come think and fast, I'd be tempted to place a small "delay" in that would fired shortly after the last request has completed...
private Timer updateTimer;
// ...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
updateTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Update compulations here...
}
});
updateTimer.setRepeats(false);
updateTimer.setCoalesce(true);
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
updateTimer.restart();
}
}, AWTEvent.PAINT_EVENT_MASK);
}
}
The idea is to allow at least 250 milliseconds between the last repaint request and the start of your compilations. You might like to play around with these values a bit and see what suits you...
UPDATE
You could also try JComponent.paintImmediately
Paints the specified region in this component and all of its
descendants that overlap the region, immediately.
It's rarely necessary to call this method. In most cases it's more
efficient to call repaint, which defers the actual painting and can
collapse redundant requests into a single paint call. This method is
useful if one needs to update the display while the current event is
being dispatched.
I know this is an old thread, but in case someone is searching for an answer to this, SwingUtilities has a method called invokeAndWait(Runnable) that runs the task on AWT dispatching thread synchronously with your current thread. Note that this cannot be done if you need it to be done inside the Event dispatching thread.

Categories