I'm trying to create a Java GUI which displays the current time. Currently, I can make it display the current time, but only on startup. It then simply remains on that time forever. The reason is that I cannot figure out how to make it automatically load in the new current time every second. Here is my relevant code thus far:
// Getting the current time...
long epoch = System.currentTimeMillis() / 1000;
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date(epoch * 1000));
// Creating the panel...
JLabel lblThetime = new JLabel(date);
sl_panel.putConstraint(SpringLayout.NORTH, lblThetime, 55, SpringLayout.SOUTH, lblIBeA);
sl_panel.putConstraint(SpringLayout.WEST, lblThetime, 139, SpringLayout.WEST, panel);
lblThetime.setFont(new Font("Avenir Next", Font.PLAIN, 40));
// Adding the time to the panel
panel.add(lblThetime);
// My refresher which doesn't work
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
removeAll();
validate();
repaint();
}
}, 1000, 1000);
I attempted to make a refresher using information from this thread, but to no avail, the window (when run) is just blank. I then tried making a different refresher using information from this thread, and created this:
// My refresher which doesn't work
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
contentPane.remove(lblThetime);
contentPane.add(lblThetime);
}
}, 1000, 1000);
With contentPane having been defined as private JPanel contentPane;
Which also didn't work, however only the time itself is blank, the rest of the content in the window (One other JLabel (just some text)) remains as normal.
Without any refresher it behaves as described above, whereby it just displays the time when it started and remains on that time forever.
I'm using Eclipse with WindowBuilder. (And I'm (probably evidently) a complete noob to Java GUI stuff xD)
I discovered the solution!
I tried all the solutions given as answers here, and none which gave code fully worked, however all answers pointed me in the right direction. I found the solution to the problem on a website which I have forgotten the name of, but I used its suggestion and came up with this final solution which worked:
// New timer which works!
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date(System.currentTimeMillis()));
lblThetime.setText(date);
}
};
new Timer(delay, taskPerformer).start();
Thank you to all who answered as without said answers I probably would not have been able to find this solution :D
First of all you are using java.util.Timer instead of javax.swing.Timer. You need use this second class when working with Swing components to ensure GUI updates are made on the Event Dispatch Thread. Also take a look to Concurrency in Swing tutorial.
As suggested in other answers, there is no need to remove/add JLabel each time you want to update its text. Just call JLabel.setText() method.
If you still want remove/add the JLabel each time, then be aware of this:
From Container.add() javadoc:
This method changes layout-related information, and therefore,
invalidates the component hierarchy. If the container has already been
displayed, the hierarchy must be validated thereafter in order to
display the added component.
Then you'll need to call Component.revalidate() method. Like this:
contentPane.remove(lblThetime);
contentPane.add(lblThetime);
contentPane.revalidate();
Instead of removing and adding the label to the panel you should change the string of the label every second.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date() );
lblThetime.setText(date);
}
}, 1000, 1000);
// Creating the panel...
JLabel lblThetime = new JLabel(date);
You can try to use Swing Timer for that task, for example:
private static JLabel l;
public static void main(String[] args) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
l.setText(new Date().toString());
}
});
timer.start();
JFrame f = new JFrame();
l=new JLabel(new Date().toString());
f.getContentPane().add(l);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
That example update JLabel with new date every second
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
label.setText(new Date().toString());
}
}, 1000, 1000);
Isn't it simpler?
Clue 1: really search for Timer classes in Java. Did you pick the correct one?
Clue 2: update the label text instead.
Related
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 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 was reading about Swing Timers and the example really looks nothing like I was trying to do so I found it logically confusing to apply it to my program. I'm starting to think I don't even need a timer for this.
Here is what I am trying to do:
I am making a JFrame program that has the user enter a credit card number into a JTextField. Before they do this there is a JLabel that says "Please enter your number into the text field", then once they enter this into the field and hit enter, depending on whether my code determines that the card number is valid or not valid, the JLabel will change to "Invalid" or "Thank you, processing."
However, I have unsuccessfully found a way to make it change text based, it just seems to stay with whatever text I initially give it.
So could someone please look at my code and change it to do what I am asking? That would be excellent. You guys have been helpful in the past.
public class CreditGraphics {
public String cardNum;
public JFrame frame;
public JPanel panel;
public JLabel label;
public JTextField text;
public Timer timer;
public CreditGraphics() {
frame = new JFrame("HI");
panel = new JPanel();
label = new JLabel();
text = new JTextField(16);
panel.add(label);
panel.add(text);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(500, 500));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
label.setText("Hi");
label.setText("Hello");
text.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cardNum = text.getText();
timer = new Timer(2000,this);
timer.setInitialDelay(1000);
timer.start();
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CreditGraphics();
}
});
}
public void checkCard(){
}
}
You have some major problems with your Timer's ActionListener as it is adding the anonymous inner ActionListener object, the this in the Timer's constructor, to itself. So it will call the very same actionPerformed inside the Timer that is called by the JButton that starts the Timer -- quite confusing. If your program were to need a Timer, you would do well to make sure to give it its own ActionListener, and not the same ActionListener that you add to your JButton as you're doing now.
Most importantly, do you even need a Swing Timer? I don't think so since you don't appear to be wanting an action repeatedly happening every xxx milliseconds, or an action that occurs once after xxx milliseconds, and since all you want to do is change the text. I suggest that instead you simply change your JLabel's text in the anonymous inner ActionListener class, and just leave it at that. If your requirements are different, then you will want to clarify and expand on your question.
So in semi-pseudocode, something like:
public void actionPerformed(ActionEvent e) {
String userText = text.getText();
if (testIfTextValid(userText)) { // some method to test if input OK
label.setText(INPUT_VALID); // String constant for JLabel to display
// here pass the userText to other parts of your code that needs to use it
} else {
label.setText(INPUT_INVALID);
}
}
I want to make a customized JSpinner for editing day hours. I want the format to be 1HH:mm:ss a1, I already get this part.
I also want to add in some functionalities of JFormattedTextField:
setplaceHolderCharacter('_')
For example the time is 09:23:45, when the user delete 09,
__:23:45 shows on screen.
input restriction
For example the time is 09:23:45, when the user delete 09,
and try to enter anything, he can only enter numbers, no letter is allowed.
Any help would be appreciated!
There may be a better solution with JFormattedTextField, but here is one with built-in java classes. Also this doesn't fit your requirement replacing the deleted numbers with '_'. But you can select the seconds and then spinn the seconds instead of the hours. Also there is only almost an input restriction. You may write letters, but no ChangeEvent will be fired. Additional bonus: You don't have to think about when to change to the next hour after 60 minutes or something and it works, of course, with everything a Date can have (so spinning months or years is possible, too)
public class HourSpinner implements ChangeListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new HourSpinner();
}
});
}
public HourSpinner() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(100, 70);
frame.setLocationRelativeTo(null);
Container contentPane = frame.getContentPane();
JSpinner dateSpinner = new JSpinner(new SpinnerDateModel(new Date(), null, null, Calendar.HOUR_OF_DAY));
DateEditor editor = new JSpinner.DateEditor(dateSpinner, "HH:mm:ss");
dateSpinner.setEditor(editor);
dateSpinner.addChangeListener(this);
contentPane.add(dateSpinner);
frame.setVisible(true);
}
#Override
public void stateChanged(ChangeEvent e) {
JSpinner source = (JSpinner) e.getSource();
System.out.println(source.getValue());
}
}
I have a maybe simple question. Obviously because my program does not do what it is supposed to...
First of all my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
public class Timer
extends JFrame
implements ActionListener
{
protected class TThread extends Thread{
private boolean running = false;
#Override
public void run() {
int timer = 10,
index = 0;
running = true;
while(running){
try {
out.setText(timer + " Secs");
timer--;
if(timer == 0){
if(index % 2 == 0){
timer = ti1;
out.setBackground(Color.red);
}else{
timer = ti2;
out.setBackground(Color.green);
}
index++;
}
sleep(1000L);
} catch (InterruptedException e) {
}
}
}
#Override
public void interrupt() {
running = false;
}
}
private static final long serialVersionUID = 1L;
private JTextField t1 = new JTextField(),
t2 = new JTextField();
private int ti1 = 0, ti2 = 0;
private JLabel l1 = new JLabel("Zeit 1"),
l2 = new JLabel("Zeit 2"),
out = new JLabel("00 Secs", SwingConstants.CENTER);
private JButton go = new JButton("Go"),
stop = new JButton("Stop");
private JPanel cont = new JPanel();
private TThread tr = new TThread();
public Timer() {
super("Timer");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(800, 600);
setLayout(null);
add(cont);
cont.setBounds(0, 0, getWidth(), 200);
cont.setLayout(new GridLayout(3, 2));
cont.add(l1);
cont.add(t1);
cont.add(l2);
cont.add(t2);
cont.add(go);
go.addActionListener(this);
cont.add(stop);
stop.addActionListener(this);
add(out);
out.setBounds(0, 200, getWidth(), getHeight()-200);
out.setFont(new Font("Arial", Font.BOLD, 72));
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
SwingUtilities.updateComponentTreeUI(this);
} catch (UnsupportedLookAndFeelException e) {
}
}
public static void main(String[] args) {
Timer t = new Timer();
t.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(go)){
ti1 = Integer.parseInt(t1.getText());
ti2 = Integer.parseInt(t2.getText());
tr.run();
}else if(e.getSource().equals(stop)){
tr.interrupt();
}
}
}
So back to my problem:
If I run the program and hit the 'Go'-Button after entering some numbers the program gets stuck. I think the issue is caused by the while-loop in TThread.
It's quite a time since I last used Threads and now i searched for a long time and nothing worked for me...
Hopefully someone can tell me what the issue is and can give a solution or some hints how to solve the problem.
Greetings
max.
You never run the thread in a background thread by calling start() on it. Instead you call run() which runs it on the current thread and not in a background thread. To solve this, call start() on your Thread object, not run().
so not:
tr.run();
but rather:
tr.start();
Other issues:
You're generally better off having classes implement Runnable rather than extend Thread.
You're trying to make Swing calls from a background thread which shouldn't be done. Almost all Swing method calls should be done on the Swing event thread or EDT. For more on this, please see: Concurrency in Swing.
One of your class names, Timer, is the same as a critical Swing class, the javax.swing.Timer class. I'd rename your class to avoid confusion, especially if you would want to use a Swing Timer.
And in fact on view of your code, I think that you would be much better off using a Swing Timer instead of a background Thread for your application. This is a much easier way to do your timer animation and makes it much easier to ensure that Swing calls are made on the Swing event thread. Please check the Swing Timer tutorial.
You are using null layout. While to a newbie a null layout often appears to be the best way to easily create complex GUI's, as you gain more Swing GUI experience you will find that actually the opposite is true. If you use layout managers, often nesting JPanels, each with its own layout manager, you can more easily create complex, beautiful GUI's, that easily resize, that look good on all platforms, and that are much easier to maintain, debug and enhance.