For example, I want a JTextfield to display different random numbers continuously with start, stop and resume buttons. What is the possible solution to automatically update the JTextField continuously when the start button is pressed?
I tried using while loop inside the start button's action listener but it just makes the button stuck in the while loop.
This is the part of the code that I tried.
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
while(true){
textField.setText(String.valueOf(random.nextInt()));
}
}
});
Read Concurrency in Swing.
You can use a javax.swing.Timer to change the text of the JTextField.
A tiny example:
public class TimerExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(()->{
JTextField field = new JTextField(10);
Timer timer = new Timer(100, e->{
field.setText(String.valueOf(Math.random()));
});
timer.start();
JOptionPane.showMessageDialog(null,field);
});
}
}
If you use while(true) in the Thread that runs the UI (this thread is called EDT - event dispatch thread), the thread won't be able to handle events since it is stucked inside the while loop.
Related
This question already has answers here:
Prevent Swing GUI locking up during a background task
(5 answers)
Closed 6 years ago.
I have written code using swing libs, that when added an actionlistener, won't update a progressBar.
Without a button and action listener, it works great. How to force a progressBar update as simply and cleanly as possible? Appended code is an easy to understand example that sums up my problem. If you comment out an ActionPerformed method and execute the program from main, it works just fine.
Do not just paste code whithout explaining.
ps.: I have seen this: swing progressBar threading
public class Okno {
private JProgressBar progressBar = new JProgressBar(0,306);
JFrame f = new JFrame("JProgressBar Sample");
JButton b = new JButton("start");
ActionListener a = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
barupdate();
}
};
private void barupdate(){
for(int p = 1; p<308;p=p+2){
System.out.println(p);
progressBar.setValue(p);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Okno(){
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
progressBar.setStringPainted(true);
f.add(progressBar, BorderLayout.SOUTH);
f.add(b, BorderLayout.NORTH);
b.addActionListener(a);
f.setSize(300, 300);
f.setVisible(true);
}
public static void main(String[] args) {
Okno okno = new Okno();
}
}
The problem is you have a loop where you are adjusting the progress bar setting that is being called from an action listener. The problem is, the bar won't update until after the listener is finished. And so you will get no updates. Not only that but you will bog down the gui because the window can't react to mouseclicks etc while you are in that action listener.
So the best way to handle this is instead to create a swing timer, in the action listener, and put the code for updating the button there, and start the timer in the action listener.
The timer should only update the bar once. and you should allow the fact that the swing timer will be called multiple times, to play the part of the repetitiveness. So you don't want to have any loops in your code.
Thread.sleep(50);
Don't use Thread.sleep(...). This will prevent the GUI from repainting itself until the loop has finished executing.
Instead you can use a SwingWorker.
Read the section from the Swing tutorial on Concurrency in Swing which has more information and contains a working example with a SwingWorker.
Also, look at the tutorial table of contents. There is a section on How to Use ProgressBars that also contains a working example. The tutorial is the first place to look for examples.
So, I am trying to figure out how to add pauses in my card game to make the CPU act like they are taking turns. But, there's too much code involved, so I think if we demonstrate the strategy on this code, it could work.
How can I make this code pause for, say 5 seconds after the button has been pushed and THEN print the message.
public class TimerTest extends JFrame implements ActionListener{
private static final long serialVersionUID = 7416567620110237028L;
JTextArea area;
Timer timer;
int count; // Counts the number of sendings done by the timer
boolean running; // Indicates if the timer is started (true) or stopped (false)
public TimerTest() {
super("Test");
setBounds(30,30,500,500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
area = new JTextArea();
area.setBounds(0, 0, 500, 400);
add(area);
JButton button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
// Initialization of the timer. 1 second delay and this class as ActionListener
timer = new Timer(5000, this);
timer.setRepeats(true); // Send events until someone stops it
count = 0; // in the beginning, 0 events sended by timer
running = false;
System.out.println(timer.isRepeats());
setVisible(true); // Shows the frame
}
public void actionPerformed(ActionEvent e) {
if (! running) {
timer.start();
running = true;
}
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
timer.stop();
count = 0;
running = false;
}
}
public static void main(String[] args) {
// Executing the frame with its Timer
new TimerTest();
}
}
What I really would like to do is to be able to insert a pause into my method that runs the actions of each CPU. But it sounds like, with the Timer class, you need to perform your post-pause actions in the Timer's action listener. I am mostly confused about why the Timer needs an actionListener. I don't need the timer to repeat itself after it is done.
If it would be better for me to post my own code, let me know. But I am not sure what parts would be useful, since I don't want to have tons of code in this thread.
Conceptually, you want to trigger some event after 5 seconds, Swing Timer is perfect for this AND it's thread safe, making it safe to update the UI from, without the need for more hoop jumping.
The Timer sets up it's own thread to wait in, so it won't block the EDT, this means that you need to wait till the timer is triggered before you can something, it won't block and wait where you call it, this is the reason it has an ActionListener, so you know when it's triggered.
You can use your existing Timer, assuming it's not doing anything else, but for argument sake, I'm creating a new one...
private Timer waitItOut;
private JButton button;
Then in your constructor, you set up the timer, the difference here is I've made it non-repeating, meaning it won't trigger every 5 seconds, but you can re-run it when you want...
waitItOut = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
doImportantWork();
}
});
waitItOut.setRepeats(false);
// I made this an instance field for demonstration purposes
button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
Then in your actionPerformed method, you simply start the timer...
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
waitItOut.restart();
}
And wait till it calls your "important method which should be run after the specified delay"...
public void doImportantWork() {
button.setEnabled(false);
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
count = 0;
running = false;
}
}
You should launch a new thread which does the following:
Sleep for 5 seconds
Sets your required message
Calls repaint() to signal that the message needs to be redrawn.
On a action performed situation, you can initiate certain functions like:
TimeUnit.SECONDS.sleep(1);
to do this. I see in your code that you are trying to manually count for time. You can let the compiler or cpu do this for you.
I have a JButton, lets call it "button" and added an ActionListener to it:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
call();
}
});
It is correctly added to my frame etc. In that JFrame I also have a JLabel, and I want to change its text while the JButton method is working(because it takes ~30 secs to finish). How do I do that? Do I have to use some multi-thread-thingy?
Here is the basic principle(the JLabel is called output):
public void call(){
output.setText("test1");
try { Thread.sleep(1000);
} catch (InterruptedException e) {}
output.setText("test2");
}
This will result in the "output" Label being changed to "test2" after one second. How can I make it instantly display "test1"?
Don't use Thread.sleep(). This will prevent the GUI from repainting itself.
Do I have to use some multi-thread-thingy?
Yes.
For a long running task you need to start a separate Thread, so the GUI can remain responsive.
In this case you can use a SwingWorker.
Read the section from the Swing tutorial on Concurrency for more information and an example of using a SwingWorker.
I am using swing timer to to load different pdf files in swing application
but I am facing a problem when ever I execute a program the screen remains blank for few seconds like 4 to 5 seconds and then the pdf file is rendered so during this time I want to show a message like please wait. Here is my sample code
if (type[i].equalsIgnoreCase("PDF")) {
int k = i;
pdfTimer = new Timer(0, (ActionEvent e) -> {
renderPDF(k);
});
pdfTimer.setDelay(1000*2);
pdfTimer.start();
Run rendering on SwingWorker's background thread (doInBackground) method. That way, your GUI will remain responsive. From done method you can notify user that rendering is done. Keep in mind not to update any Swing GUI from doInBackground method since it runs outside of EDT.
P.S. Swing Timer is for repetitive tasks.
You can display a progress bar and compute the renderding on a second thread.
JLabel lblWait = new JLabel("Please wait...");
lblWait .setBounds(116, 26, 113, 14);
contentPanel.add(lblWait );
final JProgressBar progressBar = new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setBounds(72, 66, 187, 16);
contentPanel.add(progressBar);
{
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
final JButton btFin = new JButton("Cancel");
btFin.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
dispose();
}
});
buttonPane.add(btFin);
}
}
Thread t = new Thread() {
public void run() {
//You launch the thread with your progress bar as an argument
maStructure.setaAfficher(new MgtSimulation(aAfficher, nombreSimulations).jouerSimulations(progressBar));
maStructure.actualiserAffichage();
dispose();
}
};
t.start();
}
And you change your progress bar value in your method
public BeanAffichage jouerSimulations(JProgressBar progressBar){
//Variables
for (int i = 0; i < nombreSimulations; i++) {
//Computing
progressBar.setValue(Whatever you want);
}
return aAfficher;
}
Just an alternative for SwingWorker
By default show some message like Loading... and create a Thread to run in the background which loads the PDF and updates the window
class BackgroundThread implements Runnable {
#Override
public void run() {
// the Swing call below must be queued onto the Swing event thread
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
// OK To make Swing method calls here
loadFile(args..);
repaint();//Or something similar that fits your purpose
}
});
}
}
I want to keep executing work while a button is pressed, using Java. When the button is released, the work should stop. Something like this:
Button_is_pressed()
{
for(int i=0;i<100;i++)
{
count=i;
print "count"
}
}
How might I achieve this?
One way:
Add a ChangeListener to the JButton's ButtonModel
In this listener check the model's isPressed() method and turn on or off a Swing Timer depending on its state.
If you want a background process, then you can execute or cancel a SwingWorker in the same way.
An example of the former:
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ButtonPressedEg {
public static void main(String[] args) {
int timerDelay = 100;
final Timer timer = new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button Pressed!");
}
});
JButton button = new JButton("Press Me!");
final ButtonModel bModel = button.getModel();
bModel.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent cEvt) {
if (bModel.isPressed() && !timer.isRunning()) {
timer.start();
} else if (!bModel.isPressed() && timer.isRunning()) {
timer.stop();
}
}
});
JPanel panel = new JPanel();
panel.add(button);
JOptionPane.showMessageDialog(null, panel);
}
}
I want to keep executing work while a button is pressed
Execute that process in another thread and then your form is not block and you can press the button to cancel or stop the execution.
see :
How to stop threads of a Java program?
Stop/cancel SwingWorker thread?
Control thread through button
You may need to use mousePressed event to start the action
And use mouseReleased event to stop the action (This is neccesary)
For more information refer here
For Android Apps
I know this question is old, but you can use:
while (yourButton.isPressed()) {
// Do Stuff
}