Could anyone explain why my start/stop button doesn't work please? This is not a full implemented StopWatch but I got stuck here. Any help is appreciated! This is my first time posting question in forum so if there is any problem in my post, please tell me.
This is my code:
public class StopWatch {
private static boolean tiktok;
public static void setGo(boolean go) {
tiktok = go;
}
public static void main(String[] args) {
int counter = 0;
StopWatch stop = new StopWatch();
ClockFrame window = new ClockFrame("StopWatch");
JLabel lb = window.init();
while (true) {
lb.setText(Integer.toString(counter++));
if (counter == 61) {
counter = 0;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
}
class ClockFrame extends JFrame {
JLabel hour, minus, sec;
public ClockFrame(String title) {
super(title);
}
JLabel init() {
JFrame frame = new JFrame("Stop Watch");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel time = new JPanel();
hour = new JLabel("0");
minus = new JLabel("0");
sec = new JLabel("0");
time.add(hour);
time.add(minus);
time.add(sec);
JPanel pane = new JPanel();
pane.setLayout(new FlowLayout());
JButton start = new JButton("Start");
start.addActionListener(new startstopActionListener(true));
JButton stop = new JButton("Stop");
stop.addActionListener(new startstopActionListener(false));
JButton reset = new JButton("Reset");
pane.add(start);
pane.add(stop);
pane.add(reset);
Container window = frame.getContentPane();
window.setLayout(new GridLayout(2, 1));
window.add(pane);
window.add(time);
frame.setSize(500, 200);
frame.setVisible(true);
return sec;
}
}
class startstopActionListener implements ActionListener {
private boolean b;
public startstopActionListener(boolean b) {
this.b = b;
}
#Override
public void actionPerformed(ActionEvent e) {
StopWatch.setGo(b);
}
}
You don't respect Swing's threading policy:
Swing components should only be used from the event dispatch thread
Long-running and blocking methods (such as the one with the infinite loop updating the label) should be run out of the event dispatch thread (but the update of the label must be made in the EDT - see rule 1)
Read the Swing tutorial about concurrency.
If you want to make stopwatch in Swing, you best take a look at the javax.swing.Timer class. It makes it very easy to periodically update a Swing component (in your case a JLabel). Using the Timer avoids the Thread.sleep call, which you should never call on the Event Dispatch Thread as it blocks the UI.
JB Nizet already provided a link to Swing concurrency tutorial. I would suggest you also take a look at the links provided in the Swing concurrency section of the 'Swing info page' of this site, and my answer on a related question.
Related
I have 2 icons, and I want them to change every second. I also want it to always run and not stop. I am doing it with this code but I am not successful.
public static void main(String[] args) {
Timer timer = new Timer();
JFrameLeds jframeLeds = new JFrameLeds();
jframeLeds.setVisible(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
Icon icono;
icono = new ImageIcon(getClass().getResource("camera.png"));
jframeLeds.jLabel1.setIcon(icono);
icono = new ImageIcon(getClass().getResource("target.png"));
jframeLeds.jLabel1.setIcon(icono);
}
};
timer.schedule(timerTask, 0, 1000);
}
Using Thread class, or TimerTask is not recommended in a Swing environment. You should be using Swing Timers or Swing Workers since component updates should only take place to the Event Dispatch Thread. Take a look at this example.
However, in your case a flag boolean might be required in order to achieve what you want. An example that changes icons to a label:
public class ChangeIconsTest extends JFrame {
private boolean icon1IsActive;
public ChangeIconsTest(Icon icon1, Icon icon2) {
super("test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JLabel label = new JLabel(icon1);
icon1IsActive = true;
Timer swingTimer = new Timer(1000, e -> {
label.setIcon(icon1IsActive ? icon2 : icon1);
icon1IsActive = !icon1IsActive;
});
swingTimer.start();
add(label);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
ImageIcon icon1 = new ImageIcon(
new URL("https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg"));
Icon icon2 = new ImageIcon(new URL("https://www.sample-videos.com/img/Sample-png-image-500kb.png"));
ChangeIconsTest test = new ChangeIconsTest(icon1, icon2);
test.setVisible(true);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
}
As many people here reminded, using TimerTask class from java.util is highly NOT recommended while working in Swing or JavaFX environment.
The Swing components aren't thread-safe, changing the state or repainting the components in different thread than the one used by Swing components may lead to unexpected behaviour and strange bugs.
The Swing and AWT components are using Event Dispach Thread as main background thread to process the events. Events are fired inside every component method that might cause the change of interface. The setIcon() and even setText() methods of JLabel are also firing an event to the EDT.
To avoid future bugs every component state change should be done undnder EDT. The EDT can be called through EventQueue.invokeLater(Runnable), but since you are using Swing, you can call the SwingUtilities.invokeLater(Runnable) which calls the EventQueue inside.
The invokeLater method schedules the task and returns, there's also a invokeAndWait which schedules the task and waits until it's finished before returning.
For the sample below I borrowed the icon urls from the George Z. answer.
Sample code for covering the timed icon change:
public class TimedIconChange {
static String ICON_1_URL = "https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg";
static String ICON_2_URL = "https://www.sample-videos.com/img/Sample-png-image-500kb.png";
static String ICON_3_URL = "http://www.frankieballard.com/sites/g/files/g2000005856/f/Sample-image10-highres.jpg";
public static void main(String[] args) throws MalformedURLException {
Icon icon1 = new ImageIcon(new URL(ICON_1_URL));
Icon icon2 = new ImageIcon(new URL(ICON_2_URL));
Icon icon3 = new ImageIcon(new URL(ICON_3_URL));
List<Icon> circularIcons = new ArrayList<>() {
int i = 0;
#Override
public Icon get(int index) {
return get();
}
private Icon get() {
if (i == size()) {
i = 0;
}
return super.get(i++);
}
};
circularIcons.add(icon3);
circularIcons.add(icon2);
circularIcons.add(icon1);
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
JLabel label = new JLabel();
label.setIcon(icon1);
frame.setLayout(new BorderLayout());
frame.add(label);
frame.pack();
frame.setVisible(true);
new Timer(1000, e -> label.setIcon(circularIcons.get(0))).start();
});
}
}
The sample contains a little implementation of circular list for circularIcons variable, to reduce the need of using boolean flag.
Additionaly, for longer tasks which are supposed to be working in the background using the SwingWorker class is recommended.
References and further reading on EDT:
https://en.wikipedia.org/wiki/Event_dispatching_thread
Why should I use a separate thread to show a GUI in JAVA
Why does my boilerplate Java desktop app JFrame use EventQueue.invokeLater in the main method?
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
public class Pragrassbar extends JFrame implements MouseListener {
JProgressBar jb;
int i = 0, num = 0;
JButton jbt;
JLabel jl;
Pragrassbar() {
setSize(400, 400);
setLayout(new FlowLayout());
jbt = new JButton("Start");
jl = new JLabel("Click Start!");
jb = new JProgressBar(0, 2000);
jb.setValue(0);
jb.setStringPainted(true);
jbt.addMouseListener(this);
add(jbt);
add(jb);
add(jl);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void mouseClicked(MouseEvent me) {
jl.setText("downloading.....");
jbt.setEnabled(false);
try {
while (i <= 2000) {
jb.setValue(i);
jb.paintImmediately(0, 0, 200, 25);
i = i + 40;
Thread.sleep(30);
}
jl.setText("download complete");
jbt.setEnabled(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Pragrassbar m = new Pragrassbar();
m.setVisible(true);
}
setText() not functioning under mouseClicked function of MouseListener interface
although it works after the while loop that i have used
The Thread.sleep() causes the Event Dispatch Thread (EDT) to sleep which means the GUI can't repaint itself until the looping code is finished executing.
Don't use Thread.sleep. Instead long running code should execute in a separate Thread so you don't block the EDT. You should probably use a SwingWorker for this.
Read the section from the Swing tutorial on Concurrency for more information and working examples on this approach.
You can also read the section on How to Use Progress Bars for the proper way to do this.
Let me explain what I am trying to do.
I have two classes extending JFrame, the StartJFrame and TestingJFrame. In the main method, I start up a StartJFrame. It has a single button, start. When that is pressed, I have it set up to hide that frame and start up the TestingJFrame. Right now I don't have anything in the TestingJFrame.
In that screen, I want to have a label in the bottom right corner that is a timer, starting on 45 seconds and counting down to 0. I also need to have some code run every 10th of a second, and collect some data. There will be two buttons in the TestingJFrame, Yes and No. When one of them is pressed, it should stop the timer and save the information.
The data is basically just doubles. I am only going to be collecting data once per run of the program. I have a UserData class that holds some information about the test subject, and a list of doubles, it is added to every 10th of a second. I have a general idea how to save the data in java.
My question is, how should I set up the timer, so that it will count down from 45 seconds, and when it reaches 0 or the user presses the yes or no button it will call a function to save the data? I think I can handle the saving data part.
Sorry if this is really easy, I'm new to Java (from c#) and Swing has been confusing me a bit.
The first part (show the count down and stopping the timer) is relatively easy...
public class TimerTest01 {
public static void main(String[] args) {
new TimerTest01();
}
public TimerTest01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer timer;
private long startTime;
public TestPane() {
setLayout(new GridLayout(0, 1));
label = new JLabel("...");
label.setHorizontalAlignment(JLabel.CENTER);
final JButton btn = new JButton("Start");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timer.isRunning()) {
timer.stop();
btn.setText("Start");
} else {
startTime = System.currentTimeMillis();
timer.restart();
btn.setText("Stop");
}
repaint();
}
});
add(label);
add(btn);
timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long endTime = (startTime + 45000);
long timeLeft = endTime - System.currentTimeMillis();
if (timeLeft < 0) {
timer.stop();
label.setText("Expired");
btn.setText("Start");
} else {
label.setText(Long.toString(timeLeft / 1000));
}
repaint();
}
});
}
}
}
Take a look at Swing Timer for more info
The second part of you question is to vague to reliably provide you with an answer. Where is the data coming from? How is collected??
You can use javax.swing.Timer to setup your timer. You can have a look at the official tutorial too.
I am working on an application that executes some functions that run for long. To let the user aware that the processing is taking place, I needed a label that can display some label that can represent that. So, I created a small widget for such a label.
The program below runs find and I get the output as I wanted.
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**
* This is an extension to a JLabel that can be used to display an ongoing progress.
* #author Ankit Gupta
*/
public class ProgressLabel extends JLabel {
/**
* The prefix label to which periods are added.
*/
private String startLabel;
/**
* The label to display end of an operation.
*/
private String endLabel;
/**
* Flag to indicate whether the animation is running or not.
*/
private boolean running = false;
//list to hold intermediate labels
List<String> intermediateLabels;
public ProgressLabel(String slbl, String elbl) {
this.startLabel = slbl;
this.endLabel = elbl;
//initialize all the labels to be used once as creating them again and again is expensive
intermediateLabels = new ArrayList<String>();
intermediateLabels.add(startLabel+".");
intermediateLabels.add(startLabel+"..");
intermediateLabels.add(startLabel+"...");
intermediateLabels.add(startLabel+"....");
}
public void done(){
running = false;
}
public void start(){
running = true;
new LabelUpdateThread().start();
}
private class LabelUpdateThread extends Thread{
int i;
public LabelUpdateThread(){
i=0;
}
#Override
public void run(){
while(running){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
setText(intermediateLabels.get((i++)%3));
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {}
}
setText(endLabel);
}
}
public static void main(String []args) throws InterruptedException{
final JFrame frame = new JFrame("Testing ProgressLabel");
JPanel panel = new JPanel();
ProgressLabel progressLabel = new CZProgressLabel("Searching", "Done");
panel.add(progressLabel);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(500,500));
frame.pack();
progressLabel.start();
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
frame.setVisible(true);
}
});
Thread.sleep(5000);
progressLabel.done();
}
}
However, when I tried to include this in the application, it did not work as expected. I created a small panel with a button and in the actionPerfomed() code for the button I used the ProgressLabel's start() and done() methods as before but this time, the label just did not update to Done until the length process finished. Here is another piece of code using the ProgressLabel with actionPerformed() :
public class SearchPanel extends JPanel {
private JTextArea queryBox;
private JButton searchBtn;
private ProgressLabel progressLabel;
private JSeparator queryAreaSeparator;
public SearchPanel() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
//First Row
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.gridx = 0;
queryBox = new JTextArea();
queryBox.setRows(25);
queryBox.setColumns(25);
this.add(queryBox, gbc);
//Second Row
gbc.gridy = 1;
gbc.gridwidth = 1;
progressLabel = new ProgressLabel("Searching", "Done");
this.add(progressLabel, gbc);
gbc.gridx = 1;
searchBtn = new JButton("Search");
this.add(searchBtn, gbc);
searchBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
progressLabel.start();
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
}
//the above sleep() call will be replace by some time-consuming process. It is there just for testing now
progressLabel.done();
}
});
gbc.gridx = 0;
}
/**
* function to test CZSemanticSearchLabel
*/
public static void main(String[] args) throws InterruptedException {
final JFrame frame = new JFrame();
CZSemanticSearchPanel panel = new CZSemanticSearchPanel();
frame.add(panel);
frame.setPreferredSize(new Dimension(500, 500));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Thread.sleep(10000);
frame.dispose();
final JFrame frame1 = new JFrame("Testing ProgressLabel");
JPanel panel1 = new JPanel();
CZProgressLabel progressLabel = new CZProgressLabel("Searching", "Done");
panel1.add(progressLabel);
frame1.add(panel1);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setPreferredSize(new Dimension(500, 500));
frame1.pack();
progressLabel.start();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame1.setVisible(true);
}
});
Thread.sleep(5000);
progressLabel.done();
}
}
I believe that I have screwed something with Swing's Event dispatch model. But, I cannot figure what? Can someone tell me what is wrong with this code and how do I correct it?
I don't know about your actual code, but your sample code is flawed...
In your ActionListener you are doing this...
progressLabel.start();
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
}
//the above sleep() call will be replace by some time-consuming process. It is there just for testing now
progressLabel.done();
This will STOP the Event Dispatching Thread, preventing any repaint requests from the been handled (ie no screen updates) for 10 seconds...this will also make your application look like it's "hung".
I updated you ActionListener to read like this (note I added a isRunning method which returns the running member from the label)
if (progressLabel.isRunning()) {
progressLabel.done();
} else {
progressLabel.start();
}
And it works fine.
You might like to read through Currency in Swing for some more ideas.
Also, as already suggested, SwingWorker may be a better approach
Instead of implementing this yourself with threading, you can use SwingWorker: Simple Background Tasks that is made for such things, and the linked example is very similar to your problem.
Your start() doesn't execute your LabelUpdateThread().run() but your LabelUpdateThread().start().
I have created a form on which two components are present, button and progressbar (Netbeans drag and drop).Form contains the main method from where my application starts.I have created another class as well in which i have written a function.What i want is that when i press a button the application goes into the function and the progressbar runs simultaneously with it and when that function is complete with its functionality the the progress bar shows 100% complete.Now this function can take anytime for its completion so i cannot set the max value for the progressbar.So, what to do in this case?Can anyone please provide me with a good example .
JProgressBar.setIndeterminate(true)
Since what sort of a work you are doing inside that so called "Called Function", so it's tough to say, what you want in the scenario, though you can put your lines like progressBar.setValue(someProgress); at regular intervals with it's Indeterminate State to true, and at the end of the function you can simply say that progressBar.setValue(100); and the Indeterminate State will turn to false here, so that it can show that to the end user.
Have a look at this sample program :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ProgressExample
{
public static JProgressBar progressBar;
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("Progress Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout(5, 5));
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
JButton button = new JButton("START");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
progressBar.setIndeterminate(true);
WorkingDialog wd = new WorkingDialog();
wd.createAndDisplayDialog();
}
});
contentPane.add(progressBar, BorderLayout.PAGE_START);
contentPane.add(button, BorderLayout.PAGE_END);
frame.setContentPane(contentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new ProgressExample().createAndDisplayGUI();
}
});
}
}
class WorkingDialog extends JDialog
{
private String message = "HelloWorld";
private int count = 0;
private JTextField tfield;
private Timer timer;
private ActionListener timerAction = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (count == 10)
{
timer.stop();
ProgressExample.progressBar.setIndeterminate(false);
ProgressExample.progressBar.setValue(100);
ProgressExample.progressBar.setStringPainted(true);
dispose();
return;
}
tfield.setText(tfield.getText() + message.charAt(count));
count++;
}
};
public void createAndDisplayDialog()
{
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setLocationByPlatform(true);
JPanel panel = new JPanel();
tfield = new JTextField(10);
panel.add(tfield);
add(panel);
pack();
setVisible(true);
timer = new Timer(1000, timerAction);
timer.start();
}
}
So , it seems like you are write
ProgressExample.progressBar.setIndeterminate(false);
ProgressExample.progressBar.setValue(100);
ProgressExample.progressBar.setStringPainted(true);
after your while loop.
You can take a look at my answer in a previous SO question, which contains a sample using a JProgressBar which gets updates from another Thread by using a SwingWorker. Whether or not to use a SwingWorker depends a bit on your use case. If the function take some time to run you better use the SwingWorker to avoid blocking the UI.