Show / update components before process finished - java

I currently have a java program with swing gui that lets the user choose various files (xsl-fo and xml) and generates PDFs using Render X. I have trying for a while to get a pop up JFrame to appear when a button is pressed, which would then show a progress bar, or label to keep the user informed of the progress. However when instantiating a new frame it will appear black, or without components, which then show after the processes have completed.
private void RunButtonActionPerformed(java.awt.event.ActionEvent evt) {
ExecutorService executor = Executors.newFixedThreadPool(8);
//for reach file to process)
for (int i = 0; i < count; i++) {
Runnable worker = new ProcessThreader(conf, i);
executor.execute(worker);
}
executor.shutdown();
JFrame PercentageFrame = new JFrame();
PercentageFrame.setVisible(true);
PercentageFrame.setSize(200, 200);
PercentageFrame.repaint();
while (!executor.isTerminated()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
NarrowOptionPane.errorMessage("Interrupted: ", ex.getMessage());
}
}
System.out.println("Complete");
}
The run button is located in a JPanel, which is located in a JFrame and the Main Frame is instantiated in the main method, and wrapped in the invoke later method
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new MainFrame("PDF Producer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(710, 530);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
}
});
}
I'm fairly new to threading / executors and java swing, so go easy! Thanks

Code executed from within a listener is executed on the Event Dispatch Thread. So the Thread.sleep() is causing the EDT to sleep which means the GUI can't respond to events or repaint itself.
Read the section from the Swing tutorial on Concurrency In Swing for more information. One solution as described in the tutorial is to use a SwingWorker for the long running task and to publish results as they become available.

Your while loop block EDT, delete that and your code will be work.
See next example with ExecutorService and JProgressBar :
import java.awt.BorderLayout;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
public class Example extends JFrame {
private static JProgressBar progress;
public static void main(String[] args) {
final JFrame f = new JFrame();
progress = new JProgressBar();
progress.setStringPainted(true);
progress.setIndeterminate(true);
ExecutorService newCachedThreadPool = Executors.newFixedThreadPool(1);
for( int i =0; i<10;i++){
final int j = i;
Runnable r = new Runnable() {
#Override
public void run() {
progress.setString(j+"");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
newCachedThreadPool.submit(r);
}
f.getContentPane().add(progress,BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
Here JFrame show number of Runnable which executed in ExecutorService. Also if you need to get result from your Runnables, try to use Callable instead of Runnable. With that when you submit Callable you get Future instance from which you can get result.
Read about Executors, Callable and Future.

Related

JProgressBar update and thread scheduling

Hello stack exchangers,
I have a problem with progress bars in java Swing. I think my confusions arise from my poor understanding of threads and the Swing event queue (I don't know much about java Threads, and exactly what is happening on the AWTEventQueue, although I generally understand what multithreading is about).
The context is that a JButton is pressed to start a long calculation. Before the calculation starts, I make a progress bar in a JFrame, which I thought would be painted, but it isn't. The frame appears, but it is just grey. The button, in this example has "clickMe" written on it.
In the "clickMe" action listener, I first make and display a JFrame in a subtask which is "run" (I'm not clear on when this is scheduled TBH). Then, I call doTask() which is running in the same thread as the action listener (which I think is the AWTEventThread??). The doTask() runs, printing out numbers to the Console. Intermixed with the doTask() output are iteration counts of the progressbar (from when the action listener started makeProgressBar()).
So, from the output, it looks like both the progress bar is running and the AWTEventThread, but the value set in the JProgressBar GUI is never updated.
How can I change my code so that the GUI gets updated? I've tried understanding the JProgressBar tutorial and hunted around the web, but I think my problem is more a conceptual understanding of Java Tasks.
This is my code:
package problemclass;
import java.awt.EventQueue;
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;
public class ProblemClass
{
void progressBarButtonClick()
{
JFrame buttonInAFrame = new JFrame();
JPanel buttonInAFramePanel = new JPanel();
JButton clickMe = new JButton("Click me!");
buttonInAFramePanel.add(clickMe);
clickMe.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
JFrame progBarFrame = makeProgressBar();
doTask();
progBarFrame.dispose();
}
});
buttonInAFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
buttonInAFrame.add(buttonInAFramePanel);
buttonInAFrame.pack();
buttonInAFrame.setVisible(true);
}
private void doTask()
{
for(int i = 0; i < 20000; i++)
{
if (i % 100 == 0)
{
System.out.println("TASK iteration " + i);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}
}
}
private JFrame makeProgressBar()
{
JFrame progBarFrame = new JFrame();
JPanel progBarPanel = new JPanel();
JProgressBar progressBar = new JProgressBar();
progBarPanel.add(progressBar);
progressBar.setValue(0);
progressBar.setStringPainted(true);
progressBar.setIndeterminate(true);
new Thread(new Runnable()
{
public void run()
{
for (int i = 0; i <= 100; i++)
{
final int j = i;
System.out.println("Progress Iteration " + j);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
progressBar.setValue(j);
}
});
try
{
java.lang.Thread.sleep(100);
}
catch(Exception e) { }
}
}
}).start();
progBarFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
progBarFrame.add(progBarPanel);
progBarFrame.pack();
progBarFrame.setVisible(true);
return progBarFrame;
}
public static void main(String[] args)
{
EventQueue.invokeLater(() ->
{
new ProblemClass().progressBarButtonClick();
});
}
}
JFrame progBarFrame = makeProgressBar();
doTask();
Not sure exactly what you are trying to do.
The above code has two loops:
In the makePrgressBar() method you start a Thread and invoke SwingUtilities.invokeLater(…), to update the progress bar, which is correct.
but then in doTack() you start another loop. This time you don't start a Thread so the code is invoked on the EDT and since you use Thread.sleep, the EDT will sleep and the GUI will not repaint itself until the entire loop is finished.
I would suggest you get rid of the doTask() method since I don't know why you need two blocks of code that loop. Or if you really need it, then you also need to use a Thread and invokeLater(…).
Just like you, I recently did some work on progress bars and threading and went nuts until I realized that it is just so simple.In a nutshell this is the code I have when my button is clicked:
// Create 2 threads. One handles your GUI. Other does the task
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
// code goes here.
//In here I choose to hide the button, display the progress bar
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
// code goes here.
//In here I get the task done, then hide the progress bar
}
});
t2.start();
Works like a charm every time. Hope it helps!

How to make a countdown timer in Java? [duplicate]

Bellow is the code for the simplest GUI countdown. Can the same be done in a shorter and more elegant way with the usage of the Swing timer?
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class CountdownNew {
static JLabel label;
// Method which defines the appearance of the window.
public static void showGUI() {
JFrame frame = new JFrame("Simple Countdown");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel("Some Text");
frame.add(label);
frame.pack();
frame.setVisible(true);
}
// Define a new thread in which the countdown is counting down.
public static Thread counter = new Thread() {
public void run() {
for (int i=10; i>0; i=i-1) {
updateGUI(i,label);
try {Thread.sleep(1000);} catch(InterruptedException e) {};
}
}
};
// A method which updates GUI (sets a new value of JLabel).
private static void updateGUI(final int i, final JLabel label) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
label.setText("You have " + i + " seconds.");
}
}
);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showGUI();
counter.start();
}
});
}
}
Yes you SHOULD use a Swing Timer. You SHOULD NOT, use a util Timer and TimerTask.
When a Swing Timer fires the code is executed on the EDT which means you just need to invoke the label.setText() method.
When using the uitl Timer and TimerTask, the code DOES NOT execute on the EDT, which means you need to wrap your code in a SwingUtilities.invokeLater to make sure the code executes on the EDT.
And that is way using a Swing Timer is shorter and more elegant than your current approach, it simplifies the coding because to code is executed on the EDT.
You could make it a little more elegant by using Timer with an appropriate TimerTask.
Yes, use a timer. updateGUI would be the code for the timer task, but it will need some changes as you won't be able to pass in i for each call since you just get a run() method.

infinite loop in swing

I am working on this application in swing .It is actually a voice controlled thingy...I give voice commands and some action is performed.But the thing is that once it is deployed, it is on an infinite while loop, it continuously searches for voice( which it should..imagine the jarvis of iron man movie) .. but this while loop freezes up my gui.I can not update it.can not hide panels , can not play sound.
swing worker and swing utilities shouldn;t help me because they check for the code after certain period of time while i need real time voice recognition..
So what can be done ? Can i make my gui interact with another java program? Like the java prog will do the voice recognition and pass on the reply to the gui?
Here is the code sketch
class{
main(){
new class()
}
class(){
frames + content pane initialized
mousePresssed()
{
///the while loop starts here and looks for voice commands..any gui update code doesnt work here..while it detects the voice fine..continuously.
}
}
Basically, you need to have your infinite loop run in another Thread than the EDT. And whenever you want to update your GUI, do it on the EDT, using a SwingUtilities.invokeLater call. The delay for calling the update of the GUI in invokeLater will be barely noticeable. SwingUtilities.invokeLater is not based on a polling mechanism. The only thing it does is transform a Runnable into an event which is then posted on the EDT. The EDT will then execute your Runnable as soon as possible, so most of the time, instantly.
Now for the pattern on how to communicate between your Thread and your GUI, you can simply use the "Observer" pattern. Your voice recognition thread is somehow a model and your UI simply listens for changes on that model. Whenever the model changes, the UI updates itself.
I made a dummy example of such thing. For the "Observer" pattern, I used the PropertyChangeSupport for it.
For the model, I created a dummy thread which generates random "command" every once in a while and the UI updates itself accordingly:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class TestThreadingAndGUI implements PropertyChangeListener {
private JFrame frame;
private JLabel label;
private DummyRunnable runnable;
public static class DummyRunnable implements Runnable {
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private String command;
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
#Override
public void run() {
Random random = new Random();
while (true) {
try {
Thread.sleep(((random.nextInt(3)) + 1) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 15; i++) {
sb.append((char) ('a' + random.nextInt(26)));
}
setCommand(sb.toString());
}
}
public String getCommand() {
return command;
}
private void setCommand(String command) {
String old = this.command;
this.command = command;
pcs.firePropertyChange("command", old, command);
}
}
protected void initUI(DummyRunnable runnable) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
frame.add(label);
frame.setSize(600, 600);
frame.setVisible(true);
this.runnable = runnable;
runnable.addPropertyChangeListener(this);
}
private void executeCommand() {
label.setText(runnable.getCommand());
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("command")) {
// Received new command (outside EDT)
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Updating GUI inside EDT
executeCommand();
}
});
}
}
public static void main(String[] args) {
final DummyRunnable runnable = new DummyRunnable();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestThreadingAndGUI testThreadingAndGUI = new TestThreadingAndGUI();
testThreadingAndGUI.initUI(runnable);
}
});
new Thread(runnable).start();
}
}

Java Swing Concurrency display JTextArea

I need to execute/display a series of events from a Arraylist to a JTextArea, however, each Event gets execute with different time. Following is a quick example of my goal:
public void start(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea.append("Test" + "\n");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
}
So right now, "Test" and "Test1" display on JTextArea after whole execution is completed.
How do I make "Test" display first, then 3 secs later, display "Test1"
Thank u all in advance
invokeLater schedules the runnable to run on the Event Dispatch Thread. You shouldn't sleep within it or you will starve the dispatch thread. Try using a separate worker thread instead:
Thread worker = new Thread(new Runnable(){
public void run(){
jTextArea.append("Test" + "\n");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
worker.start();
If your tasks are time/cpu intensive, then yes, definitely use a background thread to do this such as a SwingWorker object or a Runnable run in a Thread. If however what you need to do is to stagger the display of something and all you are looking for is the Swing equivalent of Thread.sleep(3000), then your best option is to use a Swing Timer. There is an excellent tutorial on how to use these which you can find here: http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Fu extends JPanel {
private static final int TIMER_DELAY = 600;
protected static final int MAX_COUNT = 20;
private JTextArea jTextArea = new JTextArea(10, 10);
private JButton startBtn = new JButton("Start");
private Timer timer;
public Fu() {
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startAction(e);
}
});
add(new JScrollPane(jTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
add(startBtn);
}
private void startAction(ActionEvent e) {
if (timer != null && timer.isRunning()) {
// prevent multiple instances of timer from running at same time
return;
}
timer = new Timer(TIMER_DELAY, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent e) {
if (count < MAX_COUNT) {
count++;
jTextArea.append("Test " + count + "\n");
} else {
jTextArea.append("Done! \n");
timer.stop();
timer = null;
}
}
});
timer.setInitialDelay(0);
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Foo");
frame.getContentPane().add(new Fu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
As pointed out, this is a bad idea, as you will block the event thread.
However, understanding the reason for this is important as well. As you seem to know, all code that affects the state of Swing components needs to happen in the event handling thread (which is the reason why invokeLater and friends should always be used).
What is a bit less better known is that paining code also executes in the event handling thread. When your call to Thread.sleep is executing, it's not only blocking the event thread, it's also blocking any painting of components. This is why the full update appears to happen in one go -- the JTextArea is updated but it can't be repainted until your run method returns.
Lots of info available here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

How do I make a thread wait for JFrame to close in Java?

When the program starts, a new JFrame is created. Once the user clicks the start button a thread is created and started. Part of this threads execution is to validate the data on the form and then execute with that data. Once the data has been validated the thread calls dispose() on the original frame and then creates a new JFrame that acts as a control panel.
There is also an automatic mode of the program that doesn't display any GUI at all, this mode reads data from a configuration file and then starts the execution thread and runs everything but without the control panel.
I want the program to end once the thread completes, but in GUI mode, only if the user has closed the control panel as well.
Is it possible to make the thread wait for the frame to close. I assuming that the frame is run from it's own Thread? or is that not the case.
Thanks.
The answer you chose is a little awkward. Using Thread.sleep(1000) will check for window state every second. It is not a performance issue, but just bad coding style. And you may have a one second response time.
This code is a little bit better.
private static Object lock = new Object();
private static JFrame frame = new JFrame();
/**
* #param args
*/
public static void main(String[] args) {
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (frame.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Working now");
}
}
};
t.start();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
synchronized (lock) {
frame.setVisible(false);
lock.notify();
}
}
});
t.join();
}
You can make reference from your thread to the JFrame. Then set the default close operation of JFrame to HIDE_ON_CLOSE. If the JFrame is closed, you can stop the thread.
Example code:
import java.awt.Dimension;
import javax.swing.JFrame;
public class FrameExample extends JFrame {
public FrameExample() {
setSize(new Dimension(100, 100));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setVisible(true);
}
private static class T implements Runnable {
private FrameExample e;
public T(FrameExample e) {
this.e = e;
}
#Override
public void run() {
while (true) {
if (e.isVisible()) {
// do the validation
System.out.println("validation");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
break;
}
}
}
}
}
public static void main(String[] args) {
FrameExample frameExample = new FrameExample();
new Thread(new T(frameExample)).start();
}
}
All Swing components, including JFrame, are managed by a single thread, called the Event Dispatch Thread, or EDT. (It's possible to call methods on Swing objects from other threads, but this is usually unsafe, except in a few cases not relevant here.)
You'll probably accomplish what you want here by putting the data validation and execution code in its own object which is otherwise completely unaware of the outside world. Then, call it from one of two other objects: one that manages a GUI, and another that runs in "automatic mode".

Categories

Resources