Java Swing redraw delay - java

I'm writing a little private application at the moment which utilizes the Wolfram Alpha Java Bindings. The application's GUI is realized using Swing. My Wolfram Alpha Plugin has a function looking like this:
public void fire(String value) {
// This sets the field state to loading
_state = loading;
// This tells the controller to redraw the window
// When reading the state of the plugin it sets an
// animated gif at a JPanel. This should be displayed
// before the application is blocked by the Wolfram Alpha
// Request.
// updateInput does nothing more then call repaint after some
// other function calls. No special threading included.
getController().updateInput("");
// My wolfram request follows:
WAQuery query = _engine.createQuery();
query.setInput(value);
try {
// For educational purposes, print out the URL we are about to send:
System.out.println("Query URL:");
System.out.println(_engine.toURL(query));
System.out.println("");
// This sends the URL to the Wolfram|Alpha server, gets the XML result
// and parses it into an object hierarchy held by the WAQueryResult object.
WAQueryResult queryResult = _engine.performQuery(query);
if (queryResult.isError()) {
System.out.println("Query error");
System.out.println(" error code: " + queryResult.getErrorCode());
System.out.println(" error message: " + queryResult.getErrorMessage());
} else if (!queryResult.isSuccess()) {
System.out.println("Query was not understood; no results available.");
} else {
// Got a result.
System.out.println("Successful query. Pods follow:\n");
for (WAPod pod : queryResult.getPods()) {
if (!pod.isError()) {
System.out.println(pod.getTitle());
System.out.println("------------");
for (WASubpod subpod : pod.getSubpods()) {
for (Object element : subpod.getContents()) {
if (element instanceof WAPlainText) {
System.out.println(((WAPlainText) element).getText());
System.out.println("");
}
}
}
System.out.println("");
}
}
// We ignored many other types of Wolfram|Alpha output, such as warnings, assumptions, etc.
// These can be obtained by methods of WAQueryResult or objects deeper in the hierarchy.
}
} catch (WAException e) {
e.printStackTrace();
}
}
The Problem is that the request is started BEFORE the window is repainted. I have a request delay of about one second and THEN the loading animation is displayed. This animation should be displayed before the request is started.
What I tried so far:
Thread.sleep() in front of the Wolfram Query. But that would block the repaint too?! - same unwanted behavior
Put the request into a runnable - same unwanted behavior
Put the request into a class extended by Thread - same unwanted behavior
Combine both with Thread.sleep() - same unwanted behavior
What am I overseeing here?

I have a request delay of about one second and THEN the loading animation is displayed. This animation should be displayed before the request is started.
Never use Thread.sleep() in Swing application that sometime hangs the whole application as you stated it already in your post. You should use Swing Timer in that case.
Please have a look at How to Use Swing Timers
Sample code:
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// start loading animation now
}
});
timer.setRepeats(false);
timer.start()

What I suggest is:
Use background threading, specifically a SwingWorker'
Do the long running code in a SwingWorker's doInBackground method.
Start your animation display before executing your SwingWorker.
Give the SwingWorker a PropertyChangeListener, and when the SwingWorker's state is done, then stop the animation.
Be sure that the animation does not tie up the Swing event thread either. Use a Swing Timer for it.
For example,
// start animation -- be sure to use a Swing Timer or
// other way to prevent tying up the Swing event thread
SwingWorker<Void, Void> myWorker = new SwingWorker<Void, Void>() {
protected Void doInBackground() throws Exception {
fire(someValue);
return null;
};
};
myWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
// .... stop the animation here
}
}
});
myWorker.execute();

Related

Update GUI from another class in background thread

I come from .NET environment where event listening is pretty easy to implement even for a beginner. But this time I have to do this in Java.
My pseudo code:
MainForm-
public class MainForm extends JFrame {
...
CustomClass current = new CustomClass();
Thread t = new Thread(current);
t.start();
...
}
CustomClass-
public class CustomClass implements Runnable {
#Override
public void run()
{
//...be able to fire an event that access MainForm
}
}
I found this example but here I have to listen for an event like in this other one. I should mix them up and my skill level in Java is too low.
Could you help me elaborating a optimal solution?
I think that what you are looking for is SwingWorker.
public class BackgroundThread extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
// background calculation, will run on background thread
// publish an update
publish("30% calculated so far");
// return the result of background task
return 9;
}
#Override
protected void process(List<String> chunks) { // runs on Event Dispatch Thread
// if updates are published often, you may get a few of them at once
// you usually want to display only the latest one:
System.out.println(chunks.get(chunks.size() - 1));
}
#Override
protected void done() { // runs on Event Dispatch Thread
try {
// always call get() in done()
System.out.println("Answer is: " + get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Of course when using Swing you want to update some GUI components instead of printing things out. All GUI updates should be done on Event Dispatch Thread.
If you want to only do some updates and the background task doesn't have any result, you should still call get() in done() method. If you don't, any exceptions thrown in doInBackground() will be swallowed - it is very difficult to find out why the application is not working.

Java Swing Thread changing UI - complications

I tried this for many hours.. I have a thread that changes a JTextField of my UI, which completely destroys the UI. The Thread (lets call it Thread A) is generated by an ActionListener. The .setText() function call is in a extra thread (B) created by Thread A. Thread B is the Parameter of SwingUtilitis.invokeAll() and/or SwingUtilities.invokeAndWait(). I tried them both. Here's some code to make it more clear.
This is my ActionListener which creates Thread A - shortened of course:
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == window.getBtn_Search()) {
Refresher refresh = new Refresher();
refresh.start();
}
}
This is my Thread A, which later puts Thread B into the EDT Queue:
public class Refresher extends Thread implements Runnable {
private int counter = 0;
private UI window = null;
private int defRefresh = 0;
#Override
public void run() {
while(true){
-bazillion lines of code-
do {
try {
Refresher.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(window.canceled()) break;
UI.updateCounter(window.getLbl_Status(), (Configuration.getRefreshTime()-counter));
counter++;
} while (counter <= Configuration.getRefreshTime());
- more code-
}
}
}
The UI.updateCounter(...) will queue Thread B into the EDT.
public static void updateCounter(final JLabel label, final int i) {
try {
SwingUtilities.invokeAndWait(
new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Now when the last function gets called, everything gets messed up. I tried different stuff for hours and nothing worked. I also tried using SwingWorker, but the some or nothing at all happened.
The invokeAndWait() tried allows to post a Runnable task to be executed on the EDT, but it blocks the current thread and waits until the EDT is done executing the task.
But there is deadlock potential in invokeAndWait(), as there is in any code that creates a thread interdependency.
If the calling code holds some lock (explicitly or implicitly) that the code called
through invokeAndWait() requires, then the EDT code will wait for the non-
EDT code to release the lock, which cannot happen because the non-EDT code
is waiting for the EDT code to complete, and the application will hang.
As we can see here, modifying the JLabel component passed by the waiting non-
EDT code.
Instead we can use
invokeLater() takes
care of creating and queuing a special event that contains the Runnable. This event is processed on the EDT in the order it was received, just like any other event.
When its time comes, it is dispatched by running the Runnable’s run() method.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
});
OR
isEventDispatchThread() that returns true if the calling code is currently being executed on the EDT, false otherwise.
Runnable code= new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
}
);
if (SwingUtilities.isEventDispatchThread()) {
code.run();
} else {
SwingUtilities.invokeLater(code);
}
In general, labels are not very good at displaying text which change: their width change, and the layout with it.
Using a read-only JTextField, perhaps with proper changes in style, could be a better solution.
I think the intermediate JPanels you've created may count as validation roots. Therefore the revalidate() that automagically happens when you call setText() does not cause any layout changes higher than the level of the JPanel parent.
I don't think you actually need the panels, since a JLabel can contain both an Icon and text. See the tutorial.
So my advice is to remove the panels or, if they serve a purpose, make sure isValidateRoot() on the panels returns false.
When changing the label's text you should at least call repaint()/revalidate() on the label's topmost container, triggering a relayout, assuming the label calls invalidate()/revalidate() correctly on text change.

JTextArea text disappears

I'm making a chess program for a project. I'm trying to add a move history box to the side of the board. The move history works fine, and the data is properly sent to the text area, but the text inside the JTextArea disappears while the AI is thinking about his move.
public void aiMove(){
if (!playing){ return; }
paintImmediately(0,0,totalX,totalY);
ai = eve.getMove(chess,wtm,aiOut); //text disappears here
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
writeMove(ai); //updates move history, text reappears here
playing = stillPlaying();
repaint();
}
private void writeMove(Move move){
char c = "abcdefgh".charAt(7-move.fromY);
char h ="abcdefgh".charAt(7-move.toY);
String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+" ";
if (!wtm){
String q = chess.getFullMove()+". "+s+" ";
moves.setText(moves.getText()+q);
}
else {
moves.setText(moves.getText()+s+"\n");
}
}
Here's a print screen of what's happening.
http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png
SOLVED
Thanks to all replies. I changed aiMove() so it creates a thread. Here is what I did.
Attempt #3... swing is still so foreign to me. I didn't want to change writeMove to getMove or I would have to rewrite the human's turn slightly. Since the project is essentially done, I am trying to avoid as much work as possible :)
The GUI is entirely optional anyways, I was just doing it for fun, and to try and learn a bit of swing.
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
It also fixed a problem I had before, in that if I were to hold down the 'a' key (force ai move), it would queue up many forced ai moves. Now that doesn't happen.
The problem is your AI thinking is CPU intensive/time consuming, thus it is considered a long running task. You should not do long running tasks on GUI Event Dispatch Thread as this will cause the UI to seem frozen and thus only show updates after the task has finished.
Fortunately there are 2 different approaches you could use:
Use a Swing Worker which as the tutorial states:
The SwingWorker subclass can define a method, done, which is
automatically invoked on the event dispatch thread when the background
task is finished.
SwingWorker implements java.util.concurrent.Future.
This interface allows the background task to provide a return value to
the other thread. Other methods in this interface allow cancellation
of the background task and discovering whether the background task has
finished or been cancelled.
The background task can provide
intermediate results by invoking SwingWorker.publish, causing
SwingWorker.process to be invoked from the event dispatch thread.
The background task can define bound properties. Changes to these
properties trigger events, causing event-handling methods to be
invoked on the event dispatch thread.
Alternatively create separate Thread for AI thinking and wrap setText call in SwingUtilities.invokeLater(...);
Thread t=new Thread(new Runnable() {
#Override
public void run() {
}
});
t.start();
UPDATE
After reading MadProgrammers comment (+1 to it) please remember to create/manipulate your GUI/Swing components on EDT via the SwingUtilities.invokeLater(..) block. You can read more on it here.
UPDATE 2:
That edit is defeating the point, the only call on EDT in SwingUtilitites block should be the setText or atleast only code that manipulates a Swing component i.e
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){ //originally initialized by constructor
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}

JButton stays in pressed state

In my Java GUI app I have a JButton and when clicked it calls a function to connect to a database, then calls a function to clear a table in the DB, then calls a function that reads text from one file and loads variables, which calls a function that reads text from another file, compares the data from both and then calls a function to either update or insert data in the DB, all of that works fine.
However my question is related to the JButton, when its clicked I want to run a Indeterminate progress bar just so the user knows work is being done and then right before it leaves the the action listener setIndeterminate to false and set the value of the progress bar to 100(complete), but in my case when you click the button it stays in the clicked state and the progress bar freezes.
What should I implement to prevent this? threading possibly? but Im quite new to threading in java. here is my action listener:
private class buttonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if( e.getSource() == genButton )
{
progressBar.setIndeterminate(true);
progressBar.setString(null);
try
{
dbConnect(); //connects to DB
clearSchedules(); // deletes data in tables
readFile(); // reads first file and calls the other functions
dbClose();// closes the DB
progressBar.setIndeterminate(false);
progressBar.setValue(100);
}
catch (Exception e1){
System.err.println("Error: " + e1.getMessage());
}
}
}
}
On a side note, I would like to have the action bar actually move as the the program progresses but I wasnt sure how to monitor its progress.
Thanks, Beef.
UPDATE here is my example of SwingWorker and how I used it:
Declared globally
private functionWorker task;
private abstract class functionWorker extends SwingWorker {
public void execute() {
try {
dbConnect();
} catch (SQLException e) {
e.printStackTrace();
}
clearSchedules();
try {
readFile();
} catch (IOException e) {
e.printStackTrace();
}
dbClose();
}
}
Inside my actionPerformed method
if( e.getSource() == genButton )
{
progressBar.setIndeterminate(true);
progressBar.setString(null);
try
{
task.execute();
progressBar.setIndeterminate(false);
progressBar.setValue(100);
}
catch (Exception e1){
System.err.println("Error: " + e1.getMessage());
}
}
The problem is probably related to connecting to doing expensive operations in the UI thread (connecting to a database, reading from a file, calling other functions). Under no circumstances should you call code that uses excessive CPU time from the UI thread, as the entire interface can't proceed while it is executing your code, and it results in a 'dead' looking application, with components remaining in their state at the time before an expensive operation until completion. You should execute another thread, do the expensive work in that, and then use a SwingUtilities.invokeLater(Runnable doRun) with a passed runnable where you'd update the progress.
There may be synchronisation issues relating to the states of components, but you can fix these later.
Could I create the new thread when the action is performed and call the new functions in the thread, or should I do the threading within the actual function itself?
You can start a SwingWorker from your button's handler, as shown here. A related example implementing Runnable is seen here.
One method to handle progressbars are to extend SwingWorker in a class.
SwingWorker takes care of running background tasks for you and so you do not have to implement your own threading that can end up in unknown issues.
To begin with, your class that takes care of progress bar UI should implement PropertyChangeListener
And implement public void propertyChange(PropertyChangeEvent evt) { - to update the progressbar status based on a global variable.
The background task class should look like the following(this could be an inner class) :
class ProgressTask extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
//handle your tasks here
//update global variable to indicate task status.
}
#Override
public void done() {
//re-enabled your button
}
}
on your button's event listener :
public void actionPerformed(ActionEvent evt) {
//disable your button
//Create new instance of "ProgressTask"
//make the task listen to progress changes by task.addPropertyChangeListener(this);
//calll task.execute();
}
I have tried to water down code example, you would have to read some tutorial to understand how all these pieces fit together. However, the main point is do not code your Threads, instead use SwingWorker
progressBar.setIndeterminate(true);
progressBar.setValue(0);
dbConnect(); //connects to DB
progressBar.setIndeterminate(true);
progressBar.setValue(10);
clearSchedules(); // deletes data in tables
progressBar.setIndeterminate(true);
progressBar.setValue(50);
readFile(); // reads first file and calls the other functions
progressBar.setIndeterminate(true);
progressBar.setValue(75);
dbClose();// closes the DB
progressBar.setIndeterminate(false);
progressBar.setValue(100);
You will need to tell the progress bar how much progress has been made because it does not know the percentage completed. Better yet, write a method that updates and repaints the progress bar rather than repeating the method calls here.
updateProgressBar(int progress, boolean isDeterminate, String msg){};
You will also need to make sure that your specific button is firing the action performed.
class IvjEventHandler implements java.awt.event.ActionListener {
public void actionPerformed(java.awt.event.ActionEvent e) {
if (e.getSource() == JMyPanel.this.getJButtonUpdate())
connEtoC1(e);
};
};
The connEtoC1(e); should execute a controller class or SwingWorker rather than firing from the GUI

Output to jTextArea in realtime

I have some code which takes a few minutes to process, it has to connect to the web for each string in a long array, each string is a url. I want to make it so that everytime it connects, it should refresh the jtextarea so that the user is not staring into a blank page that looks frozen for 20 min. or however long it takes. here is an example of something i tried and didnt work:
try {
ArrayList<String> myLinks = LinkParser.getmyLinksArray(jTextArea1.getText());
for (String s : myLinks) {
jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(jTextArea1, "Parsing Error", "Parsing Error", JOptionPane.ERROR_MESSAGE);
Logger.getLogger(MYView.class.getName()).log(Level.SEVERE, null, ex);
}
The problem is that you need to perform the computation asynchronously. You should create a background thread that performs the computation, and then use SwingUtilities.invokeLater to update the JTextArea.
final ArrayList<String> myLinks = //...
(new Thread()
{
public void run(){
for (String s : myLinks) {
try{
final String result = LinkChecker.checkFileStatus(s) + "\n";
SwingUtilities.invokeLater(new Runnable(){
public void run(){
jtextArea2.append(result);
}
});
}catch(IOException error){
// handle error
}
}
}
}).start();
Edit
It has been pointed out that JTextArea's append function actually is thread safe (unlike most Swing functions). Therefore, for this particular, case it is not necessary to update it via invokeLater. However, you should still do you processing in a background thread so as to allow the GUI to update, so the code is:
final ArrayList<String> myLinks = //...
(new Thread()
{
public void run(){
for (String s : myLinks) {
try{
jtextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}catch(IOException error){
// handle error
}
}
}
}).start();
However, for pretty much any other operation that modifies a Swing object, you will need to use invokeLater (to ensure the modification occurs in the GUI thread), since almost all the Swing functions aren't thread safe.
You need to investigate threading and its relationship to GUI updates in Swing. Anything that affects or makes use of GUI components in Swing must done on a special thread called the Event Dispatch Thread (EDT).
If your code snippet, if it's freezing the GUI, I imagine that it is being run in the EDT. Performing a long-running action on the EDT will make the GUI unresponsive, because no further updates can be done while your long-running process is using the thread.
There is a helper class called SwingWorker that allows you to offload long-running computations to a background thread, and then make updates to the GUI thread when it is complete. The SwingWorker looks after the context switches between the GUI thread and the background thread. You can also display progress bars to let the user know the state of the long-running process, so they know your application hasn't hung.
swing/awt is a single threaded library, so once a component is shown, just changing it's appearance won't work correctly. You need to change the component on the GUI Thread, not from your thread. To do this wrap any code that updates a component with SwingUtilities.invokeLater... as in
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea2.append(LinkChecker.checkFileStatus(s) + "\n");
}
});
also you want to limit what you do on the gui thread to avoid the gui from becoming sluggish, so if checkFileStatus is time consuming, execute it outside the run method and store the result in a final local variable, and just access the variable in the run() code.

Categories

Resources