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.
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
I created swing application that do some operations in performedAction methode, but when i create a thread that shows the progressBar, the progressBar will not visible during the performedAction methode, but at the end the progressBar will be visible with a value of 100% directly
main class:
import javax.swing.*;
import javax.swing.plaf.ButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class main {
public static void main(String[] args) {
JFrame win = new JFrame("Test");
FlowLayout layout = new FlowLayout();
Button b1 = new Button("Click ");
win.add(b1);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
window win = new window();
win.start();
try
{
Thread.sleep(2000);
}
catch(InterruptedException e5){}
}
});
win.setLayout(layout);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
window class:
import javax.swing.*;
import java.awt.*;
public class window extends Thread{
public window(){
}
#Override
public void run() {
JFrame fen = new JFrame("New window");
FlowLayout layout = new FlowLayout();
fen.setLayout(layout);
Button b2 = new Button();
fen.setVisible(true);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.add(b2);
try
{
for(int i = 0; i <= 100; i++)
{
b2.setLabel("Button " + i);
Thread.sleep(10);
}
}
catch(InterruptedException e2){
}
}
}
Yes, it is safe to create a new Thread inside of an actionPerformed handler.
But, no thread other than the Event Dispatching Thread (EDT) must interact with the Swing components.
To create animations, or delays inside of Swing, you must use a javax.swing.Timer. Executing a Thread.sleep() on the EDT is never allowed, and won't do want you hope it will.
To return from a spawned thread to the EDT, you must use SwingUtilities.invokeLater() or SwingUtilities.invokeAndWait().
Using a SwingWorker is the preferred way to execute background tasks in Swing. It handles communication and publication of partial and final results from the background task to the EDT for displaying in the GUI components.
The following is a translation of your code to a working example, using a SwingWorker. Instead of AWT Button's, the Swing JButton is used. The program is created using invokeAndWait to ensure the main window construction occurs on the EDT. A lambda function is used, but you can replace this with new Runnable() { } inner class, if desired. The "Button 0" through "Button 100" progress reports are published from the SwingWorker background task, for processing in the EDT. Multiple results can be generated at by the background thread before the EDT has a chance to process them; here, we take only the last result to display in the button.
public class Main {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(() -> {
JFrame win = new JFrame("Test");
win.setLayout(new FlowLayout());
JButton b1 = new JButton("Click");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
DoWork work = new DoWork();
work.execute();
}
});
win.add(b1);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
});
}
}
class DoWork extends SwingWorker<Void, String> {
JFrame fen;
JButton b2;
DoWork() {
fen = new JFrame("New window");
fen.setLayout(new FlowLayout());
b2 = new JButton();
fen.add(b2);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.setVisible(true);
fen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i++) {
publish("Button " + i);
Thread.sleep(10);
}
return null;
}
#Override
protected void process(List<String> data) {
String last = data.get(data.size() - 1);
b2.setText(last);
}
#Override
protected void done() {
fen.dispose();
}
}
There are still a number of improvements that can be made to this code. A JLabel instead of a JButton for displaying the results, a JDialog for the progress window, or perhaps better a ProgressMonitor. These are left as an exercise to the student.
I want to show how merge sort perform visually using JFrame. What I want to do is to make visible subsequent JLabel with some time delay. I tried many way but all of them appears at same moment with no intermediate delay.
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
// jLabel1.setVisible(false);
jLabel2.setVisible(false);
jLabel3.setVisible(false);
jLabel4.setVisible(false);
jLabel5.setVisible(false);
jLabel6.setVisible(false);
jLabel7.setVisible(false);
final Timer t=new Timer((4000), null);
final int delay=2000;
final ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jLabel1.setVisible(true);
t.getDelay();
jLabel2.setVisible(true);
t.setDelay(3000);
jLabel3.setVisible(true);
t.setDelay(2000);
jLabel4.setVisible(true);
t.setDelay(2000);
jLabel5.setVisible(true);
t.setDelay(2000);
jLabel6.setVisible(true);
t.setDelay(2000);
}
};
new Timer(delay, taskPerformer).start();
But when I click button all the lables appear at same momenet though I have kept delay.
You need to update the icons in the timer's action listener, as shown here. You can implement the Icon interface to render icons having a size proportional to an element's comparative value, as shown here.
Addendum: Can you please be little bit specific?
You want to animate the intermediate steps of sorting a List<Number> of size N in some initially random order. Number subclasses implement Comparable<T>, so compareTo() is already done. A GridLayout(1, 0) of JLabel each having an Icon can be used to display the values. DecRenderer shows how to create icons with a proportional size; you'll want to vary the height over the interval [0, N). GrayIcons & Mad's example show how to animate the display of the icons in some order.
There are a number of reasons why this won't work. Firstly, javax.swing.Timer doesn't work this way. It waits in the background until the given delay has past and then calls the registered ActionListeners actionPerformed method.
Secondly, if it did work this way, it would block the Event Dispatching Thread, preventing it from processing repaint requests.
I think you will find How to use Swing Timers of use.
public class BlinkOut {
public static void main(String[] args) {
new BlinkOut();
}
public BlinkOut() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel[] labels;
private int[] delays;
private Timer timer;
private int index;
public TestPane() {
setLayout(new GridLayout(0, 1));
labels = new JLabel[7];
for (int index = 0; index < 7; index++) {
labels[index] = new JLabel("Label " + (index + 1));
add(labels[index]);
}
delays = new int[] {2000, 3000, 2000, 2000, 2000, 2000, 2000};
JButton hide = new JButton("Hide");
add(hide);
hide.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Click");
index = 0;
labels[index].setVisible(false);
timer.setDelay(delays[index]);
timer.start();
}
});
timer = new Timer(delays[0], new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Tick");
timer.stop();
index++;
if (index < 7) {
labels[index].setVisible(false);
timer.setDelay(delays[index]);
timer.start();
}
}
});
timer.setRepeats(false);
timer.setCoalesce(true);
}
}
}
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().
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.