When I click Print Button it should show a Gif Animation followed by the text "Working..."
but here only the text "Working..." appears , not the animation.
Here's the Code:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jLabel1.setVisible(true);
/* This portion is Time Consuming so I want to display a Loading gif animation. */
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
empPrint=new HashMap();
if(!empPrint.isEmpty())
empPrint.clear();
if(jRadioButton1.isSelected())
empPrint.put("PNO",parent.emp.getPAN());
else
empPrint.put("PNO",records.get(jComboBox1.getSelectedItem()));
REPORT="Report.jrxml";
try {
JASP_REP =JasperCompileManager.compileReport(REPORT);
JASP_PRINT=JasperFillManager.fillReport(JASP_REP,empPrint,parent.di.con);
JASP_VIEW=new JasperViewer(JASP_PRINT,false);
JASP_VIEW.setVisible(true);
JASP_VIEW.toFront();
}
catch (JRException excp) {
}
setVisible(false);
}
});
}
You should use a SwingWorker for time consuming tasks. Using invokeLater() just pushes it to the event queue, and it gets run in the EDT, blocking it.
Drawing in swing is done in the event dispatch thread, but since the EDT is busy running your printing task, swing has no chance to process repaint requests.
// Note the upped case "Void"s
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
// Do the printing task here
return null;
}
#Override
public void done() {
// Update the UI to show the task is completed
}
}.execute();
The SwingUtilities.invokeLater() method will not help you in this case. The Runnable you pass still gets executed on the Event Dispatch Thread (EDT, the thread responsible for drawing the UI and responding to clicks etc.).
You could look at SwingWorkers, but you could just as well use a simple ExecutorService and pass the Runnable to there. The Executor framework - which was added in Java 5 or 6 - offers relatively simple to use tools to have stuff run in the background without having to worry about your own threads. I recommend going with something like this (pseudo code):
private ExecutorService executor = Executors.newFixedExecutorService()
....
public void buttonPressed() {
label.setVisible(true);
...
executor.submit(new Runnable() {
// create the report etc.
// DO NOT ACCESS ANY UI COMPONENTS FROM HERE ANYMORE!
// ...
SwingUtilities.invokeLater(new Runnable() {
// update the UI in here
label.setVisible(false);
});
});
}
As you can see, SwingUtilities.invokeLater is used here, too. However, it is called from a background thread to make sure your UI code gets executed on the EDT instead of on the background thread. That is what it is designed for, because UI components must never be accessed (not even read from!) from a background thread. That way you have a convenient mechanism to update you label nevertheless. You could also use it to update some progress bar etc.
Related
I've got a JFrame with some JButtons. The "update" button (named JButton1) executes a number of queries which fill a form.
Now I'd like another JButton to be the "automatic update" (named JButton2), so I would like it to loop inside the JButton1ActionPerformed method, until JButton2 is no longer selected.
When I press JButton2 the window freezes.
Is my code right? Do you think I would need a stop button?
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if(jToggleButton2.isSelected()){
start = System.currentTimeMillis();
do{
do{
jButton1ActionPerformed(evt);
}
while(System.currentTimeMillis()-start>3000);
}
while(jToggleButton2.isSelected());
}
if(jToggleButton2.isSelected()){
jToggleButton2.setSelected(false);
}
}
Swing is:
Single threaded; this means that you should not perform any long running or blocking operations within the context of the Event Dispatching Thread
Not thread safe; this means you should not update the UI or anything the UI might depend on outside of the EDT. It's also risky to try and address UI elements outside the EDT, as the thread model might return false values
Without more context to your problem, I might suggest using a Swing Timer. Two main reasons:
The ActionListener is called within the context of the EDT, making it safe to update the UI from within in
You can specify a delay between updates, which reduces the risk of overloading the EDT which will cause performance issues
For example...
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if (jToggleButton2.isSelected()) {
// You could use an instance field instead and determine
// if the Timer is already running or not
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!jToggleButton2.isSelected()) {
((Timer) e.getSource()).stop();
return;
}
jButton1ActionPerformed(evt);
}
});
timer.start();
// Just want to point out that this doesn't make sense
// As if the button was selected, you'd still be in
// your previous loop
//if (jToggleButton2.isSelected()) {
// jToggleButton2.setSelected(false);
//}
}
}
Another solution might be to use a SwingWorker, which provides you with a means to perform long running or blocking operations off the EDT, but which also provides functionality to update the UI safely from within the EDT.
Take a look at:
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Swing Timers
for more details
Lets put aside that you should not crete Thread like I will show you (but use SwingWorker for example) you need to do something like this:
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
if(jToggleButton2.isSelected()){
new Thread(new Runnable(){
public void run(){
long start = null;
do{
start = System.currentTimeMillis();
jButton1ActionPerformed(evt);
}while(jToggleButton2.isSelected() &&jSystem.currentTimeMillis()-start>3000)
}
}).start();
}
Im not focusing on if your code is valid or not, its just shows how to run something in background, so you will not freeze your GUI.
Trying to get rid of this SWTException: Invalid thread access. Generated from within a JButton ActionListener. Ultimate intent is to have button open a Browser window, navigates to a URL and then URL is brought back to opening dialog...
private static final Display display = Display.getDefault();
// Fired from JButton:
class ShowBrowserAction implements java.awt.event.ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// Place-holder UI Update...
display.asyncExec(new Runnable() {
public void run() {
System.out.println("Async task run");
}
});
// Blocking until UI element is done...
while (!display.isDisposed()) {
// Always gives a thread access error, but still calls async event:
if ( !display.readAndDispatch() )
display.sleep();
}
}
}
Thoughts?
You can only call SWT actions on the SWT UI thread. Calling them anywhere else is not supported.
Use syncExec rather than asyncExec if you want to wait for a UI runnable to complete.
You say it seems to work, but this will vary on different platforms. For example on macOS it will definitely fail completely.
I am trying to do the following: click a button, button disappears for 2 seconds, text appears for 2 seconds and after those 2 seconds the visibility is reversed. So far I have done this:
btnScan.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
txtScanning.setVisible(true);
btnScan.setVisible(false);
try {
Thread.sleep(2000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
btnScan.setVisible(true);
}
});
and the result is that as soon as I click the btnScan, the whole program freezes for 2 seconds before doing anything. How do I add the delay at the correct order?
You should not call sleep method in your code that dispatches the event. All the work related UI is handled by EDT(Event Dispatch Thread) and a sleep method will cause it to freeze and hence your Swing application will freeze.
To overcome it you should use a Timer. Run the timer and execute the UI manipulation using SwingUtilities.invokeLater so that it is handled by EDT.
import java.util.Timer;
// make it a member variable
Timer timer = new Timer();
........
public void actionPerformed(java.awt.event.ActionEvent evt) {
button.setVisible(false);
timer.schedule(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
button.setVisible(true);
}
});
}
}, 2000);
}
Currently in your code, you are causing the EDT (event dispatcher thread) to pause with the invocation of Thread.sleep
Performing any long running tasks in the EDT will cause your UI to freeze.
To achieve what you desire, use a SwingWorker thread to perform your actions
This might help: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Swing is a single threaded environment, anything that blocks this thread will prevent it from processing new events, including repaint requests.
Swing is also not thread safe, meaning img you should never create or update the UI from outside the context of the EDT.
In this case you can use a Swing Timer to trigger a callback to occur at some time in the future which (the notification) will be executed within the context of the EDT, making it safe to update the UI with
Take a look at Concurrency in Swing and How to us Swing Timers for more details
Making use of Swing timer, you can do something like this:
btnScan.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
txtScanning.setVisible(true);
btnScan.setVisible(false);
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent acv) {
btnScan.setVisible(true);
txtScanning.setVisible(false);
}
});
// setRepeats(false) to make the timer stop after sending the first event
timer.setRepeats(false);
timer.start();
}
});
I want to disable a number of buttons/menu items of my GUI while a SwingWorker thread is running. The thread is started when a button is clicked.
It looks like the while loop in my code causes the CPU load to go up significantly. Did I get something wrong about how to determine if a SwingWorker thread is still running?
The CPU load's definitely lower when I update the buttons/menu items inside the SwingWorker thread. However, I felt like that shouldn't be the SwingWorker thread's job. Should it?
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
menu.setEnabled(false);
MySwingWorker worker = new MySwingWorker();
worker.execute();
while (true) {
if (worker.isCancelled() || worker.isDone()) {
menu.setEnabled(true);
break;
}
}
}
});
t.start();
}
});
Swing GUI objects should be constructed and manipulated only on the event dispatch thread (EDT). Doing so from t is incorrect. As suggested in examples cited here, condition your GIU elements on the EDT before starting the worker, and update the GUI in process() or done().
In my java application I am using swing to implement the UI. There is a button called theButton which is engaged with some IO operation in the following timely steps :
the button originally has the text "Click to connect"
then before the connect operation starts I want the theButton reads
"Connecting in progress..."
then the IO operation gets started
once the IO operation is done theButton now reads "connected ( click to disconnect)".
Issue:
I am using the following code, but first of all the button's text doesn't change to "Connecting in progress..." before the IO starts! as well button doenst actually get disabled before the IO starts! What should I do here?
--
// theButton with text "Click to connect is clicked"
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);// to avoid clicking several times! Some users cannot wait
theButton.repaint();
// doing some IO operation which takes few seconds
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
theButton.repaint();
}
});
Your problem is here:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);
theButton.repaint();
// doing some IO operation which takes few seconds // **********
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
theButton.repaint();
}
});
The code marked with the ******* comment is running on the EDT and will tie it up freezing your app and all it's painting.
Use a SwingWorker instead to run the code in a background thread.
Note that there is no need to use invokeLater(...) for code in an ActionListener since this code is already running on the EDT by default.
Also get rid of your repaint() calls since they aren't needed and they don't help.
Add a PropertyChangeListener to your SwingWorker to listen for when it is done, and then you can reset your JButton.
Instead do:
// code not compiled nor tested
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
theButton.setText("Trying to connect...");
theButton.setEnabled(false);
MySwingWorker mySwingWorker = new MySwingWorker();
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
// listen for when SwingWorker's state is done
// and reset your button.
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
theButton.setText("connected ( click to disconnect)");
theButton.setEnabled(true);
}
}
});
mySwingWorker.execute();
}
});
and
// code not compiled nor tested
public class MySwingWorker extends SwingWorker<Void, Void> {
#Override
public void doInBackground() throws Exception {
// doing some IO operation which takes few seconds
return null;
}
}
And be sure to read: Concurrency in Swing.