I have one JFrame that's not actively rendered, a la standard basic Swing applications, which I need to launch another JFrame when a button is clicked. The second JFrame is actively rendered using Swing's BufferStrategy, and runs on its own independently - however, when I call it from the other JFrame's ActionPerformed both JFrames freeze.
I know there are complications in using Swing to accomplish this kind of behavior - how can I get around them?
You might be able to adapt this Swing based Launcher that uses
ProcessBuilder to run programs in a separate JVM.
package gui;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
/**
* #see http://stackoverflow.com/a/5696404/230513
*/
public class Launcher extends JPanel implements Runnable {
private final JLabel label = new JLabel();
private final JButton launch = new JButton();
ProcessBuilder pb = new ProcessBuilder(
"java", "-cp", "build/classes", "gui.Launcher$DialogTest");
private volatile Process proc;
public static void main(String[] args) {
EventQueue.invokeLater(new Launcher()::createGUI);
}
private void createGUI() {
final JFrame frame = new JFrame();
frame.setLayout(new GridLayout(0, 1));
frame.add(new Launcher());
frame.add(new Launcher());
frame.add(new Launcher());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public Launcher() {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
launch.setAlignmentX(0.5f);
label.setAlignmentX(0.5f);
launch.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (proc == null) {
launch.setText("Terminate");
label.setText("Status: run");
new Thread(Launcher.this).start();
} else {
proc.destroy();
reset();
}
}
});
this.add(launch);
this.add(label);
reset();
}
#Override
public void run() {
try {
proc = pb.start();
proc.waitFor();
} catch (IOException | InterruptedException ex) {
ex.printStackTrace(System.err);
}
EventQueue.invokeLater(this::reset);
}
private void reset() {
proc = null;
launch.setText("Launch");
label.setText("Status: idle");
}
private static class DialogTest {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JOptionPane.showMessageDialog(null, "Running",
"Test", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
});
}
}
}
Updated to Java 8 at the author's request.
Related
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);
}
}
}
At the interview, me was asked question: "How to perform many calculations in single thread without freezes GUI component like Progress Bar or that would be able to check another user input? (Can execute only one thread)"
I Asked that can write event loop like Node.js.
But me say that Java already have some mechanism for it. That java can use hardware parallelization of operation. What the classes or special words can used for this task?
So, assuming we can't use either SwingWorker or Swing Timer, as they create a second thread to support their operations, the only choice you're left with is to use SwingUtilities#invokeLater to repeatedly call a method, which performs a small subset of the work and updates the UI before calling itself again (using SwingUtilities#invokeLater)
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class NoMoreThreads {
public static void main(String[] args) {
new NoMoreThreads();
}
public NoMoreThreads() {
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 static class TestPane extends JPanel {
protected static final int MAX = 1000;
private JProgressBar pb;
private JButton start;
private int count;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
start = new JButton("Let's get this party started");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
calculate();
}
});
pb = new JProgressBar(0, MAX);
add(start, gbc);
add(pb, gbc);
}
protected void calculate() {
count++;
if (count < MAX) {
pb.setValue(count);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
calculate();
}
});
} else {
start.setEnabled(true);
}
}
}
}
I've been trying for at least an hour on refreshing my simple Jframe. I have tried repaint() revalidate; and just about anything else on the internet.
here is my entire class:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
public final class BRUTEFORCE {
static int Passwords = 0;
static JFrame frame;
public static void main(String[] args) {
//Create and set up the window.
frame = new JFrame("Simple GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel textLabel = new JLabel("Passwords tried: " + Passwords,SwingConstants.CENTER);
textLabel.setPreferredSize(new Dimension(300, 100));
frame.getContentPane().add(textLabel, BorderLayout.CENTER);
//Display the window.
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
Passwords++;
new Thread("Refresh") {
public void run () {
while(true){
frame.invalidate();
frame.validate();
frame.repaint();
}
}
}.start();
new Thread("Test") {
public void run () {
while(true) Passwords++;
}
}.start();
}
}
What am I doing wrong?
Two things are going wrong. You're first thread is constantly feeling the Event Queue with update requests, probably faster then the Event Queue can process them which may eventually flood it, degrading the performance of the system.
Secondly, you never actually change the text of the textLabel
For example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class BruteForce {
static transient int Passwords = 0;
static JFrame frame;
public static void main(String[] args) {
//Create and set up the window.
frame = new JFrame("Simple GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel textLabel = new JLabel("Passwords tried: " + Passwords, SwingConstants.CENTER);
textLabel.setPreferredSize(new Dimension(300, 100));
frame.getContentPane().add(textLabel, BorderLayout.CENTER);
//Display the window.
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
Passwords++;
new Thread("Test") {
public void run() {
while (true) {
try {
Passwords++;
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
textLabel.setText("Passwords tried: " + Passwords);
}
});
Thread.sleep(5);
} catch (InterruptedException ex) {
Logger.getLogger(BruteForce.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}.start();
}
}
Now, instead of a Thread, you might consider using a SwingWorker instead...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class BruteForce {
static transient int Passwords = 0;
static JFrame frame;
public static void main(String[] args) {
//Create and set up the window.
frame = new JFrame("Simple GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel textLabel = new JLabel("Passwords tried: " + Passwords, SwingConstants.CENTER);
textLabel.setPreferredSize(new Dimension(300, 100));
frame.getContentPane().add(textLabel, BorderLayout.CENTER);
//Display the window.
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
SwingWorker worker = new SwingWorker<Integer, Integer>() {
#Override
protected void process(List<Integer> chunks) {
// Only care about the last one..
int value = chunks.get(chunks.size() - 1);
textLabel.setText("Passwords tried: " + value);
}
#Override
protected Integer doInBackground() throws Exception {
while (true) {
// Perform long running process...
// Forced delay to simulate long running process
Thread.sleep(5);
Passwords++;
publish(Passwords);
}
}
};
worker.execute();
}
}
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();
}
}