This question already has answers here:
Swing Progress Bar updates via Worker to EventDispatch thread
(2 answers)
Closed 7 years ago.
All the coding runs, my only problem is that the progress bar doesn't display the value changes. I set the values, but if I run it the value set, doesn't display on the progress bar. (The bar doesn't fill up)
public void logIn() {
try {
while (true) {
calculate();
System.out.println(loadingProgress);
if (loadingProgress < 100) {
mGUI.pbLoading.setValue(loadingProgress);
Thread.sleep(random.nextInt(5000) + 100);
} else {
mGUI.pbLoading.setValue(100);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void calculate() {
random = new Random();
loadingProgress = loadingProgress + random.nextInt(9) + 1;
}
The calculation has to be done in an extra thread. This is because the currently running thread is blocking the repaint of the UI until the calculation is done.
The problem is outside your posted code. I used your code to create a working example:
package snippet;
import java.util.Random;
public class Snippet {
public class ValueAccepter {
public void setValue(int loadingProgress) {
System.out.println(loadingProgress);
}
}
public class MGUI {
public final ValueAccepter pbLoading = new ValueAccepter();
}
private int loadingProgress;
private Random random;
private MGUI mGUI = new MGUI();
public static void main(String[] args) {
new Snippet().logIn();
}
public void logIn() {
try {
while (true) {
calculate();
System.out.println(loadingProgress);
if (loadingProgress < 100) {
mGUI.pbLoading.setValue(loadingProgress);
Thread.sleep(random.nextInt(5000) + 100);
} else {
mGUI.pbLoading.setValue(100);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void calculate() {
random = new Random();
loadingProgress = loadingProgress + random.nextInt(9) + 1;
}
}
I think you have a problem with your swing-threads. Make sure, that mGUI.pbLoading.setValue is returning as expected.
You have to use SwingWorker to execute graphical user interface operations and calculations otherwise the event dispatch thread won't be able to repaint. Here is a progress bar demo code by Oracle.
Related
I have a question about using events to run loops since doing so seems to lock down the thread. For example I have an nativeMousePressed and nativeMouseReleased event and I am trying to execute some code continuously while the mouse is pressed and then stop when its released. I tried to do this by creating a static boolean variable in another manager class and then setting it to true when the mouse is being pressed and false when the mouse is released. Then I decided to make a while loop that gets called from inside that nativeMousePressed event that uses the boolean value I talked about earlier. The issue is that no events can be called while that while loop is running which means the boolean value when never become false creating an infinite loop. How can I run the while loop while keeping the events running as well?
I assume this has to do with the thread being locked down but I have not worked with stuff like this much and would like some help figuring out how to run both these things in parallel.
public class NativeMouseEvent implements NativeMouseListener {
Program program = new Program();
#Override
public void nativeMouseClicked(org.jnativehook.mouse.NativeMouseEvent e) {
}
#Override
public void nativeMousePressed(org.jnativehook.mouse.NativeMouseEvent e) {
if(e.getButton() == 1 && Controller.threeClicked) {
Controller.fixAim = true;
program.start();
}
}
#Override
public void nativeMouseReleased(org.jnativehook.mouse.NativeMouseEvent e) {
program.interrupt();
Controller.fixAim = false;
}
}
Here is what my second thread is running...
public class Program extends Thread {
public void run() {
while(Controller.fixAim) {
System.out.println("test");
}
}
Here my second attempt which also gives me an error saying that this.program is null.
public class NativeMouseEvent implements NativeMouseListener {
Program program;
#Override
public void nativeMouseClicked(org.jnativehook.mouse.NativeMouseEvent e) {
}
#Override
public void nativeMousePressed(org.jnativehook.mouse.NativeMouseEvent e) {
if(e.getButton() == 1 && Controller.threeClicked) {
Controller.fixAim = true;
if(program != null) {
program = new Program();
program.start();
}
}
}
#Override
public void nativeMouseReleased(org.jnativehook.mouse.NativeMouseEvent e) {
program.interrupt();
program = null;
Controller.fixAim = false;
}
}
Start a tread on mouse down and stop the tread on mouse up. In the thread do circle drawing.
Something like below java code. Note: it is just an example. You need to make changes to make it work in your android environment.
public class Test {
Thread drawTask;
public void mouseDown() {
drawTask = new Thread(()-> {
int i = 0;
try {
for(;;) {
System.out.print("\rDrawing circle " + i++);
Thread.sleep(500);
}
} catch(InterruptedException e) {
System.out.println("finished drawing circle.");
}
});
drawTask.start();
}
public void mouseUp() {
if(drawTask != null) {
drawTask.interrupt();
drawTask = null; //<--- make sure you do this
}
}
public static void main(String[] args) {
Test test = new Test();
Scanner in = new Scanner(System.in);
System.out.println("type anything and press Enter to simulate mouse down/up");
in.next();
test.mouseDown();
in.next();
test.mouseUp();
in.next();
in.close();
}
}
So I have implemented Observer Pattern. In the update function of my Observer class, I want to update the progressbar. The problem is that it instantly updates to the maximum value. I don't get an loading animation. The application calculates some edges. After the calculation of the edges, they will be drawn on the screen.
My observers are the following :
GenerateEdgeBottom
GenerateEdgeLeft
GenerateEdgeRight
I also have a KochManager and an javaFX class.
I make my progressbars in the javaFX class. In the javaFX class is a function that binds all the progressbars.
Part of my javaFX class
public void binding(GenerateEdgeLeft leftEdges, GenerateEdgeRight rightEdges, GenerateEdgeBottom bottomEdges) {
progressBarLeft.progressProperty().unbind();
progressBarLeft.setProgress(0);
progressBarRight.progressProperty().unbind();
progressBarRight.setProgress(0);
progressBarBottom.progressProperty().unbind();
progressBarBottom.setProgress(0);
progressBarLeft.progressProperty().bind(leftEdges.progressProperty());
progressBarRight.progressProperty().bind(rightEdges.progressProperty());
progressBarBottom.progressProperty().bind(bottomEdges.progressProperty());
lblLeft.textProperty().bind(leftEdges.messageProperty());
lblRight.textProperty().bind(rightEdges.messageProperty());
lblBottom.textProperty().bind(bottomEdges.messageProperty());
}
My kochmanager
public void changeLevel(int nxt) {
edges.clear();
koch.setLevel(nxt);
this.leftEdgeTask = new GenerateEdgeLeft(koch);
this.bottomEdgeTask = new GenerateEdgeBottom(koch);
this.rightEdgeTask = new GenerateEdgeRight(koch);
tsCalc.init();
tsCalc.setBegin("Begin calculating");
//System.out.println("test 3");
Platform.runLater(() -> {
//System.out.println("test 4");
application.binding(leftEdgeTask,rightEdgeTask,bottomEdgeTask);
futures.clear();
Future<?> f = pool2.submit(bottomEdgeTask);
Future<?> f2 =pool2.submit(leftEdgeTask);
Future<?> f3 = pool2.submit(rightEdgeTask);
futures.add(f);
futures.add(f2);
futures.add(f3);
try {
edges.addAll(leftEdgeTask.get());
edges.addAll(rightEdgeTask.get());
edges.addAll(bottomEdgeTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//System.out.println("test 5");
koch.getEdges().clear();
tsCalc.setEnd("End calculating");
application.setTextNrEdges("" + koch.getNrOfEdges());
application.setTextCalc(tsCalc.toString());
application.requestDrawEdges();
drawEdges();
});
}
My GenerateEdgeLeft
public class GenerateEdgeLeft extends Task<ArrayList<Edge>> implements Observer {
private KochFractal koch;
//private KochManager manager;
private int numberOfEdges;
private int counter;
private ArrayList<Edge> edges = new ArrayList<>();
public GenerateEdgeLeft(KochFractal koch){
this.koch = koch;
numberOfEdges = koch.getNrOfEdges() / 3;
this.koch.addObserver(this);
}
public GenerateEdgeLeft() {
//edges.size
}
public ArrayList<Edge> getResult() {
return edges;
}
#Override
public ArrayList<Edge> call() throws Exception {
counter = 0;
System.out.println("Thread Started Left");
koch.generateLeftEdge();
edges = koch.getEdges();
return edges;
}
#Override
public void update(Observable o, Object arg) {
edges.add((Edge) arg);
counter++;
updateProgress(counter, numberOfEdges);
updateMessage( "Left : " + counter);
}
}
Below is the compiled program replica of actual problem code,
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class Dummy {
public static boolean getUserCheck(int size, boolean Check) {
if (Check) {
int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
"Warning", 0);
if (ret > 0) {
System.out.println("User said No: " + ret);
return false;
} else if (ret <= 0) {
System.out.println("user said Yes: " + ret);
return true;
}
}
return true;
}
public static void workerMethod1() {
System.out.println("am worker method 1");
}
public static void workerMethod2() {
System.out.println("am worker method 2");
}
public static void main(String[] args) {
System.out.println("mainthread code line 1");
int size = 13;
boolean thresholdBreach = true;
if (getUserCheck(size, thresholdBreach)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
workerMethod1();
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
workerMethod2();
}
});
}
System.out.println("mainthread code line 2");
System.out.println("mainthread code line 3");
}
}
where i would like to run the if{} block in main() on separate thread. Because these 2 lines,
System.out.println("mainthread code line 2");
System.out.println("mainthread code line 3");
need not wait for completion of if(){} block
Another problem is, experts recommend to run confirm-dialog methods on event thread.
int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
"Warning", 0);
Please help me!!!!
JOptionPane is a Swing method and should be called on the EDT, the Event Dispatch Thread, and only on this thread, and so it suggests that all your code above should be on the EDT, and that most of your SwingUtilities.invokeLater(new Runnable() calls are completely unnecessary. The only necessary ones will be the main one, where you launch your Swing GUI code, and any areas where Swing calls need to be made from within background threads. Again, if any of the above code is being made within background threads, then the JOptionPane should not be in that thread.
For more specific information in this or any other answer, please provide more specific information in your question. Let's end all confusion. The best way to get us to fully and quickly understand your problem would be if you were to to create and post a minimal example program, a small but complete program that only has necessary code to demonstrate your problem, that we can copy, paste, compile and run without modification.
I have a sneaking suspicion that a decent refactoring along MVC lines could solve most of your problems. Your code is very linear with its lines of code that must follow one another and its if blocks, and it is also tightly coupled with your GUI, two red flags for me. Perhaps better would be less linear code, more event and state-driven code, code where your background code interacts with the GUI via observer notification, and where the background code likewise responds to state changes in the GUI from control notification.
Your control needs two SwingWorkers, one to get the row count and the other to get the rest of the data if the user decides to do so. I'd add a PropertyChangeListener to the first SwingWorker to be notified when the row count data is ready, and then once it is, present it to the view for the user to select whether or not to proceed. If he decides to proceed, I'd then call the 2nd SwingWorker to get the main body of the data.
For example, a rough sketch of what I'm talking about:
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class SwingWorkerFooView extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 300;
private JProgressBar progressBar;
private JDialog dialog;
public SwingWorkerFooView() {
add(new JButton(new ButtonAction("Foo", this)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public boolean showOptionGetAllData(int numberOfRows) {
String message = "Number of rows = " + numberOfRows + ". Get all of the data?";
String title = "Get All Of Data?";
int optionType = JOptionPane.YES_NO_OPTION;
int result = JOptionPane.showConfirmDialog(this, message, title, optionType);
return result == JOptionPane.YES_OPTION;
}
public void showProgressBarDialog() {
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
Window window = SwingUtilities.getWindowAncestor(this);
dialog = new JDialog(window, "Hang on", ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel();
panel.add(progressBar);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
}
public void closeProgressBarDialog() {
dialog.dispose();
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SwingWorkerFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SwingWorkerFooView());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class ButtonAction extends AbstractAction {
Workers workers = new Workers();
private SwingWorker<Integer, Void> firstWorker;
private SwingWorker<List<String>, Void> secondWorker;
private SwingWorkerFooView mainGui;
public ButtonAction(String name, SwingWorkerFooView mainGui) {
super(name);
this.mainGui = mainGui;
}
#Override
public void actionPerformed(ActionEvent e) {
firstWorker = workers.createFirstWorker();
firstWorker.addPropertyChangeListener(new FirstPropertyChangeListener());
firstWorker.execute();
mainGui.showProgressBarDialog();
}
private class FirstPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
mainGui.closeProgressBarDialog();
try {
int numberOfRows = firstWorker.get();
boolean getAllData = mainGui.showOptionGetAllData(numberOfRows);
if (getAllData) {
secondWorker = workers.createSecondWorker();
secondWorker.addPropertyChangeListener(new SecondPropertyChangeListener());
secondWorker.execute();
mainGui.showProgressBarDialog();
} else {
// user decided not to get all data
workers.cleanUp();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class SecondPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
mainGui.closeProgressBarDialog();
try {
List<String> finalData = secondWorker.get();
// display finalData in the GUI
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
class Workers {
// database object that may be shared by two SwingWorkers
private Object someDataBaseVariable;
private Random random = new Random(); // just for simulation purposes
private class FirstWorker extends SwingWorker<Integer, Void> {
#Override
protected Integer doInBackground() throws Exception {
// The Thread.sleep(...) is not going to be in final production code
// it's just to simulate a long running task
Thread.sleep(4000);
// here we create our database object and check how many rows there are
int rows = random.nextInt(10 + 10); // this is just for demonstration purposes only
// here we create any objects that must be shared by both SwingWorkers
// and they will be saved in a field of Workers
someDataBaseVariable = "Fubar";
return rows;
}
}
private class SecondWorker extends SwingWorker<List<String>, Void> {
#Override
protected List<String> doInBackground() throws Exception {
// The Thread.sleep(...) is not going to be in final production code
// it's just to simulate a long running task
Thread.sleep(4000);
List<String> myList = new ArrayList<>();
// here we go through the database filling the myList collection
return myList;
}
}
public SwingWorker<Integer, Void> createFirstWorker() {
return new FirstWorker();
}
public void cleanUp() {
// TODO clean up any resources and database stuff that will not be used.
}
public SwingWorker<List<String>, Void> createSecondWorker() {
return new SecondWorker();
}
}
The key to all of this is to not to think in a linear console program way but rather to use observer design pattern, i.e., listeners of some sort to check for change of state of both the GUI and the model.
It's essentially:
create worker
add observer to worker (property change listener)
execute worker
show progress bar dialog or notify user in some way that worker is executing.
The listener will be notified when the worker is done, and then you can query the worker (here via the get() method call) as to its end result.
Then the progress dialog can be closed
And the view can display the result or get additional information from the user.
Yes; SwingUtilities.invokeLater() simply places your runnable on the AWT event queue to be processed later, and it is safe to do so at any time.
So my code works just the way I want it the only issue I'm having is this.. Basically I am having a main class which controls gates on a railroad track, when a train is approaching or crossing the track from either 1 of two tracks the gates should close. The only issue I'm having is the statements for when a gate opens or closes spam like 3-5 times everytime it does something so if the gate is closing it will go..
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closed
I'm wondering why this is occuring, here is my code for the Gate class and Main class
public class Gate {
private boolean isClosed = false;
private boolean closing = false;
private boolean opening = false;
public Gate(){
}
public void close(){
if(!(isClosing() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setClosing(true);
setOpening(false);
System.out.println("GATE: Closing");
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
public void open(){
if(!(isOpening() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setOpening(true);
System.out.println("GATE: Opening");
Thread.sleep(400);
setOpening(false);
if(closing == false){
setClosed(false);
System.out.println("GATE: Opened");
}
}catch(Exception ex){
}
}
};
new Thread(task, "openThread").start();
}
}
public boolean isClosed(){
return isClosed;
}
public boolean isClosing(){
return closing;
}
public boolean isOpening(){
return opening;
}
public synchronized void setClosing(boolean t){
closing = t;
}
public synchronized void setOpening(boolean t){
opening = t;
}
public synchronized void setClosed(boolean t){
isClosed = t;
}
}
public class Controller {
public static void main(String[] args){
Track t1 = new Track("Track 1");
Track t2 = new Track("Track 2");
Gate g = new Gate();
t1.simulateTrack();
t2.simulateTrack();
do{
System.out.print("");
if((t1.isApproaching() || t1.isCrossing()) || (t2.isApproaching() || t2.isCrossing())){
if(!g.isClosed() && !g.isClosing()){
g.close();
}
}else if(g.isClosed() && !g.isOpening()){
g.open();
}
}while((t1.isSimulating() || t2.isSimulating()));
}
}
Also the code for Track
import java.security.SecureRandom;
public class Track {
private static final SecureRandom gen = new SecureRandom() ;
private boolean approaching = false;
private boolean atCrossing = false;
private boolean simulating = false;
private String trackName = "";
public Track(String n){
trackName = n;
}
public void simulateTrack(){
Runnable task = new Runnable() {
public void run() {
try {
setSimulating(true);
for(int i = 0; i < 10; i++){
Thread.sleep((gen.nextInt(5000) + 2500));
setApproaching(true);
System.out.println(trackName + ": Train is now approaching.");
Thread.sleep((gen.nextInt(5000) + 3500));
setCrossing(true);
setApproaching(false);
System.out.println(trackName + ": Train is now crossing.");
Thread.sleep((gen.nextInt(1000) + 1000));
setCrossing(false);
System.out.println(trackName + ": Train has left.");
}
setSimulating(false);
} catch (Exception ex) {
}
}
};
new Thread(task, "simulationThread").start();
}
public boolean isApproaching(){
return approaching;
}
public boolean isCrossing(){
return atCrossing;
}
public boolean isSimulating(){
return simulating;
}
public synchronized void setSimulating(boolean t){
simulating = t;
}
public synchronized void setApproaching(boolean t){
approaching = t;
}
public synchronized void setCrossing(boolean t){
atCrossing = t;
}
}
This is just an idea:
By shooting the close() logic on a background thread you lose the atomicity. The main's do loop can go around 5 times before it gives up the control of the main thread and one of the "closeThread"s start executing. Don't you see multiple "GATE: Closed"s as well?
Try this (not tested, sorry):
public synchronized void close() { // added synchornized
if (!isClosing()) { // read: "if not closing"
setClosing(true); // set closing so next time close() is called it is a no op
setOpening(false); // close other loopholes so the state is correct
System.out.println("GATE: Closing");
// we're in closing state now, because the close method is almost finished
// start the actual closing sequence
Runnable task = new Runnable() {
public void run() {
try {
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
You'll need to modify open() the same way, so that the invariants are always kept. Checking and setting the closing and opening flags are mutually exclusive, that's what you get by placing synchronized on both of them.
I am new to programming (I'm 11 and hoping for java coding to be my career, but its just a hobby right now :)) and I just made a countdown program, here is the class:
package me.NoahCagle.JAVA;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
private static final long serialVersionUID = 1L;
public static int width = 600;
public static int height = 500;
public static String title = "Countdown!";
public static boolean running = false;
public int number = 11;
public Thread thread;
Dimension size = new Dimension(width, height);
public Main() {
super(title);
setSize(size);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Main m = new Main();
m.start();
}
public void start() {
if (running) {
return;
}
running = true;
Thread thread = new Thread(this);
thread.start();
}
#SuppressWarnings("static-access")
public void run() {
while (running) {
number--;
if (number == -1) {
System.out.println("Done!");
System.exit(0);
}
try {
thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("" + number);
}
}
public void stop() {
if (!running) {
return;
}
running = false;
try {
thread.join();
}catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
That may not have been necessary, but whatever. Well like I was saying, if you read the code, you will notice that it prints the value to the console. Well, if I could get that to display on a JLabel, while updating at the same time. I have tried just doing setText("" + number) thinking that because I have a thread going, it would repaint. But that didn't happen. It was just stuck at 11. Can someone please help me? Thanks
First, you may want to take a read through Concurrency in Swing. There are some very important constraints when it comes to dealing with multiple threads and Swing.
For your problem, you really should be using a javax.swing.Timer, and with examples...
Java Label Timer and Saving
Adding a timer and displaying label text
How could I add a simple delay in a Java Swing application?
As 11yrs old you have done good job here. But where did you add any panel to the frame on which u want to show the number? Once you do it and put some label to add the number you will need to call the repaint method. Also to use threads with swings, there are many libraries you can use like Timer.
Happy Coding!