How to keep a method running? - java

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

Related

is it safe to create a new thread inside actionPerformed in swing?

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.

setText() not working in mouseClicked() function

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.

Implement StopWatch

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.

How to add a progress bar?

I have been trying to understand how to add a progress bar, I can create one within the GUI I am implementing and get it to appear but even after checking through http://docs.oracle.com/javase/tutorial/uiswing/components/progress.html I am still no clearer on how I can set a method as a task so that I can create a progress bar for running a method. Please can someone try to explain this to me or post an example of a progress bar being used in the GUI with a task being set as a method. Thanks.
Maybe I can help you with some example code:
public class SwingProgressBarExample extends JPanel {
JProgressBar pbar;
static final int MY_MINIMUM = 0;
static final int MY_MAXIMUM = 100;
public SwingProgressBarExample() {
// initialize Progress Bar
pbar = new JProgressBar();
pbar.setMinimum(MY_MINIMUM);
pbar.setMaximum(MY_MAXIMUM);
// add to JPanel
add(pbar);
}
public void updateBar(int newValue) {
pbar.setValue(newValue);
}
public static void main(String args[]) {
final SwingProgressBarExample it = new SwingProgressBarExample();
JFrame frame = new JFrame("Progress Bar Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(it);
frame.pack();
frame.setVisible(true);
// run a loop to demonstrate raising
for (int i = MY_MINIMUM; i <= MY_MAXIMUM; i++) {
final int percent = i;
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
it.updateBar(percent);
}
});
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
;
}
}
}
}
Your question is a bit vague, but it sounds to me like you want the progress bar to show progress for a specific running method, which I'll call the "work()" method. Unfortunately, there's no way to just pass a reference to your method to a progress bar - your method needs to explicitly tell the progress bar what to display. Here's what I would do:
Make the progress bar's reference available to work() - either pass it in as an argument to work(), or provide an accessor method that your code in work() can call to get a reference to the progress bar.
Inside work(), after you've obtained a reference to the progress bar (which I'll call "pb", call pb.setMinimum(0) and pb.setMaximum(n) where n is the number of steps your method has to get through.
As your method completes each step, call pb.setValue(pb.getValue()+1);
At the end of your method, call pb.setValue(0); to reset the progress bar prior to returning.
Also, if you want your progress bar to display a String message, you first have to call pb.setStringPainted(true), then subsequent calls to pb.setString(string) will show up on the progress bar.
See my answer on another SO question which includes an example of a JProgressBar which gets updated by using a SwingWorker. The SwingWorker is used to execute a long running task in the background (in case of the example it is just a regular Thread.sleep) and report on progress at certain intervals.
I would also strongly suggest to take a look at the Swing concurrency tutorial for more background info on why you should use a SwingWorker when performing long-running tasks which interfere with the UI.
A similar example as the one I posted is available in the Swing tutorial about JProgressBars, which it also worth looking at
How about this,
JFrame->JButton (BorderLayout.NORTH)
JFrame-> JPanel->JProgressBar (BorderLayout.SOUTH)
You can add button part where ever you like, for example when Progress progress = ...; state=true; progress.waitFor(); state=false;
private static void daa() {
//Frame
JFrame frame = new JFrame("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setSize(frame.getWidth() + 55, frame.getHeight() + 55);
//Button
JButton jButton = new JButton("State");
frame.add(jButton, BorderLayout.NORTH);
//Progress Bar
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
//Text for progress bar
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
//linking
panel.add(progressBar);
frame.add(panel, BorderLayout.SOUTH);
boolean[] state = {false};
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
state[0] = !state[0];
state();
}
private void state() {
if (state[0] == true) {
panel.hide();
} else {
panel.show();
}
}
});
}

Components in JDialog not showing

First of all: I know this question seems to have been asked a few million times, but none of the answers given to other questions seem to work with me.
Sometimes, when I run Message.popup(String, int) in the code below, the text displays correctly, but sometimes the JDialog is empty, like if the component wasn't added at all.
public class Message extends JDialog {
private int width;
private int height;
private JLabel content;
public Message(String _content, int _margin) {
super();
this.content = new JLabel(_content);
content.setFont(new Font("Monospaced", Font.BOLD, 20));
this.margin = _margin;
this.width = content.getPreferredSize().width + _margin;
this.height = content.getPreferredSize().height + _margin;
createComponents();
setProperties();
}
public static void popup(String _content, int _time) {
if (SwingUtilities.isEventDispatchThread()) {
runPopup(_content, _time);
}
else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
runPopup(_content, _time);
}
});
}
}
private static void runPopup(String _content, int _time) {
final Message message = new Message(_content);
new Timer(_time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
message.dispose();
}
}).start();
}
private void createComponents() {
setLayout(new BorderLayout());
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(content, BorderLayout.CENTER);
box.add(Box.createHorizontalGlue());
add(box);
}
private void setProperties() {
setSize(width, height);
setLocation(Coordinator.calculateCenteredWindowLocation(width, height));
setUndecorated(true);
setResizable(false);
setTitle(content.getText());
setVisible(true);
update(getGraphics());
}
}
Without the update(getGraphics());, the frame is always empty, but with it, it depends of what direction the wind is blowing... (go figure!)
As mentioned by #Riduidel, it is important that anything Swing-related occur on the Event Dispatch Thread, or EDT. This is because Swing is not thread-safe. When invoking popup(), you ought to do the following
if(SwingUtilities.isEventDispatchThread()){
Message.popup(...);
}
else{
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.popup(...);
}
});
}
This will ensure that the JFrame is created on the EDT. Also, from the code snippet you've posted, it would seem likely that Message should have a private constructor. In addition, since you're not doing any custom rendering, why not just make a JFrame member variable instead of extending the class? -- seems a bit superfluous to me.
Regardless, you should also never sleep in the EDT either, since this will make the GUI appear to "freeze" and blocks execution of other queued events. When performing long-running tasks, use either SwingWorker, or as #Riduidel mentioned, javax.swing.Timer. But if you prefer to use the java.util.Timer, use the SwingUtilities utility class as shown above to post the Runnable task on the EventQueue to be executed in the EDT.
EDIT
Here is what I'd do (and yes, it works)
public class Message {
// Private constructor to prevent external instantiation
private Message(){
}
public static void createAndShowDialog(final String content, final int time){
final JDialog dialog = new JDialog();
dialog.setLayout(new BorderLayout());
dialog.setUndecorated(true);
JLabel label = new JLabel(content);
label.setFont(new Font("Monospaced", Font.BOLD, 20));
Box b = Box.createHorizontalBox();
b.add(Box.createHorizontalGlue());
b.add(label, BorderLayout.CENTER);
b.add(Box.createHorizontalGlue());
dialog.add(b);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// kick-off timer
Timer t = new Timer(time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
dialog.dispose();
}
});
t.setRepeats(false);
t.start();
}
}
And wherever you invoke createAndShowDialog(...), do the following
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.createAndShowDialog("Content", 5000); // wait 5 seconds before disposing dialog
}
});
Are you sure your code is executing in the EDT ? indeed, if not (which is what I expect to be, since you sleep the current thread, what Swing would typically don't like), your frame will have trouble rendering.
To avoid those typical Swing threading issues, please take a look at the SwingUtilities class, which provide you methods to ensure you're running in EDT. Additionnaly, instead of directly sleeping your thread, you could repalce it with a Swing javax.swing.Timer (beware not to confuse it with the java.util.Timer).
update(getGraphics());
Never use the update() method or the getGraphics() method.
Invoking update() is used for AWT NOT Swing.
If you need to do custom painting then you override the paintComponent() method of your component which already has access to the graphics object.

Categories

Resources