Been trying at this a little while, going no where fast. I have elements in a JFrame that I want to update/toggle viability for when I click my play button (which triggers the ActionEvent). However, these only seem to be updating after the actionPerformed method is finished. This is what I think is happening because the constructor for my SoundPlayer object triggers (Thread.sleep)s, causing a noticeable delay. I did read here that using Thread.sleep() locks up a GUI, but I make my changes before calling SoundPlayer, so I don't think that would be the issue.
As seen below, I've tried repainting the JFrame in general, as well as the individual element. These do not update until after the SoundPlayer's delay is finished, though my println statement prints a the correct time.
MultiThreading has crossed my mind as a solution, though I don't see why I would need to do so. Any help on this matter is appreciated!
public void actionPerformed(ActionEvent e){
int channel = 0, volume = 0; //Assigned for safety.
String musicNotes = ""; //Will be filled with the under-input.
boolean willPlay = true; //Assumes will be played.
/*Stuff that makes 'willPlay' either true of false*/
//If nothing is wrong, plays the String!
if (willPlay) { //If above parameters are good...
badNums.setVisible(false);
prog.setVisible(true);
if (vis.isSelected())
prog.setText("Estimated duration: " + estiDuration(musicNotes)*(0.4) + "seconds");
else
prog.setText("Duration: " + estiDuration(musicNotes)*(0.3) + "seconds");
System.out.println("test");
repaint();
prog.repaint();
new SoundPlayer(channel, volume, musicNotes); //Plays the music!
} else {
vis.setVisible(false);
badNums.setVisible(true);
}
}
Swing is single threaded - all code for painting, events, etc... runs on this Thread (called the EDT). If you have a long running task and place it on the EDT, it can do nothing else until it is complete (note that calling repaint doesn't directly repaint the Component, so running something lengthy after this call does not mean the Component will actually paint itself before). If you need to perform lengthy operations, do so in a different Thread - this can be accomplished either directly using a Thread, or using a SwingWorker. If the former, be sure any calls to Swing get dispatched to the EDT using SwingUtilities.invokeXXX
Related
So, I have an object that extends JPanel and displays dots in a matrix via paintComponent. The dots of the matrix can move, disappear or multiply given certain conditions, and I want to show their evolution over time automatically like so:
for(int i = 0; i < 100; ++i){
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint(); //Draw new dots
Thread.sleep(100); //Wait 0.1 seconds for next iteration (yes, this should be in a
//try-catch)
}
However, I only get drawn the last iteration after the loop is finished, and all the previous calls to repaint() are basically ignored. If I do the iterations only one at a time (for example, via a manual button press), I have no problem.
Is there any way to get multiple, periodic repaint calls automatically?
I had a simile problem with JComponent in my library and I found a solution with swing timer, I reported the java description of timer
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
I think you are in one of this cases.
Without a minimal example reproducible, I can use the my code.
You should create the Swing action listener, like this:
public class UpdateComponentListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
count += 10;
timeLabel.setText(count + "");
//The label call repaint
//in your app you should be call the repaint
//In your cases
/*
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint();
*/
}
}
The timer constructor get in input the delay and the action listener, so you can build your timer, with this code:
Timer timer = new Timer(1000, new UpdateComponentListener());
timer.start();
You can stop, restart your timer, so you should be set how propriety the timer.
The GUI example:
I wrote the post and after I see the #camickr comment. I post the answer because my work is finished but, the comment answered your question.
I hope to have to build a food example
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I have this code in java
private void buttonShowImageActionPerformed(java.awt.event.ActionEvent evt)
{
if(folderFiles != null)
{
for(int i=0; i<folderFiles.size(); i++)
{
icon = new ImageIcon(folderFiles.get(i));
labelImage.setIcon(icon);
timeDelay(2); // A method that delays 2 secs - it works
}
}
}
When I push the button, it waits some time, and from the 1st goes to the last image, and skips the images from the folder one-by-one
The delay method works (I tested it)
Thanks in advance !
Regarding:
timeDelay(2); // A method that delays 2 secs - it works
No, it doesn't work, regardless of how you tested it. Swing doesn't work that way, and you would do well to avoid making such assumptions. Use a Swing Timer.
You will ask, how do I know that it doesn't work, and I'll tell you: that code does not call a background thread, nor does it start a Swing Timer, so the only thing it can do is delay the current thread, likely somewhere with a Thread.sleep(...). If you call this and "test" this, yes, it will cause delays that will show up in your System.out.println(...) statements to the console, but it will also sleep the Swing event thread, and put your application to sleep. So you really don't want to do this.
And in fact the best test to see if this works in Swing is your current code. What happens then? You state:
When I push the button, it waits some time, and from the 1st goes to the last image, and skips the images from the folder one-by-one
So in fact you know for a fact that your delay doesn't work for Swing, as you describe the classic symptoms of code that stomps on the Swing event thread, bringing it to its knees. So again, use a Swing Timer. Here's a link to the Swing Timer Tutorial.
If this were my code, I'd not read in the images each time the button is pushed, but instead read them all at once, and one time only and put them in an ArrayList<ImageIcon> say called iconList. Assuming that you did this, then the code could look like:
private void buttonShowImageActionPerformed(java.awt.event.ActionEvent evt) {
// !! I'd use a variable or constant instead of the magic number 2000
new Timer(2000, new ActionListener() {
int count = 0;
actionPerformed(ActionEvent e) {
if (count != iconList.size()) {
labelImage.setIcon(iconList.get(count));
count++;
} else {
// stop the Timer
((Timer) e.getSource()).stop();
}
}
}).start();
}
Edit
You ask:
My friend one last question ..... I put the "int count = 0;" inside the actionPerformed and nothing happend ... I cannot understand why it works only if it is outside the method .... ?
Please understand that the way a Swing Timer works is that the actionPerformed method is called repeatedly, here every 2000 seconds. What my code does is that when the timer is started, the count is set to 0. Each time the actionPerformed method is called, count increments by 1, and the next ImageIcon is displayed.
With your code, when the actionPerformed method is called, the count is re-set to 0, the first image is displayed, and the count is then incremented. But each time the actionPerformed method is called, your code resets the count back to 0, so the incrementation has no effect.
In a method of a class, I update the same label twice. The first time, it shows the user message to wait, but the second time shows the user the completed message. Something like the following:
MyClass{
myMethod(){
jLabel.setText("Please wait...");
//does calculation
jLabel.setText("Completed successfully!");
}
}
When I run the app, all I see is the "Completed successfully" message. Is the JLabel updating too quickly? How do I control it? I tried using the following but no luck :(
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jLabel.setText("Please wait...");
}
});
If the calculation is done in the event dispatch thread, then it blocks the thread and prevents it from doing all its repaintings. You must do the computation in another thread, and have this thread change the label text when it ends (in the event dispatch thread, using SwingUtilities.invokeLater, or by using the SwingWorker mechanism). If the computation is really fast, it's not worth it, though, because the second text will appear so quickly after the first one that you won't even notice the first one.
Have a look at SwingWorker, which is designed for such use-cases. Its javadoc contains a useful example.
if you want to delay some Action/Event then use javax.swing.Timer, or wrap your code to the Runnable#Thread,
notice: never use Thread.sleep(int) durring EDT, your GUI freeze until Thread.sleep(int) ended
example for javax.swing.Timer & Runnable#Thread & Freeze GUI by implements Thread.sleep(int) durring EDT here
Possibly because your calculations are happening too fast. Did you try putting a delay after your calc.
Also as Nizet points out above if this is happening in EDT the component will not repaint until end of the thread which means it will take the last set value.
Hi im trying to setText to JTextArea in loop but I want to do it, thtat in each loop every line will be seen in frame.
I have tryied with Thread.sleep(500), becouse I thought loop is too fast to set each line, but its didnt help.
Is it possible ?? to do it ? I want to do it to show to user progress with downloading files from database.
the nature of event-based singlethreaded guis makes it so that the changes are only visible once the event is fully handled (returned from the event handler)
blocking the event dispatch thread won't help (and even makes the entire app unresponsive)
you should use a timer to simulate the adding one at the time with a delay in between
final String[] lines;
for(int i=0; i<10; i++){
Timer t = new Timer(500*i,new ActionListener(){
int ind=i;
void actionPerformed(ActionEvent e){
area.setText(area.getText() + "\n ...");
}
});
t.start();
}
this creates 10 timers each adding a line after some time (increments of 500)
there's a better way to do this that reuses the timer and stops it after everything is done but it's a bit more verbose
I think JTextArea.append(text) might be more useful.