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();
Related
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();
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.
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().
I have been trying this all day in different variations with little success. Could someone please help explain what I am doing wrong? I am just a beginner with regards to threads.
private JTextArea text = new JTextArea();
private JButton button = new JButton("Cancel");
public StatusFrame() {
text.setEditable(false);
this.add(text);
this.add(button, BorderLayout.EAST);
this.setSize(new Dimension(150, 100));
this.setVisible(true);
}
public void updateStatus(String textIn) {
text.setText(textIn);
}
public JButton getButton() {
return button;
}
In another class, I am calling methods which may take a while to complete. I want to be able to call the StatusFrame.updateStatus() method to keep the user informed on the progress.
This is what I have:
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
}
The frame appears but none of the code after the runnable appears to be run/processed. It just stops/blocks/something. But the GUI remains active
Thanks for any advice.
P.S.: I have tried using invokeAndWait() method but again not sure if I am doing it the right way. For now a quick fix would be preferred as I have not learned much about threads yet. Any instructions are welcome.
You have the concepts backwards.
Here's your code
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
//do something
status.update("process 1 completed");
//do something else
status.updateStatus("Process 2 completed");
You should execute the long running code in a thread, and use the SwingUtilities invokeLater method to update the GUI.
someMethod() {
// prevent GUI from freezing using threads
final Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
new Thread(r).start();
// inside the StatusFrame
//do something
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("process 1 completed");
}
);
//do something else sometime later
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update("Process 2 completed");
}
);
I don't know if I was clear in my answer.
Execute SwingUtilities.invokeLater when you start your Java application to make sure Swing components are on the Event Dispatch thread (EDT).
From the EDT, invoke long running processes as a runnable thread.
In the runnable thread, since you're not on the EDT, execute SwingUtilities.invokeLater whenever you're updating Swing components. This ensures that Swing components are updated on the EDT.
Every Swing application should start with a class like this:
import javax.swing.SwingUtilities;
import com.ggl.text.entry.model.TextEntryModel;
import com.ggl.text.entry.view.TextEntryFrame;
public class TextEntry implements Runnable {
#Override
public void run() {
new TextEntryFrame(new TextEntryModel());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new TextEntry());
}
}
This class does 3 things.
Constructs the GUI data model.
Constructs the GUI JFrame.
Ensures that the Swing components are on the EDT.
You'll need to call the updates on EDT too. I would suggest to sleep on the main thread, to give GUI a chance to show up before any other work:
someMethod() {
// prevent GUI from freezing using threads
Runnable r = new Runnable() {
public void run() {
status = new StatusFrame();
}
};
SwingUtilities.invokeLater(r);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
//do something
r = new Runnable() {
public void run() {
status.update("process 1 completed");
}
};
SwingUtilities.invokeLater(r);
//do something else
r = new Runnable() {
public void run() {
status.update("Process 2 completed");
}
};
SwingUtilities.invokeLater(r);
}
See Concurrency in Swing.
You may find using a Swing Worker easier to work with since it uses a Thread and has methods that will allow you to update the GUI properly.
Hello guys I am doing a thread to update a ball over JFrame so I repaint the screen... and then paint the ball update its position .. and then draw the screen again ... draw the ball and the same cycle ... here is the code
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
Thread t = new Thread()
{
public void run()
{
while(true)
{
repaint();
b2.update(ob,2);
b2.paint(ob.getGraphics());
b2.setT(b2.getT() + 1);
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
System.out.println("Error in Sleeping");
}
}
}
};
t.start();
}
but the problem is that I don't see the ball... the paint of the screen always overrides the ball and the ball is like down under the Jframe ..
If you want to have animations in Swing, the recommended class to use is the javax.swing.Timer . This class allows you to perform operations on the Event Dispatch Thread at regular intervals.
The Swing Timer tutorial
An animation example posted here on SO (which is linked in the Swing wiki here on SO btw)
Some General Rules
Swing is not thread safe, you should only ever update UI components from within the context of the Event Dispatching Thread.
You do not control the paint process, the repaint manager does. You can request updates to occur by calling repaint, but you should never call update and paint directly when trying to update the display.
The Graphics context used by the paint sub system is a shared resource and is not guaranteed to be the same between paint cycles, you should never maintain a reference to it. You should also not rely on the results from JComponent#getGraphics this method is capable of returning null.
An Example Solution
You have a number of options, depending on what you want to ultimately achieve.
You could use a SwingWorker, but given the fact that all your going to is enter an infinite loop and it would easier to use SwingUtilities#invokeLater then actually use the publish method, this approach would actually be more work.
You could also use a Thread, but you'd end up with the same problems as using a SwingWorker
The simpliset solution, for what you're presented, is actually a javax.swing.Timer
public class Blinky {
public static void main(String[] args) {
new Blinky();
}
public Blinky() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BlinkyPane extends JPanel {
private JLabel blinkyLabel;
private boolean blink = false;
public BlinkyPane() {
setLayout(new GridBagLayout());
blinkyLabel = new JLabel("I'm blinking here");
blinkyLabel.setBackground(Color.RED);
add(blinkyLabel);
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
blink = !blink;
if (blink) {
blinkyLabel.setForeground(Color.YELLOW);
} else {
blinkyLabel.setForeground(Color.BLACK);
}
blinkyLabel.setOpaque(blink);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
}
}
You can take a look at Swing Timer and Concurrency in Swing for more info
If you access GUI components outside the EDT (Event Dispatch Thread) then you might encounter strange problems, Contrary if you perform long running tasks in the EDT then also you will get problems.
Check this post for more info on GUI Threading in Java