Adding a repeat function to a simple MIDI player - java

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);
}
}
}

Related

JavaFx Progress Indicator freezes when I run another thread

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.

Synchronize sound effect with other Timer actions

I have this Timer that is supposed to initiate various actions at 1 second intervals. It's a pretty simple idea that simulates a 5 second countdown (literally). At the start, a JLabel is updated to set its text to "5". Simultaneously, a little mp3 sound file plays that voices the number that the user sees on the screen. Then, one second later, the text is changed to "4" and a different mp3 plays that voices the number 4. And so on until we get to zero.
This all works, but I can't get the visual updating to synchronize precisely with the audio part. The mp3 always seems to play just slightly before the screen is updated. At first, I thought that I just needed to put a little extra silence at the beginning of each mp3 on a trial and error basis until things synched up. But no matter how much silence I prepend to each mp3, I still hear the audio before the screen updates. All that changes is the lag between each "one second" update.
Anyway, here is the code that I am working with. Can anyone help me get this to synchronize? Maybe I need a second timer? I'm not sure how that would work. Thanks in advance!
class Countdown extends JFrame implements ActionListener {
private Timer countdownTimer = new Timer(1000, this);
int countdownSeconds;
MyJFrame myFrame;
public Countdown(MyJFrame thisFrame) {
int countdownSeconds = 5;
countdownTimer.start();
myFrame = thisFrame;
}
#Override
public void actionPerformed(ActionEvent e) {
if (countdownSeconds == 0) {
myFrame.updateCountdown(myFrame, "Go");
SoundEffect.play("launch.mp3");
countdownTimer.stop();
} else {
myFrame.updateCountdown(myFrame, Integer.toString(countdownSeconds));
if (countdownSeconds == 5) {SoundEffect.play("five.mp3");}
if (countdownSeconds == 4) {SoundEffect.play("four.mp3");}
if (countdownSeconds == 3) {SoundEffect.play("three.mp3");}
if (countdownSeconds == 2) {SoundEffect.play("two.mp3");}
if (countdownSeconds == 1) {SoundEffect.play("one.mp3");}
countdownSeconds--;
}
}
}
public void updateCountdown(MyJFrame thisFrame, String numSec) {
lblCountdown.setText(numSec);
}
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class SoundEffect {
public static void play(String mp3File) {
try {
FileInputStream mp3_file = new FileInputStream(mp3File);
Player mp3 = new Player(mp3_file);
mp3.play();
}
catch(Exception e) {
System.out.println(e);
}
}
}
I highly doubt you will ever be able to sync those perfectly, but I can explain why the current approach will not work.
Swing components must be updated on the Event Dispatch Thread, as you do with the Timer. When you update the text of the label, it will schedule a repaint on the Event Dispatch Thread. Note the word schedule, and not perform.
However, the Event Dispatch Thread is currently busy playing your sound, so the actual repaint operation will only occur after you called mp3.play().
Now you could (if allowed, not sure about the threading rules for playing MP3's) try to play the mp3 on a different Thread (e.g. by using a secondary non-Swing timer). But since you never have full control over when the actual repaint is going to happen and only can control when the repaint is scheduled, the visual and auditive updates can still be out-of-sync.
The major part of the problem comes down to:
if (countdownSeconds == 5) {SoundEffect.play("five.mp3");}
..leading to..
public class SoundEffect {
public static void play(String mp3File) {
try {
FileInputStream mp3_file = new FileInputStream(mp3File);
Player mp3 = new Player(mp3_file);
mp3.play();
}
catch(Exception e) {
System.out.println(e);
}
}
}
Whoa! This is not the time to be loading the clips!
Instead they should be loaded before the timer ever starts. I think the file I/O is the real cause of the (perceptible) lag or delay.

How to make the main method wait for input on GUI without using Listener as a direct trigger?

I am working on a webscraping tool that should perform various operations with the scraped data.
Because of this, I need various different GUIs to work in an orderly manner and because of that, I need the main method to wait before each has completed it's purpose.
After searching for a while, I have found the following StackOverflow questions that provided some clues on how to solve the problem, but that I could not implement because they have some differences to my case:
How to wait for input in a text field
How to make main thread wait a different thread to finish
I know I can trigger code using a Listener to a/the GUI's components (a button, for example), but i'm having a hard time making the main-thread wait for that listener to wake it up, while the code for the GUI's thread (when there is one) is initialized by the main thread...
This is an simplified code to demonstrate how the program is supposed to work:
public class Main {
/*
* Waiter is a simple GUI with just an "Start" button in it. Here in place of my actual GUIs.
*/
private static Waiter auth; //Represents my NTLM-authentication form.
private static Waiter status; //Represents a status-feedback GUI that will be displayed during processing.
private static Waiter operation; //Represents a GUI in with the user choses what to do with the gathered data.
public static void main(String[] args) throws InterruptedException {
auth = new Waiter();
auth.setVisible(true);
System.out.println("NTLM Authentication form. Should wait here until user has filled up the GUI and clicked \"Start\".");
System.out.println("Authenticates WebClient's NTLM using data inputed to the GUI...");
auth.dispose();
Thread srt = new Thread(status = new Waiter());
srt.start();
status.setVisible(true);
//Performs webscraping operations...
System.out.println("Prepares the webscraped data here...Things like downloading files and/or parsing text...");
System.out.println("Keeps the user aware of the progress using the \"status\" GUI.");
status.setVisible(false);
//Clears the status GUI.
operation = new Waiter();
operation.setVisible(true);
System.out.println("Operation selection form. Should wait here until user selects an option.");
System.out.println("Starts performing the operation(s)...");
operation.dispose();
status.setVisible(true);
System.out.println("Performs the operation(s), while giving status-feedback to the user.");
status.setVisible(false);
System.out.println("Displays a file-save dialog to save the results.");
System.out.println("And finally, displays a \"End of operations\" dialog before ending.");
}
}
UPDATE 1:
The main difficulty I'm having is to implement something like this (this is what I want to do):
//Main method...code...
Thread srt = new Thread(status = new Waiter());
//Before "srt.start();"...
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
main.continueExecution();
}
});
//Thread's run() being something like "status.setVisible(true); main.waitGUI();"
srt.start();
//continues here after the Listener is triggered...more code...
Instead of this (what is being the solution to most other people, if I'm understanding it right...) (this is what I don't want to do, if possible):
//GUI before this one...
//code...
Thread srt = new Thread(status = new Waiter());
status.startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
/*
* Code that should come after this GUI.
*/
}
});
//Thread's run() being something like "status.setVisible(true);"
srt.start();
//"ends" here...(Initial code or GUI before this "status")
In other words, I'm having trouble implementing the GUIs and Listeners in a way to trigger main's thread's "sleep" and "wake up" actions, instead of triggering actual processing code.
UPDATE 2:
Following #JB_Nizet 's tip on SwingUtilities.invokeLater(), I took a good look at the SwingUtilities docs, and after I found out about how the SwingUtilities.invokeAndWait() method works, and I think I've found how to do it, using a combination of Semaphore and invokeAndWait().
I need someone with a better understanding of multi-threading and/or GUIs to confirm if it's a safe, valid solution or not. (I'll then edit the question and clean it up, and if confirmed, post this in proper "answer format")
Anyways, here goes the modified code, which seems to be working for me:
public class Main_Test {
//Semaphore:
public static Semaphore semaphore;
//GUIs:
private static Waiter auth; //Represents my NTLM-authentication form.
public static void main(String[] args) {
try {
semaphore = new Semaphore(1);
// semaphore.acquire();
auth = new Waiter() {
#Override
public void run() {
try {
System.out.println(Main_Test.getThread() + this.getName() + " has been created and is now running.");
semaphore.acquire(); //Makes main pause.
this.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
auth.jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(getThread() + "NTLM has been hypothetically authenticated.");
semaphore.release(); //Makes main continue after GUI is done.
auth.dispose();
}
});
// semaphore.release();
SwingUtilities.invokeAndWait(auth);
semaphore.acquire(); //<- Where the main effectively gets paused until the permit is released.
/*
* GUI's run() will accquire the semaphore's permit.
* The invokeAndWait() garantees (?) it will happen before main's acquire().
* This causes the main to pause when trying to acquire the permit.
* It stays paused until the actionListener release() that permit.
*/
System.out.println(getThread() + "This message represents the processing, and should come only after the hypothetical NTLM authentication.");
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String getThread() {
return String.format("%-32s --- ", Thread.currentThread().toString());
}
}
I'm not sure I have completely understood what you want to do, but it seems to me that you have a consumer thread (the main thread, waiting for events from the event dispatch thread), and a producer thread (the event dispatch thread).
The typical way to implement this is to use a blocking queue as a communication mechanism:
Create a blocking queue
Create your GUI and pass it the blocking queue
start a loop which gets data from the queue. Since the queue is blocking, the main thread will be blocked untile there is something in the queue
Have your event listeners, running in the EDT, post data to the blocking queue

Starting and Stopping Threads with Buttons

I have what I thought was a simple problem, which I have yet to find a good solution to: I would like to be able to pause and unpause the activity taking place in a thread, by hitting a button on a Swing interface panel.
Specifically, I would like to use one thread to take in audio frames in real time; a second thread to perform magic processing on those frames; and a third thread to serialize the results and send over a socket somewhere else. The kicker is that depending on the brand of magic we employ, the processing in the second thread might take longer per frame to perform than the actual data collection, and the data might pile up after a while.
As a very crude prototype workaround we thought we'd add a GUI with a button to turn the audio collection process on and off and a status bar (to be implemented later) so that a user could sort of keep an eye on how full the buffer (a linked blocking queue) happened to be.
This is harder than I anticipated. I've stripped the problem down to a toy version: A linked blocking queue that can store 50 Integers, a GUI, two threads (adding to and removing from the queue at different rates) and a Token object wrapped around a boolean. It looks like this, and it sorta works:
Test.java
public class Test {
public static void main(String[] args) throws IOException {
Token t1 = new Token();
Token t2 = new Token();
LinkedBlockingQueue<Integer> lbq = new LinkedBlockingQueue<Integer>(50);
startFill sf = new startFill(t1, lbq);
startEmpty se = new startEmpty(t2, lbq);
TestUI testUI = new TestUI(t1, t2, lbq);
testUI.setVisible(true);
sf.start();
se.start();
}
}
TestUI.java
public class TestUI extends JFrame implements ActionListener {
private JToggleButton fillStatus, emptyStatus;
public boolean filling, emptying;
public Token t1, t2;
public LinkedBlockingQueue<Integer> lbq;
public TestUI(Token t1, Token t2, LinkedBlockingQueue<Integer> lbq) {
this.t1 = t1;
this.t2 = t2;
this.lbq = lbq;
initUI();
}
public synchronized void initUI() {
JPanel panel = new JPanel();
panel.setLayout(null);
filling = false;
fillStatus = new JToggleButton("Not Filling");
fillStatus.setBounds(20, 20, 150, 25);
fillStatus.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (filling == false) {
fillStatus.setText("Filling");
} else {
fillStatus.setText("Not Filling");
}
filling = !filling;
t1.flip();
System.out.println("fill button press");
}
});
// Similar code for actionListener on Empty button, omitted
panel.add(fillStatus);
panel.add(emptyStatus);
add(panel);
setTitle("Test interface");
setSize(420, 300);
setLocationByPlatform(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
}
}
startFill.java
public class startFill extends Thread {
public Token token;
public LinkedBlockingQueue<Integer> lbq;
public startFill(Token token, LinkedBlockingQueue<Integer> lbq) {
this.token = token;
this.lbq = lbq;
}
public void run() {
int count = 0;
while (true) {
while (!token.running()) {
try {
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (token.running()) {
try {
lbq.put(count);
System.out.println("queue size = " + lbq.size());
count++;
sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}
}
There is also a startEmpty.java that works about the same way, and a Token.java that's a wrapper for a boolean state variable, omitted for merciful brevity.
So that works, but at the expense of polling in the while (!token.running()) loop.
I tried using Locks and Conditions, but failed, always getting IllegalMonitorStateExceptions.
And I looked at this similar question and managed to get that working, but at the expense of using the yield() method which apparently differs significantly between Java 5 and Java 6, and seems to be highly discouraged.
So my question: Is there some correct, or significantly better way to do what I am trying to do? It seems like there should be a way to make this happen without the polling and with reliable methods.
Update: I'm not sure I can get around the issue of controlling the audio capture loop in some way for the application. Whether it is a human pressing a button, or internal logic making decisions based on some other factors, we really need to be able to shut the darn thing down and bring it back to life on command.
Instead of handling the synchronisation between your 3 worker processes by hand via a GUI, you could also setup a factory lineup between the workers:
add 2 queues between your workers
block your threads on queue-state conditions;
readers (consumers) block on empty queue
writers (producers) block when the queue is full (say 2n messages where n is the number of consumers for that queue.)
wait() on a queue to block your thread and notifyAll() on that queue after adding or removing a message from a queue.
A setup like this automatically slows down producers running faster than their consumers.
Why dont you implement ArrayBlockingQueue.
Its Better use ArrayBlockingQueue class which is present in java.util.concurrent package, which is Thread Safe.
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(100);
Here is one way to do what I was trying to do: Properly use wait() and notify(), synchronized on the Token objects, like such:
startFill.java run() method
public synchronized void run() {
int count = 0;
try {
// token initializes false
// wait until notification on button press
synchronized (token) {
token.wait();
}
// outer loop
while (true) {
// inner loop runs as long as token value is true
// will change to false on button press
while (token.running()) {
lbq.put(count);
System.out.println("queue size = " + lbq.size());
count++;
sleep(100);
}
// wait until notification on button press, again
synchronized (token) {
token.wait();
}
}
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}
TestUI.java ActionListener:
fillStatus.addActionListener(new ActionListener() {
// t1 was initialized false
public void actionPerformed(ActionEvent event) {
if (filling == false) {
fillStatus.setText("Filling");
// if false, change t1 status to true
t1.flip();
// and send the notification to the startFill thread that it has changed
synchronized (t1) {
t1.notify();
}
} else {
fillStatus.setText("Not Filling");
// if true, change t1 status to false
t1.flip();
// no notification required due to polling nature of startFill's active thread
}
filling = !filling;
System.out.println("fill button press");
}
});
This works rather nicely, without polling while the thread is turned off.
My initial attempts at this failed due to bad syntax-- I neglected the synchronized (token) {...} context block around the wait() and notify() statements.

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

Categories

Resources