I am a beginner in java and unfortunately I was stuck on this problem. In code:
NewJFrame.java :
public class NewJFrame extends JFrame {
public void showText() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel1.setText("in show()"); //it does not work
System.out.println("in show()"); //it works
}
});
}
public NewJFrame() {
initComponents();
jLabel1.setText("start"); //it works
}
public static void main(String args[]) {
Timer timer = new Timer();
timer.schedule(new NewClass(), 1000, 2000);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
private javax.swing.JLabel jLabel1;
}
NewClass.java :
package newpackage;
import java.util.TimerTask;
class NewClass extends TimerTask {
public void run() {
System.out.println("in NewClass.run()"); //it works
new NewJFrame().showText();
}
}
I have a problem with the fact that the setText does not set jLabel1 when is called from timer thread. I tried to solve the problem using invokeLater(), but still does not work.
Thanks for your help.
The JLabel is never added to any container. Why would it appear?
As general advice, don't extend frame, simply keep a reference and as mentioned by #Reimeus, use a Swing Timer.
You are creating a new instance of NewJFrame in NewClass which never gets displayed:
new NewJFrame().showText();
You would need to pass the visible instance to NewClass for it to be updated.
However, better to use javax.swing.Timer rather than java.util.Timer to interact with Swing components. From How to Use Swing Timers:
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.
Also See: Concurrency in Swing
Try adding repaint() right after you set the text.
After changing the look of something on screen, you must always repaint the frame.
jLabel1.setText("in show()"); //it does not work
repaint(); //now it works
System.out.println("in show()"); //it works
Related
So i want to print out a word every second for 10 seconds, but nothing is working
Here's my code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Main{
public static void main (String[] args){
class TimerListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("helo");
}
}
ActionListener dummy = new TimerListener();
Timer power_up_time = new Timer(10000,dummy);
power_up_time.addActionListener(dummy);
power_up_time.start();
}
}
EDIT: so i added the start function and it still doesnt work
I believe that you need to start a Timer in order to make it work.
Something like this:
Timer timer = new Timer(500, new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
timer.start();
In newer versions of Java (from the last ten years or so) I would suggest using a ScehduledExecutorService
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate(() -> System.out.println("hello"), 0, 1, TimeUnit.SECONDS);
TimeUnit.SECONDS.sleep(10);
ses.shutdown();
If the code you posted is complete you are starting the timer right before the application (i.e. the main thread) terminates. Thus the scheduler won't run long enough to execute the timer.
Try sleeping or running in a loop with some exit condition (that's probably what you'll want to do anyways).
You do not initialize and start the Swing toolkit, hence your application immediately terminates.
If you really want to use the Swing Timer, you need to show at least some window or dialog, so that the Swing event thread is running, e.g. by adding the following to the end of your main() method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JFrame("Frame title").setVisible(true);
}
});
See the other answers for alternatives outside of Swing.
Bellow is the code for the simplest GUI countdown. Can the same be done in a shorter and more elegant way with the usage of the Swing timer?
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class CountdownNew {
static JLabel label;
// Method which defines the appearance of the window.
public static void showGUI() {
JFrame frame = new JFrame("Simple Countdown");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel("Some Text");
frame.add(label);
frame.pack();
frame.setVisible(true);
}
// Define a new thread in which the countdown is counting down.
public static Thread counter = new Thread() {
public void run() {
for (int i=10; i>0; i=i-1) {
updateGUI(i,label);
try {Thread.sleep(1000);} catch(InterruptedException e) {};
}
}
};
// A method which updates GUI (sets a new value of JLabel).
private static void updateGUI(final int i, final JLabel label) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
label.setText("You have " + i + " seconds.");
}
}
);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showGUI();
counter.start();
}
});
}
}
Yes you SHOULD use a Swing Timer. You SHOULD NOT, use a util Timer and TimerTask.
When a Swing Timer fires the code is executed on the EDT which means you just need to invoke the label.setText() method.
When using the uitl Timer and TimerTask, the code DOES NOT execute on the EDT, which means you need to wrap your code in a SwingUtilities.invokeLater to make sure the code executes on the EDT.
And that is way using a Swing Timer is shorter and more elegant than your current approach, it simplifies the coding because to code is executed on the EDT.
You could make it a little more elegant by using Timer with an appropriate TimerTask.
Yes, use a timer. updateGUI would be the code for the timer task, but it will need some changes as you won't be able to pass in i for each call since you just get a run() method.
i know multithreading a bit but not in vast and i think the problem is of multithreading. I am calling a method to set label's text by invoking a new thread and leaving it blank after a specified time. I am getting the desired output every time but not only the place which i am going to show you by my piece of code. I am expecting that message should be set and disappeared after the specified time and the window should be minimized after that time. But what actually happening is when it is going to the other thread main thread execution starts and goes for sleep for 5 sec and the message is not appearing and after 5 sec window is getting minimized without showing the message which i am setting on the label.
(Main thread)
Validation.setMessageOnLabel("Username and password has been copied", jLabel15,1.5F);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
setState(ICONIFIED);
validation.java (setMessageOnLabel())
static public void setMessageOnLabel(final String msg, final JLabel label, final float time)
{
new Thread(new Runnable() {
#Override
public void run() {
label.setText(msg);
try {
Thread.sleep((long) (time*1000));
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
label.setText("");
}
}).start();
}
Since you're calling setState() directly, I assume the first code snippet is part of a JFrame. In that case you're most probably sending the event dispatch thread to sleep for 5 seconds and thus prevent screen updates during that time.
Put the sleep into another thread or use a swing worker instead and call setState() on the EDT in the worker's callback method, since setState() is not labelled as thread-safe and calling it on a thread other than the EDT might result in unexpected behavior.
From the linked tutorial:
Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
Don't use Thread.sleep(5000);, that block EDT.
For that purposes you can use swing Timer, examine next example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class TestFrame extends JFrame {
private JLabel lbl;
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
lbl = new JLabel(" ");
JButton setText = new JButton("setText");
setText.addActionListener(getActionListener());
add(lbl);
add(setText,BorderLayout.SOUTH);
}
private ActionListener getActionListener() {
return new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("wait...");
Timer t = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("");
setState(JFrame.ICONIFIED);
}
});
t.setRepeats(false);
t.start();
}
};
}
public static void main(String args[]) {
new TestFrame();
}
}
When dealing with Swing components you shuld not use threads like that. Launch your own SwingWorker instead.
public class MySwingWorker extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
//your code here
//dont forget to repaint changed component or validate parent of it,
//if your text dont shows up.
return null;
}
}
you can also execute your own runnable via SwingUtilites
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//again your code here...
}
});
Sorry for posting yet another thread about Java swing and loops. I have read through others and none of the solutions have really been appropriate for me. Okay so now onto the problem. I have a class extending JFrame in which I have a Timer running a stopwatch as below:
class StopWatch extends TimerTask{
public void run() {
time.setText(String.format("%02d:%02d", minutes_elapsed,seconds_elapsed));
seconds_elapsed++;
if(seconds_elapsed == 60){
seconds_elapsed = 0;
minutes_elapsed++;
}
}
}
Now the only way my Label "time" updates is when I call time.update(time.getGraphics()) but this paints over the previous time. Any ideas what I should do? I have also tried repaint,validate,and paint. Paint does the same thing for me that update does...Any Ideas?
P.S I don't know if it matters or not but I create the Timer in a public void start_timer() function which is like below:
public void start_timer(int minutes, int seconds){
if(timer == null)
timer = new Timer();
timer.scheduleAtFixedRate(new StopWatch(),0,1000);
}
I suspect you're using a java.util.Timer instead of a javax.swing.Timer. The latter will make sure that the timer runs on the UI thread, which I suspect is the problem.
See "How to use Swing Timers" for a tutorial on them. You'll want to convert your TimerTask into an ActionListener`, and then use
timer.setDelay(1000);
timer.setRepeats(true);
timer.start();
You can do this
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
// do stuff
}
}
));
}
but you really should be using the javax.swing.Timer though. Remember that Swing is single-threaded and as such, its components must be modified in its thread (i.e. Event Dispatch Thread).
When the program starts, a new JFrame is created. Once the user clicks the start button a thread is created and started. Part of this threads execution is to validate the data on the form and then execute with that data. Once the data has been validated the thread calls dispose() on the original frame and then creates a new JFrame that acts as a control panel.
There is also an automatic mode of the program that doesn't display any GUI at all, this mode reads data from a configuration file and then starts the execution thread and runs everything but without the control panel.
I want the program to end once the thread completes, but in GUI mode, only if the user has closed the control panel as well.
Is it possible to make the thread wait for the frame to close. I assuming that the frame is run from it's own Thread? or is that not the case.
Thanks.
The answer you chose is a little awkward. Using Thread.sleep(1000) will check for window state every second. It is not a performance issue, but just bad coding style. And you may have a one second response time.
This code is a little bit better.
private static Object lock = new Object();
private static JFrame frame = new JFrame();
/**
* #param args
*/
public static void main(String[] args) {
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (frame.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Working now");
}
}
};
t.start();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
synchronized (lock) {
frame.setVisible(false);
lock.notify();
}
}
});
t.join();
}
You can make reference from your thread to the JFrame. Then set the default close operation of JFrame to HIDE_ON_CLOSE. If the JFrame is closed, you can stop the thread.
Example code:
import java.awt.Dimension;
import javax.swing.JFrame;
public class FrameExample extends JFrame {
public FrameExample() {
setSize(new Dimension(100, 100));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setVisible(true);
}
private static class T implements Runnable {
private FrameExample e;
public T(FrameExample e) {
this.e = e;
}
#Override
public void run() {
while (true) {
if (e.isVisible()) {
// do the validation
System.out.println("validation");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
break;
}
}
}
}
}
public static void main(String[] args) {
FrameExample frameExample = new FrameExample();
new Thread(new T(frameExample)).start();
}
}
All Swing components, including JFrame, are managed by a single thread, called the Event Dispatch Thread, or EDT. (It's possible to call methods on Swing objects from other threads, but this is usually unsafe, except in a few cases not relevant here.)
You'll probably accomplish what you want here by putting the data validation and execution code in its own object which is otherwise completely unaware of the outside world. Then, call it from one of two other objects: one that manages a GUI, and another that runs in "automatic mode".