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
Related
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();
So I have been trying to implement a progress indicator with no luck. I am not sure I understand managing threads with JavaFx very well, despite having read a bit about the Platform.RunLater and Tasks. So here is my use case.
My program allows users to connect to a database and look at some of the schemas and other objects in the database. Sometimes connecting to a large database and pulling up all its tables and info takes a while, so I would like to show a progress indicator. I am not trying to update the progress at all I would just like to make the progress indicator visible at a value of -1 while the process is running to pull everything from the database. Ideally I will have a progress indicator loaded in from an FXML file invisible. When I start the process of pulling info from the database I would like to make it visible.
When trying to make my progress visible it never showed up, so I decide to start out having it visible and making it invisible, just to see what happens. The progress indicator rotated nicely when I opened the program up, but as soon as I try to connect to the database it stopped rotating and just froze. I assume this is what happens when I try to make it visible too which is why it was never showing up.
The following is my current code, I would appreciate any detailed help with explanations so I can understand what is going on. Thanks
from the method that is doing most of the work.
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.start();
//Wait until the thread is done
try{
t.join();
}
catch(Exception e){
e.printStackTrace();
}
//Retrieve the dbextractor from the thread
DbExtractor dbe = t.getDbe();
//move on to the next page in the application
this.caster.goToDataSource(c, cp, dbe);
The ExtractorThread which does the work.
private class ExtractorThread extends Thread{
private ConnectionProperties cp;
private DbExtractor dbe;
public void run() {
dbe = new DbExtractor(cp);
try {
dbe.extract();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public DbExtractor getDbe() {
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
If I am supposed to use the Platform.RunLater I am not sure where to use it or why. Any help would be appreciated, thanks!
Use the javafx.concurrent API. Extend Task instead of Thread:
private class ExtractorThread extends Task<DbExtractor>{
private ConnectionProperties cp;
public DbExtractor call() throws Exception {
dbe = new DbExtractor(cp);
dbe.extract();
return dbe;
}
public void setCp(ConnectionProperties cp) {
this.cp = cp;
}
}
Then do:
//make progress indicator visible
pi.setVisible(true);
// separate non-FX thread
final ExtractorThread t = new ExtractorThread();
t.setCp(cp);
t.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
public void handle(WorkerStateEvent event) {
DbExtractor dbExtractor = t.getValue();
this.caster.goToDataSource(c, cp, dbe);
}
});
t.setOnFailed(...); // similarly, to handle exceptions
new Thread(t).start();
I don't code JavaFX, and so I can't give you chapter and verse, but this line:
t.join();
will block the calling code until the background thread is through. Don't do this. Instead use some type of listener to get notified when the background thread finishes. If this were Swing, I'd use a PropertyChangeListener added to a SwingWorker to notify me when the background thread was done. I think that you can still use a PropertyChangeListener to do a similar thing with with JavaFX, but I cannot tell you if this would represent the canonical solution.
Also, don't extend Thread but instead implement Runnable. This won't fix your problem but is basic Java common sense.
I am trying to implement a repeat function on a custom MIDI player, but I am unable to implement a repeat function. Here are the classes I am using:
NotePlayer - Plays MIDI notes using Java's MIDI package.
GuitarTunerGUI
Interface to the NotePlayer class.
Provides six JButtons for each guitar string, a JComboBox for selecting the desired tuning, and a JCheckBox for toggling the repeat function.
Provides toggleRepeat() for toggling the repeatEnabled field, a private field of the GuitarTunerGUI class.
I created a SwingWorker that is responsible for playing a MIDI note in a separate thread. This solves the issue of keeping the GUI responsive while the note is being played.
However, a problem arises when repeat is enabled and the user pushes more than one button.
When the user pushes one of the six JButtons the listener does the following:
public void actionPerformed(ActionEvent event) {
// The note param is a private field of the listener object
MusicianWorker clapton = new MusicianWorker(note);
clapton.execute();
}
The execute method does the following:
protected Void doInBackground() throws Exception {
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
} while (repeatEnabled);
return null;
}
An issue arises when the user pushes multiple buttons without toggling repeat. For example, when the 'A' button and the 'E' button are pushed sequentially, two threads are created and the 'A' and 'E' notes are both played repeatedly until repeatEnabled is toggled off. When the user pushes a JButton I need to first determine if any worker threads are currently executing and, if so kill those threads before playing the specified note. Thanks in advance for your time and feedback.
You need to maintain shared state between your workers. Introduce new boolean variable "playing". Before execution check whether playing flag is set to true, after execution set it to false again.
The code you have given is great, it just needs to be tweaked a little bit. When you create your SwingWorker, you should keep track of it in an instance variable (maybe in a List if you are going to be wanting to play multiple notes at some point?). Then, before playing a new note you check to see if the last note has finished, and if not, you cancel it.
Whether or not cancellation will have any effect on your MusicianWorker is up to you. The worker thread will be interrupted, which would mean that your Thread.sleep method would prematurely terminate if it is running - you would have to check your docs to see what effect it would have on NotePlayer.
Lastly, it seems that you don't actually need to be using the SwingWorker at all, since your background task is not interacting with the UI. You might want to investigate Executors.
You could try something like this:
public class AlbertHall {
private final ExecutorService es = Executors.newSingleThreadExecutor();
// No longer a local variable in the listener
private Future<Void> clapton; // ... or maybe a Collection of Futures
private class Listener implements ActionListener {
private final Note note;
public Listener(Note note) {
this.note = note;
}
public void actionPerformed(ActionEvent event) {
// Watch out, Clapton may finish after you have asked if he is done
// but before you call cancel
if (clapton != null && !clapton.isDone()) clapton.cancel(true);
// You may need to have a wait loop here if Clapton takes a while
// to leave the stage
// Next note
clapton = es.submit(new MusicianWorker(note));
}
}
static class MusicianWorker implements Runnable {
private final Note note;
public MusicianWorker(Note note) {
this.note = note;
}
public void run() {
boolean cancelRequested = false;
do {
NotePlayer.playNote(thisNote);
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
// Looks like we got cancelled
cancelRequested = true;
}
} while (repeatEnabled && !cancelRequested);
}
}
}
I have a program where I am loading a file while at the same time I am displaying a window to inform the user that the file is being loaded. I decided to make a FileLoader class that was a SwingWorker which actually handled loading the file and a ProgressWindow that implements PropertyChangeListener to inform the user about the status of the SwingWorker that was passed into it.
My code currently looks like this:
FileLoader loader = new FileLoader(filePath);
new ProgressWindow(loader, "Loading File", "Loading File");
//ProgressWindow's constructor calls loader.execute() inherited from SwingWorker
doc = loader.get(); //GUI Freezes when called
The problem is that whenever I call loader.get(), it freezes the GUI, thus the progress bar in the Progress Window doesn't run and the whole thing is pointless. As far as I can tell, this is because the thread controlling the GUI is the same thread that calls loader.get(), which goes on hold while loader.execute() is running.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
I've considered creating a ChangeListener for when SwingWorker.isDone() and then running loader.get(), but this would require some reworking of my code that I would rather not do.
Could anyone tell me what the best way is to get this to work?
get() is like join() in that it will block until called, and will wait for the SwingWorker to finish before being called. Using it wrongly can completely nullify all the advantages of using a SwingWorker in the first place.
Solution: Don't call get() until you know that the SwingWorker is done with its processing, by either calling it in the SwingWorker's done() method, or if you need to call it from the calling code, then in a PropertyChangeListener that has been added to the SwingWorker when the SwingWorker's "state" property is SwingWorker.StateValue.DONE.
Something like:
final FileLoader loader = new FileLoader(filePath);
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
// since DONE is enum, no need for equals(...) method
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
loader.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
});
new ProgressWindow(loader, "Loading File", "Loading File");
Note: code not compiled nor tested
Edit: try/catch added.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
If you call SwingUtilities.invokeLater() on the thread that will execute the thread in the EDT which freezes the GUI. Instead, run the thread by calling it's start() method and only use SwingUtilities.invokeLater() when you need to update the progress bar in the PropertyChangeListener.
I have create a WorkerThread class which take care of Threads and GUI current/main thread .
i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the Server GUI...");
// initializing the Server
try {
xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}
When one of the panels present in a JTabbedPane is clicked, I need to perform a few actions at the start. Say, for example, I need to check the username and password. Only if those match, the particular panel operations need to be performed. Can you suggest any methods?
Not sure I fully understand your question, but I would do something like:
Add a ChangeListener to the JTabbedPane to listen for the first tab click.
When a ChangeEvent occurs perform the login on a background thread using a SwingWorker.
If the login is successful perform the required UI operations on the Event dispatch thread.
For example:
tabbedPane.addChangeListener(new ChangeListener() {
private boolean init;
public void stateChanged(ChangeEvent e) {
if (!init) {
init = true;
new SwingWorker<Boolean, Void>() {
#Override
protected void done() {
try {
boolean loggedIn = get();
if (loggedIn) {
// Success so perform tab operations.
}
} catch (InterruptedException e1) {
e1.printStackTrace(); // Handle this.
} catch (ExecutionException e1) {
e1.printStackTrace(); // Handle this.
}
}
protected Boolean doInBackground() throws Exception {
// Perform login on background thread. Return true if successful.
return true;
}
}.execute();
}
}
});
The action to change the tab is triggered by a mouse listener in the UI class. it goes through and checks whether there is a tab at the clicked coordinate and if so, whether the tab is enabled. If that criteria is met, it will call setSelectedIndex(int) on your JTabbedPane. In order to intercept the tab changing, what you can do is override setSelectedIndex(int) to trigger a permissions check. Once the permissions are validated, you can make a call to super.setSelectedIndex(int). this should do what you want.
please note that if the permissions check is a long running call (ie a call to a database or a server), you should use something like a SwingWorker break up your processing, so that the permissions check is done off the AWT EventQueue and the call to super.setSelectedIndex(int) is done on the AWT EventQueue.