I am learning about multithreading right now.
I have a "main" class in which I build a JFrame with a Textfield and a Start and a Stop Button in it.
I also have another class/Thread from which I want to print the current time into my Textfield when I click the Start Button of my Frame. Everything works but the text doesn't change in my Textfield as I start the Thread even though Eclipse says my code is alright.
What am I doing wrong?
Class 1:
public class Uhr extends JFrame {
private JPanel contentPane;
public JTextPane tpZeit;
Thread t;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Uhr frame = new Uhr();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Uhr() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
tpZeit = new JTextPane();
tpZeit.setText("test");
tpZeit.setBounds(43, 50, 212, 43);
contentPane.add(tpZeit);
JButton btnstart = new JButton("GO");
btnstart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tpZeit.setText("started");
if (t == null) {
t = new Thread(new Uhrsteuerung());
}
if(!t.isAlive()) {
t = new Thread(new Uhrsteuerung());
t.start();
}
}
});
btnstart.setBounds(10, 227, 89, 23);
contentPane.add(btnstart);
JButton btnstop = new JButton("Stop");
btnstop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
t.interrupt();
}
});
btnstop.setBounds(248, 227, 89, 23);
contentPane.add(btnstop);
}
}
Class with additional Thread:
public class Uhrsteuerung extends Uhr implements Runnable {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String time;
boolean x ;
#Override
public synchronized void run() {
// TODO Auto-generated method stub
x = true;
while (x) {
try {
time = sdf.format(System.currentTimeMillis());
System.out.println(time);
tpZeit.setText(time);
repaint();
Thread.sleep(1000);
} catch (InterruptedException ex) {
x = false;
}
}
}
}
All code for Java's Swing classes must be executed on the Event Dispatch Thread (EDT). Swing provides some special classes for helping you do this.
To execute code off the EDT, use a SwingWorker class. SwingWorker will execute a task in the background (not using the EDT) and then return a result properly synchronized that executes on the EDT. https://docs.oracle.com/en/java/javase/16/docs/api/java.desktop/javax/swing/SwingWorker.html
Its application is pretty simple. Define your own class that extends SwingWorker.
class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> {
// ...
If you need to update a component, it's good to pass in the component so that you can refer to it later.
class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> {
private JTextArea textArea;
public PrimeNumbersTask( JTextArea textArea ) {
this.textArea = textArea;
}
// ...
Do your work in the method doInBackground() by overriding it. Don't touch the Swing classes in this method. Use the publish() method to send smaller chunks of data to the EDT if you want to provide progressive results instead of waiting for the whole task to complete.
#Override
public List<Integer> doInBackground() {
while (!done && ! isCancelled()) {
// do stuff
publish(number);
setProgress(100 * numbers.size() / numbersToFind);
}
}
return numbers;
}
Finally override the method process() to update your Swing classes.
#Override
protected void process(List<Integer> chunks) {
for (int number : chunks) {
textArea.append(number + "\n");
}
}
From the EDT, you can call execute() to start your background task.
JTextArea textArea = new JTextArea();
// manipulate and set up GUI...
PrimeNumbersTask task = new PrimeNumbersTask(textArea);
task.execute();
There's a lot more docs on SwingWorker if you Google for it, that's the basics of how to use it.
I've downloaded a small Java project from oracle website to create a progress bar.
I understand it, but I need to apply it in a different way, the application is creating a thread in the background so the progress bar can be updated accordingly (doInBackground()).
My question is, how can I replace this kind of process in the background in this application by a method from my application (method is just doing a kind of batch processing on a database), can someone help please?
Here is code by Oracle:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.util.Random;
public class ProgressBarDemo2 extends JPanel
implements ActionListener, PropertyChangeListener {
private JProgressBar progressBar;
private JButton startButton;
private JTextArea taskOutput;
private Task task;
class Task extends SwingWorker<Void, Void> {
/*
* Main task. Executed in background thread.
*/
#Override
public Void doInBackground() {
Random random = new Random();
int progress = 0;
//Initialize progress property.
setProgress(0);
//Sleep for at least one second to simulate "startup".
try {
Thread.sleep(1000 + random.nextInt(2000));
} catch (InterruptedException ignore) {}
while (progress < 100) {
//Sleep for up to one second.
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException ignore) {}
//Make random progress.
progress += random.nextInt(10);
setProgress(Math.min(progress, 100));
}
return null;
}
/*
* Executed in event dispatch thread
*/
public void done() {
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
taskOutput.append("Done!\n");
}
}
public ProgressBarDemo2() {
super(new BorderLayout());
//Create the demo's UI.
startButton = new JButton("Start");
startButton.setActionCommand("start");
startButton.addActionListener(this);
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
//Call setStringPainted now so that the progress bar height
//stays the same whether or not the string is shown.
progressBar.setStringPainted(true);
taskOutput = new JTextArea(5, 20);
taskOutput.setMargin(new Insets(5,5,5,5));
taskOutput.setEditable(false);
JPanel panel = new JPanel();
panel.add(startButton);
panel.add(progressBar);
add(panel, BorderLayout.PAGE_START);
add(new JScrollPane(taskOutput), BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
/**
* Invoked when the user presses the start button.
*/
public void actionPerformed(ActionEvent evt) {
progressBar.setIndeterminate(true);
startButton.setEnabled(false);
//Instances of javax.swing.SwingWorker are not reusuable, so
//we create new instances as needed.
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
/**
* Invoked when task's progress property changes.
*/
public void propertyChange(PropertyChangeEvent evt) {
if ("progress" == evt.getPropertyName()) {
int progress = (Integer) evt.getNewValue();
progressBar.setIndeterminate(false);
progressBar.setValue(progress);
taskOutput.append(String.format(
"Completed %d%% of task.\n", progress));
}
}
/**
* Create the GUI and show it. As with all GUI code, this must run
* on the event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("ProgressBarDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new ProgressBarDemo2();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
First, I'd recommend by defining a concept of a "progressable" state
public interface Progressable {
public void setProgress(int progress); // I prefer double, but we'll keep it inline with the rest of the API
}
Then, the entry point for your tasks would take a reference to Progressable
public class MySuperAwesomeLongRunningTask ... {
//...
private Progressable progressable;
public void performTask(Progressable progressable) {
this.prgressable = progressable
}
protected void methodThatDoesSomeWork() {
// Do some super duper work...
// calculate the progress of that work some how,
// based on your implementation...
int progress = ...;
progressable.setProgress(progress);
}
}
Then, create a SwingWorker which implements Progressable and calls your work...
class Task extends SwingWorker<Void, Void> implements Progressable {
private MySuperAwesomeLongRunningTask taskToBeDone;
public Task(MySuperAwesomeLongRunningTask taskToBeDone) {
self.taskToBeDone = taskToBeDone;
}
/*
* Main task. Executed in background thread.
*/
#Override
public Void doInBackground() {
taskToBeDone.performTask(this);
return null;
}
/*
* Executed in event dispatch thread
*/
public void done() {
// What ever you need to do...
}
}
Now, because SwingWorker already has a method called setProgress(int) it automatically conforms to Progressable (so long as you implement it), so when MySuperAwesomeLongRunningTask calls setProgress, it will actually be calling the SwingWorkers implementation.
This means, that the rest of the code basically remains the same, expect, I'd change
if ("progress" == evt.getPropertyName()) {
to
if ("progress".equals(evt.getPropertyName())) {
because comparing Strings with == is bad idea (and freaks me out :P)
I have created a progress bar in my project that loads whilst words are being created in a database. Now this works fine and here is some code (Note: Not all code is shown as it is confidential)
Structure of form:
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
Welcome frame = new Welcome();
frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
//**************************************************
// CREATE THE FORM *
//**************************************************
public CreateWindow()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 163);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
//Progress Bar
JProgressBar progress = new JProgressBar();
progress.setBounds(5, 41, 424, 17);
progress.setStringPainted(true);
contentPane.setLayout(null);
//Label above the Progress Bar
JLabel label = new JLabel("Loop progress is: ");
label.setBounds(5, 5, 424, 14);
contentPane.add(label);
contentPane.add(progress);
setContentPane(contentPane);
//Will be used for updating the progress bar
ProgressWorker worker = new ProgressWorker(progress);
The Progress bar:
private static class ProgressWorker extends SwingWorker<Void, Integer>
{ //Swing worker class for updating the progress bar
private final JProgressBar progress; //declaration for progress bar
public ProgressWorker(JProgressBar progress)
{
this.progress = progress;
}
#Override
protected Void doInBackground() throws Exception
{
//Task performed here
}
System.out.println("i = " + recordLoop);
final int progr = ((int) ((100L * (recordLoop - firstRecord)) / (lastRecord-firstRecord)));
publish(progr);
}
return null;
}
#Override
//This is the process of how the progress bar will load
protected void process(List<Integer> chunks)
{
progress.setValue(chunks.get(chunks.size() - 1));
super.process(chunks);
}
#Override
protected void done()
{
progress.setValue(100); //This is the value when process is complete
}
}
Now this is well and good as it works as intended but now I want another task to automatically run after the progress bar reached 100%. This is where the problem occurs. Here is what I have tried:
protected void done()
{
progress.setValue(100); //This is the value when process is complete
if (int progress == 100) {
//Do task here
}
}
}
}
This is not the only way I've tried. I have also tried adding the code under a new signature method and I have also tried putting the if statement in the doInBackground() method. I am new to progress bars so sorry for any silly mistakes. Is there a way to be able to do this?
If my idea is incorrect I have also tried this without an if statement by running the task after the first task but the problem here is that I have a for loop in the task performed in the doInBackground() method and I don't want this second task to be included in the for loop.
I am creating a simple user interface whereby a user could click on a button to run a specific Java class. Upon clicking, the progress of the task should be displayed to the user and also provide a Cancel button for the user to terminate the task at any point of time while the task is running.
In this case, I am using a ProgressMonitor to be displayed when a user clicks on a JButton in the UI, whereby runEngineerBuild() containing a runnable thread will be invoked to execute the methods of another Java class (called EngineerBuild.java). However, the ProgressMonitor dialog does not display. How can I get the ProgressDialog to show? I'm wondering if it is because of the nature of multiple running threads or maybe I'm missing out on something. Would really appreciate your help!
In SecondPanel.java:
package mainApplication;
import java.awt.Font;
public class SecondPanel extends JPanel {
private MainApplication ma = null; // main JFrame
private JPanel pnlBtn;
private JPanel pnlProgress;
private JButton btnRunAll;
private JButton btnEngBuild;
private JButton btnWholeDoc;
private JButton btnCancelProgress;
private JLabel lblTitleSteps;
private JLabel lblAlt;
private JLabel lbl_1a;
private JLabel lbl_1b_c;
private JLabel lblTitleStatus;
private JProgressBar progressRunAll;
private JProgressBar progressEngBuild;
private JProgressBar progressWholeDoc;
private Property property = Property.getInstance();
// private Task task;
private boolean cancelFlag;
/**
* Create the panel for Step 1 TabbedPane.
*/
public SecondPanel(MainApplication mainApp) {
// TODO Auto-generated constructor stub
super();
ma = mainApp;
}
public SecondPanel() {
this.setBackground(new Color(224, 255, 255));
this.setBounds(0, 0, 745, 1350);
this.setPreferredSize(new Dimension(745, 600));
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
pnlBtn = new JPanel();
pnlBtn.setLayout(new BoxLayout(pnlBtn, BoxLayout.PAGE_AXIS));
pnlBtn.setAlignmentY(Component.TOP_ALIGNMENT);
pnlProgress = new JPanel();
pnlProgress.setLayout(new BoxLayout(pnlProgress, BoxLayout.PAGE_AXIS));
pnlProgress.setAlignmentY(TOP_ALIGNMENT);
pnlBtn.add(Box.createRigidArea(new Dimension(0, 15)));
btnEngBuild = new JButton("Run EngineerBuild.java");
btnEngBuild.setToolTipText("Build search engineer");
btnEngBuild.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// start activity
activity = new SimulatedActivity(1000);
activity.start();
// launch progress dialog
progressDialog = new ProgressMonitor(ma,
"Waiting for Simulated Activity", null, 0, activity
.getTarget());
progressDialog.setMillisToPopup(1000);
// start timer
activityMonitor = new Timer(500, null);
activityMonitor.start();
btnEngBuild.setEnabled(false);
}
});
activityMonitor = new Timer(500, new ActionListener() {
private PrintStream textArea;
public void actionPerformed(ActionEvent event) {
int current = activity.getCurrent();
// show progress
runEngineerBuild();
textArea.append(current + "\n");
progressDialog.setProgress(current);
// check if task is completed or canceled
if (current == activity.getTarget() || progressDialog.isCanceled()) {
activityMonitor.stop();
progressDialog.close();
activity.interrupt();
btnEngBuild.setEnabled(true);
}
}
});
btnEngBuild.setMinimumSize(new Dimension(200, 30));
btnEngBuild.setPreferredSize(new Dimension(200, 30));
btnEngBuild.setMaximumSize(new Dimension(200, 30));
pnlBtn.add(btnEngBuild);
pnlBtn.add(Box.createRigidArea(new Dimension(0, 15)));
// components in panel progress
lblTitleStatus = new JLabel();
lblTitleStatus.setText("<html><u>Task Status</u></html>");
progressEngBuild = new JProgressBar();
Border border2 = BorderFactory.createTitledBorder("Run EngineerBuild");
progressEngBuild.setBorder(border2);
// title
pnlProgress.add(lblTitleStatus);
pnlProgress.add(Box.createRigidArea(new Dimension(0, 15)));
pnlProgress.add(progressEngBuild);
pnlProgress.add(Box.createRigidArea(new Dimension(0, 15)));
this.add(Box.createRigidArea(new Dimension(15, 10)));
this.add(pnlBtn);
this.add(Box.createRigidArea(new Dimension(50, 10)));
this.add(pnlProgress);
}
public void runEngineerBuild()
{
EngineerBuildRunnable ebr = new EngineerBuildRunnable();
ebr.run();
}
private class EngineerBuildRunnable implements Runnable {
EngineerBuild eb;
public EngineerBuildRunnable() {
eb = new EngineerBuild();
}
public void run() {
eb.initial();
eb.storeIntoFile();
}
}
private Timer activityMonitor;
private ProgressMonitor progressDialog;
private SimulatedActivity activity;
public static final int WIDTH = 300;
public static final int HEIGHT = 200;
}
/**
* A simulated activity thread.
*/
class SimulatedActivity extends Thread {
/**
* Constructs the simulated activity thread object. The thread increments a
* counter from 0 to a given target.
*
* #param t
* the target value of the counter.
*/
public SimulatedActivity(int t) {
current = 0;
target = t;
}
public int getTarget() {
return target;
}
public int getCurrent() {
return current;
}
public void run() {
try {
while (current < target && !interrupted()) {
sleep(100);
current++;
}
} catch (InterruptedException e) {
}
}
private int current;
private int target;
}
Here's the link for the original ProgressMonitor code if you're interested:
User Interface Programming - Example 1-11 ProgressMonitorTest.java
In all likelihood, calling runEngineerBuild() will call long-running code, something that you're doing on the Swing event thread, and this will tie up the thread rendering your GUI useless and frozen until that long-running code has completed its run. The solution is the same as all similar issues -- call runEngineerBuild() in a background thread such as a SwingWorker.
A quick fix would be to explicitly call runEngineerBuild() in a simple thread:
EngineerBuildRunnable ebr = new EngineerBuildRunnable();
new Thread(ebr).start();
// ebr.run(); // !!! don't call a Runnable's run method directly !!!!
For details on how to use a SwingWorker, please check out: Lesson: Concurrency in Swing.
I have my main GUI thread which has a JprogressBar in it and is implementing ProprtyChangeListener.
When a button is pressed, a different class, which extends SwingWorker, kicks into action and performs a series of potentially long calculations.
I need the progress bar in class A to present the progress according to a variable in Class B.
My code is below (could be a bit messy with all my failed tries...)
Would appreciate any help.
GUI CLASS:
SignalSimulator signalSimulator = new SignalSimulator(
path, numOfdataPoints, numOfLocalSpikes,
numOfExpSpikes, noiseAmp, slope, offset,
rdbtnSineWave.isSelected());
signalSimulator.addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
String property = evt.getPropertyName();
if ("progress".equals(property)) {
int progress = (Integer) evt.getNewValue();
System.out.println("value in PropertChangeListener is: " + progress);
progressBar.setValue(progress);
}
}
});
signalSimulator.execute();
Calculating class:
protected Integer doInBackground() throws Exception {
if (isSine){
data = generateSineWave(numOfDataPoints, noiseAmp, offset);
data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
} else {
data = generateLinearSignal(numOfDataPoints, noiseAmp, slope, offset);
data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
}
writeLogFile(path, ".txt", data);
firePropertyChange("progress", 1, 1);
setProgress((int)progress);
publish(progress);
System.out.println("value in doInBackground is: " + progress);
return 1;
}
EDIT
Original problem remains. For some reason the progress bar is still not updating,
I know for sure that the "progress" variable in progressBar.setValue(progress) is updating yet the progress bar in the GUI remains unchanged (fixed at 0)
here is my new code:
GUI Class:
SignalSimulator signalSimulator = new SignalSimulator(path, numOfdataPoints, numOfLocalSpikes, numOfExpSpikes, noiseAmp, slope, offset, rdbtnSineWave.isSelected());
signalSimulator.addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
String property = evt.getPropertyName();
if ("progress".equals(property)) {
int progress = (Integer) evt.getNewValue();
System.out.println("value in PropertChangeListener is: " + progress);
progressBar.setValue(progress);
}
}
});
signalSimulator.execute();
SwingWorker Class:
#Override
protected Integer doInBackground() throws Exception {
if (isSine){
data = generateSineWave(numOfDataPoints, noiseAmp, offset);
data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
}
else{
data = generateLinearSignal(numOfDataPoints, noiseAmp, slope, offset);
data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
}
writeLogFile(path, ".txt", data);
return 1;}
public double[] generateSineWave(int numOfDataPoints, double noiseAmp, double offset){
Random rnd = new Random();
double[] dataArray = new double[numOfDataPoints];
for (int i=0;i<numOfDataPoints;i++){
dataArray[i] = Math.sin(Math.toRadians(i))+rnd.nextDouble()*noiseAmp+offset;
progress = ((double)i)/(double)numOfDataPoints*100;
//firePropertyChange("progress", 1, 1);
setProgress((int)progress);
//publish(progress);
System.out.println("value in doInBackground is: " + progress);
}
return dataArray;
EDIT
Rewrote the whole thing without the extra (irrelevant) code. I guess I'm missing something basic here caus it still doesn't update the progress bar.
public class ProgressBarTest implements PropertyChangeListener {
private JFrame frame;
private JButton btnRun;
static JProgressBar progressBar = new JProgressBar(0,100);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
ProgressBarTest window = new ProgressBarTest();
window.frame.setVisible(true);
//SignalSimulator signalSimulator = new SignalSimulator();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ProgressBarTest() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setResizable(false);
JProgressBar progressBar = new JProgressBar();
progressBar.setAlignmentX(Component.RIGHT_ALIGNMENT);
progressBar.setBounds(0, 252, 444, 20);
progressBar.setStringPainted(true);
frame.getContentPane().add(progressBar);
JButton btnRun = new JButton("Start Long Run");
btnRun.setBounds(167, 214, 159, 31);
frame.getContentPane().add(btnRun);
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
longRun();
}
} );
}
private void longRun(){
LongRunner longRunner = new LongRunner(100000);
longRunner.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())){
int progress = (int) evt.getNewValue();
System.out.println("Value in propertyChangeListener: "+progress);
progressBar.setValue(progress);
}
}
});
longRunner.execute();
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
}
And the SwingWorker:
import javax.swing.SwingWorker;
public class LongRunner extends SwingWorker<Integer, Double>{
int numOfPoints;
double progress;
public LongRunner(int numOfPoints) {
this.numOfPoints = numOfPoints;
this.progress = 0;
}
private void runLong(int bigNum){
for (int i=0; i< bigNum; i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
progress = (((double)i*100)/(double)bigNum);
setProgress((int)progress);
System.out.println("Value in setProgress: "+progress);
}
}
#Override
protected Integer doInBackground() throws Exception {
runLong(numOfPoints);
return null;
}
}
What am I doing wrong here?
You are calling doInBackground() directly from your code, something that is akin to calling run() directly in a runnable. This means that your code is not in fact running on a background thread, and so you are likely clobbering the event thread with long running code, preventing the Swing GUI, and your progress bar, from updating.
Solution: don't do this. Call execute() on your worker when you want it to run.
If you need further help, you're going to first have to help us. You understand that you have a ton of code posted, most of it completely unrelated to your problem at hand, and certainly more than you should ask volunteers to go through. Please get rid of all the extraneous unrelated code, and instead create and post a proper MCVE.
Edit
You also appear to be calling code directly from the EDT that should be left to your worker thread here:
signalSimulator.execute();
// ************* all these signalSimulator calls below ***********
if (rdbtnSineWave.isSelected()) {
data = signalSimulator.generateSineWave(numOfdataPoints,
noiseAmp, offset);
data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
numOfExpSpikes);
} else { // Linear signal is selected
data = signalSimulator.generateLinearSignal(numOfdataPoints,
noiseAmp, slope, offset);
data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
numOfExpSpikes);
}
signalSimulator.writeLogFile(path, ".txt", data);
You also appear to be creating only one worker object which is not proper since you can't re-use a SwingWorker object.
I suggest that you only create your SwingWorker object when it is needed, that you pass the information for what type of signal is needed into its constructor. This way the above methods can be called from the SwingWorker's doInBackground method where they belong.
e.g.,
signalSimulator = SignalSimulator(rdbtnSineWave.isSelected())
signalSimulator.addPropertyChangeListener(...);
signalSimulator.execute();
Note that you have some other significant unrelated problems in the code you've posted, but they will have to be addressed at some other time, but they include use of null layout and setBounds, almost always a very bad idea.
Edit
Just to clarify once again, your main problem is that you're calling long-running code on the Swing event thread. Just because a method is located in your Worker class does not mean that calling it will automatically have it run on a background thread. The only way to guarantee this is to have the code called from within your doInBackground() method. Again, what you want to do is to create your new worker object when it is needed, for instance, inside of some ActionListener, and at the time of its creation, pass into it all the information that it will need to run. Then add your PropertyChangeListener, then .execute() your worker. Do this, and I'll bet your code will work much better.
Edit
For example
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class ProgressExampleGui {
private JPanel mainPanel = new JPanel();
private JProgressBar progressBar = new JProgressBar();
private JButton pressMeBtn = new JButton(new MyAction("Press Me", KeyEvent.VK_P, this));
public ProgressExampleGui() {
progressBar.setStringPainted(true);
progressBar.setString("");
mainPanel.add(pressMeBtn);
mainPanel.add(progressBar);
}
public void setProgress(int progress) {
progressBar.setValue(progress);
progressBar.setString(progress + "%");
}
public JComponent getMainComponent() {
return mainPanel;
}
public void setEnabled(boolean enabled) {
pressMeBtn.setEnabled(enabled);
}
private static void createAndShowGui() {
ProgressExampleGui progExampleGui = new ProgressExampleGui();
JFrame frame = new JFrame("Progress Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(progExampleGui.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyAction extends AbstractAction {
private ProgressExampleGui gui;
public MyAction(String name, int mnemonic, ProgressExampleGui gui) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
this.gui = gui;
}
#Override
public void actionPerformed(ActionEvent e) {
AbstractButton source = (AbstractButton) e.getSource();
gui.setProgress(0);
source.setEnabled(false);
MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerPropChngListener(gui));
myWorker.execute();
}
}
class WorkerPropChngListener implements PropertyChangeListener {
private ProgressExampleGui gui;
public WorkerPropChngListener(ProgressExampleGui gui) {
this.gui = gui;
}
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
MyWorker myWorker = (MyWorker) pcEvt.getSource();
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = ((Integer)pcEvt.getNewValue()).intValue();
gui.setProgress(progress);
}
if (SwingWorker.StateValue.DONE.equals(pcEvt.getNewValue())) {
try {
myWorker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
gui.setEnabled(true);
}
}
}
class MyWorker extends SwingWorker<Void, Void> {
private static final int MAX_INCR = 8;
private static final long SLEEP_TIME = 200;
private static final int MAX_VALUE = 100;
private int value = 0;
private Random random = new Random();
#Override
protected Void doInBackground() throws Exception {
while (value < MAX_VALUE) {
value += random.nextInt(MAX_INCR);
value = Math.min(value, MAX_VALUE);
Thread.sleep(SLEEP_TIME);
setProgress(value);
}
return null;
}
}
Edit
Regarding your new code, you've got two main problems:
Look at the results of your data output:
Value in setProgress: 0.0
Value in setProgress: 0.001
Value in setProgress: 0.002
Value in setProgress: 0.003
Value in setProgress: 0.004
Value in setProgress: 0.005
Value in setProgress: 0.006
Value in setProgress: 0.007
Value in setProgress: 0.008
Value in setProgress: 0.009
Value in setProgress: 0.01
Value in setProgress: 0.011
Value in setProgress: 0.012
Value in setProgress: 0.013
Value in setProgress: 0.014
Value in setProgress: 0.015
Value in setProgress: 0.016
Value in setProgress: 0.017
Value in setProgress: 0.018
Value in setProgress: 0.019
Value in setProgress: 0.02
Value in setProgress: 0.021
Value in setProgress: 0.022
Value in setProgress: 0.023
Value in setProgress: 0.024
Value in setProgress: 0.025
Value in setProgress: 0.026
Value in setProgress: 0.027
Value in setProgress: 0.028
Value in setProgress: 0.029
At the pace that this is going, your progress value will reach 1 and cause a visible change to the PropertyChangeListener and the JProgressBar when the next ice age is upon us. So first of all, change your sleep times, and change your big number to something more reasonable.
Next, you shadow important variables, notably your JProgressBar variable, progressBar. Here is where you declare it and initialize it in the class:
public class ProgressBarTest implements PropertyChangeListener {
private JFrame frame;
private JButton btnRun;
static JProgressBar progressBar = new JProgressBar(0, 100);
As a side note, this variable should most definitely not be declared static, but that's not the cause of your current problem. The cause is that you in fact re-declare the same variable elsewhere in your initialize method, and then add this new object into your GUI:
private void initialize() {
frame = new JFrame();
// .....
JProgressBar progressBar = new JProgressBar();
// .....
frame.getContentPane().add(progressBar);
Please understand that this new progressBar variable references a completely different JProgressBar, and so if you advance the value of the object created in the class, your GUI will show nothing because it is displaying a completely different object. To solve this, **don't redeclare and initialize a new variable in the initialize method. Instead use the object created in the class.
Other problems with your code: you use null layout and setBounds a lot. This will show to all that you are a newbie Swing programmer, since it means that you like to create rigid programs that are extremely difficult to upgrade, and that may not look good on all systems. Instead use the layout managers. For instance, here's your code with a few changes, all noted by comments:
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
//!! no need to implement PropertyChangeListener
//!! public class ProgressBarTest implements PropertyChangeListener {
public class ProgressBarTest2 {
private JFrame frame;
private JButton btnRun;
// !! this shouldn't be static!
// !! static JProgressBar progressBar = new JProgressBar(0, 100);
private JProgressBar progressBar = new JProgressBar(0, 100); // !!
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager
.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
ProgressBarTest2 window = new ProgressBarTest2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ProgressBarTest2() {
initialize();
}
private void initialize() {
frame = new JFrame();
// !!frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//!! frame.getContentPane().setLayout(null); //!! never use null layouts
frame.setResizable(false);
// !! don't create a shadowed variable
// !! JProgressBar progressBar = new JProgressBar();
progressBar.setAlignmentX(Component.RIGHT_ALIGNMENT);
//!! progressBar.setBounds(0, 252, 444, 20);
progressBar.setStringPainted(true);
//!! frame.getContentPane().add(progressBar);
frame.getContentPane().add(progressBar, BorderLayout.SOUTH);
btnRun = new JButton("Start Long Run"); //!! no shadowing
//!! btnRun.setBounds(167, 214, 159, 31);
JPanel panel = new JPanel(); //!!
panel.setPreferredSize(new Dimension(450, 300)); //!!
panel.setLayout(new GridBagLayout()); //!!
panel.add(btnRun); //!!
frame.getContentPane().add(panel, BorderLayout.CENTER); //!!
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
longRun();
}
});
//!!
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void longRun() {
//!! use a more realistic value, one that should show change in listener
//!! LongRunner longRunner = new LongRunner(100000);
LongRunner2 longRunner = new LongRunner2(10000);
longRunner.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (int) evt.getNewValue();
System.out.println("Value in propertyChangeListener: "
+ progress);
progressBar.setValue(progress);
}
}
});
longRunner.execute();
}
// !! #Override // !! not needed
// public void propertyChange(PropertyChangeEvent evt) {
// }
}
class LongRunner2 extends SwingWorker<Integer, Double> {
private static final long SLEEP_TIME = 15; // !!
int numOfPoints;
double progress;
public LongRunner2(int numOfPoints) {
this.numOfPoints = numOfPoints;
this.progress = 0;
}
private void runLong(int bigNum) {
for (int i = 0; i < bigNum; i++) {
try {
// !! quicker turn-over so that our bigNum can change
// in a realistic way
// !! Thread.sleep(100);
Thread.sleep(SLEEP_TIME);// !!
} catch (InterruptedException e) {
e.printStackTrace();
}
progress = (((double) i * 100) / (double) bigNum);
setProgress((int) progress);
// !! System.out.println("Value in setProgress: " + progress); //!! This will slow us down
}
}
#Override
protected Integer doInBackground() throws Exception {
runLong(numOfPoints);
return null;
}
}