Multi threading with Swing progress bar - java

I am running out of ideas how to make my progress bar responsive during performing RMI connection, so I have decided to ask You for help.
Here's the code :
Thread performLogin = new Thread(new Runnable()
{
#Override
public void run()
{
LoginResult = TryLogin();
}
});
performLogin.start();
WaiterFrame.setVisible(true);
SetProgressDialog();
try
{
performLogin.join();
}
catch(InterruptedException exc)
{
System.err.println(exc.getLocalizedMessage());
}
if (LoginResult)
{ ... }
WaiterFrame.setVisible(false);
this.dispose();
Progress bar is unresponsive - does not animate as it should while performing performLogin thread. I was trying to run progress bar frame on the other thread too, but result was the same (as well as using Eventqueue.invokelater()).

The likely cause is performLogin.join(); is blocking the Event Dispatching Thread, making the UI non-responsive.
Two things to remember with Swing (and most GUI frameworks);
It is single threaded, meaning if your block the EDT for any reason, it will no longer able to process new events or perform repaints
It's not thread safe, so you should never modify the state of the UI from outside the context of the EDT.
You could use a SwingWorker, which would allow you to run your long running process in a background thread but provides a number of mechanism through which you can send updates back to the EDT safely.
See Worker Threads and SwingWorker for more details and Issues with SwingWorker and JProgressBar for an example

If you're using Java 8 you could try something like this:
CompletableFuture<LoginResult> loginResult = CompletableFuture.supplyAsync(this::tryLogin);
WaiterFrame.setVisible(true);
setProgressDialog();
loginResult.thenAccept(lr -> {
//do your thing
WaiterFrame.setVisible(false);
})
There are other options to "thenAccept" depending on what you need to do. "thenAccept" only consumes the the content of the Future.
The same could be accomplished using Guava's ListenableFuture and Executors if Java 8 is not an option.

Thank You very much MadProgrammer! Progress bar works as intended with SwingWorker usage. I'm posting code if someone would encourage same problem in future :
PerformLogin = new SwingWorker<Boolean, Object>()
{
#Override
protected Boolean doInBackground() throws Exception
{
LoginResult = TryLogin();
if (LoginResult)
{
MainF = new MainFrame();
MainF.Connection = DataEntry.TestConnection;
MainF.prepareFormToShow();
}
return LoginResult;
}
#Override
protected void done()
{
if (LoginResult == true)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
MainF.setVisible(true);
WaiterFrame.setVisible(false);
}
});
}
else
{
setVisible(true);
this.cancel(true);
JOptionPane.showMessageDialog(null, "Wrong adress!",
"Błąd",JOptionPane.WARNING_MESSAGE);
}
}
and
WaiterFrame.setVisible(true);
PerformLogin.execute();
in the main thread

Related

can't get sequential behavior Java Swing [duplicate]

basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();

Wait for thread to finish in Java

I have some code which executes a download in a separate thread, created so that the JFrame GUI will continue to update during the download. But, the purpose is completely defeated when I use Thread.join(), as it causes the GUI to stop updating. I need a way to wait for the thread to finish and still update the GUI.
You can have the task that does the download also fire an event to the GUI.
For example:
Runnable task = new Runnable() {
public void run() {
// do your download
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// call some method to tell the GUI that the download finished.
}
});
}
};
and then to run it, either use an Executor (preferred method) or a raw thread:
executor.execute(task);
or
new Thread(task).start();
As pointed out in the comments, you'd generally use a SwingWorker to do this kind of thing but you can also do the manual approach outlined above.
SwingWorker provides a doInBackground method where you would stick your download logic in, a done method where you would stick in code to notify the GUI that the download finished and a get method to get the result of doInBackground (if there was one).
E.g.,
class Downloader extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
return doDownload();
}
#Override
protected void done() {
try {
frame.downloadDone(get());
} catch (Exception ignore) {
}
}
}
(new Downloader()).execute();

What is the ways updating jProgressBar?

I need to update jProgressBar in method which read from file and do some operations.
I tried to update progress bar by this method:
public void progressUpdate(int percent) {
System.out.println("Update = "+percent);
synchronized (jMainProgressBar) {
jMainProgressBar.setValue(percent);
}
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
jMainProgressBar.updateUI();
jMainProgressBar.repaint();
}
});
}
how ever this works only then when method is done. But if i continuously updating by this method then nothing happens.
Maybe some know how to improve this method?
It also would be nice for more suggestion Worker thread and else.
You probably want to do
public void progressUpdate(final int percent) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jMainProgressBar.setValue(percent);
}
});
}
Don't use Thread. Use Timer. Refer the following:
JProgressBar in Napkin look and feel is not working
How to Use Progress Bars
http://www.roseindia.net/java/example/java/swing/SwingProgressBar.shtml
Read Concurrency in Swing for more information
Based on the comments you provided (but not from the question!) you are performing heavy work on the Event Dispatch Thread (EDT). This blocks that thread and avoids any scheduled repaints to be performed. That is why you only see the update of your JProgressBar after the work is finished, as that is the moment the EDT becomes available to perform the repaint.
The solution is already provided in the links posted by others but it basically comes down to:
perform the work on a worker thread
update the progress on the JProgressBar on the EDT
The two most common ways to achieve this are using a SwingWorker or using SwingUtilities.invokeLater from the worker thread.
All relevant links can be found in the answer of Yohan Weerasinghe
Check this out
Timer barTimer;
barTimer=new Timer(100,new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
barvalue++;
if(barvalue>jProgressBar1.getMaximum())
{
/*
* To stop progress bar when it reaches 100 just write barTime.stop()
*/
barTimer.stop();
barvalue=0;
}
else
{
int a=(int)jProgressBar1.getPercentComplete();
jProgressBar1.setStringPainted(true);
jProgressBar1.setValue(barvalue);
}
}
});
barTimer.start();
Check the code at this link
http://java23s.blogspot.in/2015/10/how-to-implement-progress-bar-in-java.html

Java Altering a GUI from a different class

So my program has multiple classes and after one of them has run, it'd like it so it appends the text area in the main class GUI with a 'finished' message
ta.append("Search Complete\n");
and this is the code that needs to complete
statCl.addActionListener(new ActionListener(){
public void actionPerformed (ActionEvent e) {
try {
ta.append("Searching...\n");
task.execute();
} catch (Exception IOE) {}
}
});
So it is in the class where task where actual code runs.
Any advice or help would be amazing, thanks.
If the task.execute() method doesn't start launch an operation in another thread, then the GUI will be freezed, and nothing will apear in the text area until the operation is finished. So you might just have
ta.append("Searching...\n");
task.execute();
ta.append("Finished");
If the operation is launched in a new thread, then this thread should append in the text area, but it should make sure this append is done in the event dispatch thread (EDT). Your code could thus look like this :
public class Task {
private JTextArea ta;
public Task(JTextArea ta) {
this.ta = ta;
}
public void execute() {
Thread t = new Thread(new Runnable() {
// perform the long operation
// at the end, update the text area, in the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ta.append("finished");
}
});
}
t.start();
}
}
You might also look at SwingWorker, which is designed just for that (and other things like progress update). There is a code example in its class javadoc which does just what you're trying to do.
You should not be performing long-running task on EDT (event dispatching thread):
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
Swing does all it's work on EDT, so you should not block EDT, e.g. run intensive tasks on it. Note: all event handlers are executed on EDT.
So there are two challenges:
Run intensive tasks in a background thread.
Update GUI, which must be done on EDT.
Use SwingWorker to solve this two issues.

Java's Swing Threading

My understanding is that if I start up another thread to perform some actions, I would need to SwingUtilities.invokeAndWait or SwingUtilities.invokeLater to update the GUI while I'm in said thread. Please correct me if I'm wrong.
What I'm trying to accomplish is relatively straightforward: when the user clicks submit, I want to (before performing any actions) disable the submit button, perform the action, and at the end of the action re-enable the button. My method to perform the action updates the GUI directly (displays results) when it gets the results back.
This action basically queries a server and gets some results back.
What I have so far is:
boolean isRunning = false;
synchronized handleButtonClick() {
if ( isRunning == false ) {
button.setEnabled( false );
isRunning = true;
doAction();
}
}
doAction() {
new Thread() {
try {
performAction(); // Concern A
} catch ( ... ) {
displayStackTrace( ... ); // Concern B
} finally {
SwingUtilities.invokeLater ( /* simple Runnable to enable button */ );
isRunning = false;
}
}
}
For both of my concerns above, do I would have to use SwingUtilities.invokeAndWait since they both will update the GUI? All GUI updates revolve around updating JTextPane. Do I need to in my thread check if I'm on EDT and if so I can call my code (regardless of whether it updates the GUI or not) and NOT use SwingUtilities.invokeAndWait?
EDIT: Here is what I am doing now:
handleButtonClick() {
if ( isRunning == true )
return;
disable button;
SwingWorker task = new MyTask();
task.execute();
}
...inside MyTask
doInBackground() {
return performAction();
}
done() {
result = get();
enable button;
isRunning = false;
interpret result (do most of the GUI updates here);
}
While performAction() does some GUI updates, I have wrapped those in:
if ( SwingUtil.isEDT() )
doGUIupdate()
else
SwingUtil.invokeLater( new Runnable() {
run() {
doGUIupdate();
}
} );
Hopefully this is a step in the right direction, please comment if you believe there are better ways to handle my situation.
In my opinion you should almost never use invokeAndWait(). If something is going to take awhile that will lock your UI.
Use a SwingWorker for this kind of thing. Take a look at Improve Application Performance With SwingWorker in Java SE 6.
You should consider using SwingWorker since it will not block the UI thread, whereas both SwingUtilities methods will execute on the EDT thread, thus blocking the UI.
I keep the simple Thread inside EventQueue.invokeLater(...) and that worked smoothly...
java.awt.EventQueue.invokeLater(new Runnable() {
public void run(){
new Thread(new Runnable(){
public void run(){
try{
EdgeProgress progress = EdgeProgress.getEdgeProgress();
System.out.println("now in traceProgressMonitor...");
while(true){
// here the swing update
if(monitor.getState() == ProgressMonitor.STATE_BUSY){
System.out.println(monitor.getPercentDone()/2);
progress.setProgress(monitor.getPercentDone()/2);
}else{
break;
}
Thread.sleep(5);
}
}catch(InterruptedException ie){}
}
}).start();
}
});

Categories