I am creating a Java program using Swing and want to open a JFrame, run a for-loop that stores items in a list of Strings, display graphics while that is happening, and only after that is done call another method. The issue is that even though I call frame.setVisible(true); before running the loop, it only displays after the loop is done. I would use a SwingWorker but I need to stop the main thread from running the next method until after the loop is finished. If someone knows a way to use SwingWorker or knows a fix to this, that would be great. Here is the code I am referring to:
//The JPanel in charge of displaying graphics while the loop is running
FrameRenderer renderer = new FrameRenderer(videoFile, this.getWidth(), this.getHeight());
this.add(renderer);
this.setVisible(true);
//Call the method with the for-loop after this.setVisible is called
List<String> frames = renderer.renderFrames();
//I need this to run after the loop is finished
DisplayFrames display = new DisplayFrames(frames, this.getWidth(), this.getHeight(), this);
this.add(display);
SwingWorker
Worker Threads and SwingWorker
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private JLabel label = new JLabel("...");
public MainPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 64, 32, 64));
add(label);
SwingWorker<Void, String> worker = new SwingWorker<>() {
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
publish(Integer.toString(index));
// It's important, if you want to allow the UI to
// update on a single view, you need to allow time
// for this thread to sleep, otherwise, you could
// end up in a siutatio where the only update you
// get is the last one (with a list of all the
// the values you "published"
Thread.sleep(10);
}
return null;
}
#Override
protected void process(List<String> chunks) {
label.setText(chunks.get(chunks.size() - 1));
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
label.setText("All done here");
}
}
});
worker.execute();
}
}
}
Swing Timer
How to Use Swing Timers
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private JLabel label = new JLabel("...");
public MainPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 64, 32, 64));
add(label);
Timer timer = new Timer(10, new ActionListener() {
private int value = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (value >= 1000) {
((Timer)(e.getSource())).stop();
label.setText("All done here");
}
label.setText(Integer.toString(value));
value++;
}
});
timer.start();
}
}
}
Related
I've created an actionlistener connected to a button. On press it is suppose to change a label's text
then perform a function call. The function call is intensive enough that the program may seem to freeze up so the label just says "calculating please wait." However on button click the label is never displayed before the function call and lag hits. I have tried various ways of displaying the label as well as trying to add a sleep function to increase the time before function call. Any ideas?
lineup.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
calculating.setText("Calculating Lineup! please be patient."); //The text to be displayed"
try {
Thread.sleep(2000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
DecimalFormat df=new DecimalFormat("#.00");
lineupSet lineup = genetic.runGenetic(parser); //Function call causing hangup
QB.setText("QB: " + lineup.lineUp.get(0).getPlayerName()); //Text displayed after function
You should start by having a looking at Concurrency in Swing
From the sounds of your problem, you want to make use of a SwingWorker, for (a rough) example...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public class SoTest {
public static void main(String[] args) {
new SoTest();
}
public SoTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(8, 92, 8, 92));
JLabel label = new JLabel("Make it so");
JButton button = new JButton("Push me");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(button, gbc);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText("Rummaging through your stuff");
button.setEnabled(false);
SwingWorker<String, String> worker = new SwingWorker<String, String>() {
#Override
protected String doInBackground() throws Exception {
for (int index = 0; index < 5; index++) {
publish(Integer.toString(index));
Thread.sleep(1000);
}
return "";
}
#Override
protected void process(List<String> chunks) {
// Important, you will want to check chunks, as the
// API will consolidate calls to this if publish
// is fast enough
label.setText(label.getText() + ".");
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
button.setEnabled(true);
label.setText("Make it so");
}
}
});
worker.execute();
}
});
}
}
}
I want to update the Jlabel text in every second as long as the loop is running. how could I do this? I want to do as this fromat.
JPanel jpnl=new JPanel();
jfrm.add(jpnl);
String[] fonts=GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
jlab = new JLabel("This is Label");
jpnl.add(jlab);
for(int i=0;i<fonts.length;i++){
System.out.println(fonts[i]);
jlab.setText(fonts[i]);
jlab.setFont(new Font(fonts[i],Font.PLAIN,30));
jlab.setForeground(Color.DARK_GRAY);
}
Swing's single threaded nature precludes using a loop or Thread.sleep in the way you seem to be trying. Doing so, will simply block the UI and prevent it from been painted/updated until the loop is completed.
Because Swing is not thread safe, you can't simply use another Thread and the above approaches to update the UI, without jumping through some hoops
The conical answer to your question is to use a Swing Timer, which triggers an update at a regular bases. Because these updates are triggered within the context of the Event Dispatching Thread, it makes it safe to use when you want to update the UI.
Take a closer look at How to use Swing Timers for more details
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String[] fonts;
private final JLabel jlab;
private int index = 0;
public TestPane() {
setLayout(new GridBagLayout());
fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
jlab = new JLabel("This is Label");
add(jlab);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateFont();
index++;
if (index >= fonts.length) {
((Timer)e.getSource()).stop();
}
}
});
timer.setInitialDelay(0);
timer.start();
}
protected void updateFont() {
System.out.println(fonts[index]);
jlab.setText(fonts[index]);
jlab.setFont(new Font(fonts[index], Font.PLAIN, 30));
jlab.setForeground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I am trying to program a Rubiks cube timer. Once you click space I want the timer to countdown from 15 seconds, once the 15 seconds is over, start counting up from 0. When you are done solving the cube you click space again, stopping the timer (I want the timer to count to the nearest hundredth). Here is what I have now:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class CubeMain extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
CubeMain frame = new CubeMain();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public CubeMain() {
setTitle("Cube Timer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 600, 490);
contentPane = new JPanel();
final JLabel Timerlbl = new JLabel("");
Timerlbl.setBounds(269, 219, 46, 14);
contentPane.add(Timerlbl);
addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
Timerlbl.setText("Label Change");
}
});
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
}
}
So you know you need some kind of timer. The timer you want for a Swing program is a javax.swing.Timer. This is the basic constructor
Timer(int delay, ActionListener listener);
Where, delay is the delay time between fired actions, and the listener is what listens for those timer action events being fired each interval. A basic implementation would me something like this
public TimerPanel() {
Timer timer = new Timer(1000, new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do something
}
});
timer.start();
}
What you could do is have a count variable that you increment and use to set the timerLabel. Then just set your key binding for the SPACE to timer.start() or timer.stop()
Take a look at this, it has the timer. It's pretty much what you need
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public class TimerPanel{
double count = 15.00;
boolean reverse = true;
boolean started = false;
private JLabel timerLabel = new JLabel(String.format("%.2f", count));
private Timer timer;
public TimerPanel() {
timerLabel.setHorizontalAlignment(JLabel.CENTER);
timerLabel.setBorder(new EmptyBorder(20, 20, 20, 20));
JFrame frame = new JFrame();
frame.add(timerLabel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
timer = new Timer(10, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (reverse && count > 0) {
count -= 0.01;
timerLabel.setText(String.format("%.2f", count));
if (count <= 0) {
reverse = false;
}
}
if (!reverse){
count += 0.01;
timerLabel.setText(String.format("%.2f", count));
}
}
});
Action spaceAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!started) {
timer.start();
started = true;
} else {
timer.stop();
count = 15.00;
started = false;
reverse = true;
}
}
};
InputMap inputMap = timerLabel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = timerLabel.getActionMap();
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "spaceAction");
actionMap.put("spaceAction", spaceAction);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new TimerPanel();
}
});
}
}
How to edit the JLabel every seconds like (time left or score) in some games.
this is my code
static int l = 1;
static int s = 5000;
static int t = 90;
public static void main(String[] args) {
//Frame
final JFrame f = new JFrame();
f.setTitle("Picture Puzzle");
f.setSize(500,500);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
f.setVisible(true);
//some extra stuffs here
JLabel blevel00 = new JLabel("Level:" + l);
JLabel bscore00 = new JLabel("Score:" + s);
JLabel btime00 = new JLabel("Time:" + t);
p2.add(blevel00);
p2.add(bscore00);
p2.add(btime00);
//some extra stuffs here
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
while(t != 0 ) { //the t is the static int t = 90;
f.add(p2);
f.remove(p1);
f.setVisible(true);
f.revalidate();
f.repaint();
}
t--;
}
});
}
}
I tried this and nothing happens. any help will be appreciated.
Swing is a single threaded environment, that is, all alterations and modifications to the UI are expected to occur within the context of the Event Dispatching Thread.
Anything that blocks this thread, like a never ending loop or blocking I/O will prevent this thread from processing new events, including paint events.
Swing provides a number of solutions to this problem, in your case the best solution is probably to use a javax.swing.Timer. This will allow you to schedule a regular callback that is called within the context of the EDT, allowing you to make modifications to the UI on a regular bases.
Take a look at Concurrency in Swing and How to use Swing Timers for more details
Update with simple example
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleClock {
public static void main(String[] args) {
new SimpleClock();
}
public SimpleClock() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
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 time;
public TestPane() {
setLayout(new GridBagLayout());
time = new JLabel();
time.setFont(time.getFont().deriveFont(Font.BOLD, 48));
add(time);
updateTime();
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
updateTime();
}
});
timer.start();
}
protected void updateTime() {
time.setText(DateFormat.getTimeInstance().format(new Date()));
}
}
}
I wanted to know if there's any easy way to delete the content (text) from a JLabel after 5 seconds. Is that possible? Because I have a JLabel in a JFrame and it shows some internal errors from the program I'm coding and I want the JLabel to show the message for a couple of seconds and then go to blank. Any ideas?
Simplest solution is to use a Swing Timer. It will prevent freezing the GUI and ensure proper Thread access (ie, UI modification is performed on the EDT).
Small demo example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestLabelDelete {
private JFrame frame;
private JLabel label;
protected void initUI() {
frame = new JFrame(TestLabelDelete.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel("Some text to delete in 5 seconds");
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer t = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(null);
}
});
t.setRepeats(false);
t.start();
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestLabelDelete testLogin = new TestLabelDelete();
testLogin.initUI();
}
});
}
}
Use Timer. Please see my example.
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class SourceCodeProgram {
private static void createAndShowGUI() {
// Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
// Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setMinimumSize(new Dimension(200, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add the ubiquitous "Hello World" label.
final JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
// Display the window.
frame.pack();
frame.setVisible(true);
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Clear text or whatever you want
label.setText("New text");
}
});
// start Tick-Tack
timer.start();
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Or you can write a separate class, which can clean label.
class JLabelCleaner {
private JLabel label;
private int waitSeconds;
public JLabelCleaner(int waitSeconds, JLabel label) {
this.label = label;
this.waitSeconds = waitSeconds;
}
public void startCountdownFromNow() {
Timer timer = new Timer(waitSeconds * 1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText("");
}
});
timer.start();
}
}
Now, you can use it whenever you need it in this way:
new JLabelCleaner(5, label).startCountdownFromNow();
Also see:
How to Use Swing Timers
There is an easy solution to this.
JLabel label = new JLabel("error text");
Thread.sleep(5000);
label.setText("");
Hope this helps!
EDIT: If you don't want the program to freeze for 5 secs you'll have to put this inside a Runnable.
It's very easy to do... just create a new thread and write code to clear text on label, and make that thread to sleep for 5sec and then start it.
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class LabelThread {
private JLabel textLabel;
public LabelThread() {
try {
JFrame frame = new JFrame("Label");
frame.setSize(500, 500);
textLabel = new JLabel("Hiiii.... Kill me");
frame.setContentPane(textLabel);
frame.setVisible(true);
MyThread thread = new MyThread();
MyThread.sleep(5000);
thread.start();
} catch (InterruptedException ex) {
}
}
class MyThread extends Thread{
#Override
public void run() {
System.out.print("Running thread");
textLabel.setText("");
}
}
public static void main(String args[]){
LabelThread labelThread = new LabelThread();
}
}