I want to be able to see updates on my graphical interface while the program is executing and not to wait till the click event on button ends
public class MainController implements Initializable {
#FXML
private label label;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
private void Event(ActionEvent event) {
// start is a button
if (event.getSource() == Start) {
// calculations
// updating label
label.setText(" update me ");
// other calculations
// updating label for the second time
label.setText(" update me ");
}
}
}
This is a simple example of my code (javaFXML), please note that the calculations and updates are more complicated than the demonstrative example and takes too much time to execute that's why I want to preview updates while executing.
You should Know and use the threads concept
Your code will be like this:
if (event.getSource() == Start) {
new Thread(new Runnable() {
#Override
public void run() {
//do the calculations
System.out.println("calculations is finished");
}
}).start();
// updating label
label.setText(" update me ");
new Thread(new Runnable() {
#Override
public void run() {
//do the other calculations
System.out.println("calculations is finished");
}
}).start();
// updating label for the second time
label.setText(" update me ");
}
But you must be aware the synchronization problems that may occur between the calculations.
I recommend you follow this course :
https://www.youtube.com/playlist?list=PLBB24CFB073F1048E
Related
Looking to update GUI first thing upon click of a button however Platform.runLater executes at a later stage and am looking for the piece of code which updates the GUI to happen first thing upon click of a button.
Platform.runLater(new Runnable() {
#Override
public void run() {
//Update GUI here
}
});
Would highly appreciate if anyone can provide any inputs or recommendations.
Although the API specifies that Platform.runLater "runs the specified Runnable on the JavaFX Application Thread at some unspecified time in the future", it usually takes little to no time for the specified thread to be executed. Instead, you can just add an EventHandler to the button to listen for mouse clicks.
Assuming the controller implements Initializable
#FXML Button button;
#Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
updateGUI();
}
});
}
private void updateGUI() {
// code
}
I have a method like below.
ProgressWindow is a sub class of JFrame containing JProgressBar.
addProgress() increments a value in the JProgressBar.
If I call this method from a method in another class, a frame of ProgressWindow will show up but not JProgressBar and some JLabels inside the frame. They show up after the last line (System.out.println("finish")).
If I call this method in a main method in the class containing this method, then every component (Bar, labels...) instantly shows up.
What can I do for showing the window correctly?
static void search(){
ProgressWindow window = new ProgressWindow();
window.setVisible(true);
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
progressWindow.addProgress(); // increment progress value
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
The main problem is you seem to be calling search from the context of the Event Dispatching Thread.
The problem occurs because you are using execs.invokeAll which blocks until all the callables have finished running.
This means that the EDT is unable to process new events in Event Queue, including repaint events, this is why your UI is coming to a stand still...
There are a number of issues you are now going to face...
You should never update/modify a UI component from any thread other than the EDT
You should block the EDT for any reason
You seem to want to know when the search is complete, so you know need some kind of event notification...
The first thing we need is some way to be notified that the search has completed, this means you can no longer rely on search returning when the search is complete...
public interface SearchListener {
public void searchCompleted();
}
Next we need an intermeditate search method that builds the UI and ensure that the search is launched within it's own Thread...
static void search(final SearchListener listener) {
final ProgressWindow window = new ProgressWindow();
window.setVisible(true);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
search(listener, window);
}
});
t.start();
}
Then we need to modify the original search method to utilise the SearchListener interface to provide notification when the search is complete...
static void search(final SearchListener listener, final ProgressWindow window){
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
// This method needs to ensure that
// what ever it does to the UI, it is done from within
// the context of the EDT!!
progressWindow.addProgress();
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
listener.searchCompleted();
}
});
}
Now, without the source code for addProgress, I might be tempted to use
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressWindow.addProgress();
}
});
return null;
}
});
}
Instead...
Take a look at Concurrency in Swing for more details
Sounds like you what you're wanting to do is invoke the setVisible on the Swing UI thread, you can do this with invokeAndWait or invokeLater.
So something like:
final ProgressWindow window = new ProgressWindow();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
window.setVisible(true);
}
});
I need to update some components (Labels, ProgressBar, button) from an handle function in javafx.
The dialog is a modal dialog and it's used to perform sequentially some operations.
#FXML public void updateHandle(ActionEvent action){
buttonSend.setDisable(true);
/* Operation 1 */
progressBar.setProgress(0.05);
label.setText("Init..");
myInitFunction();
myVar = new var(); //global
/* Op2 */
progressBar.setProgress(0.10);
label.setText("Check connection..");
myConnFunction();
// ....
// ....
}
The problem is that all my functions are correctly processed, but the elements on the GUI didn't change.
EDIT
I tried to use Platform.runlater but it seems to don't work...
void updateLabelLater(final Label label, final String text) {
Platform.runLater(new Runnable() {
#Override public void run() {
label.setGraphic(null);
label.setText(text);
}
});
}
void updateProgressBar(final ProgressBar progressBar, final double val){
Platform.runLater(new Runnable() {
#Override public void run() {
progressBar.setProgress(val);
}
});
}
Is updateHandle running on the Event Thread? I didn't bother with FXML because of tooling issues so this isn't much of an answer. (But hopefully it helps!)
//Pseudo Code
public doLongRuningOperation(final Object ... objects){
final Thread longRunning = new Thread(){
public void run(){
update("this");
sleep(1000); //Pause/do something etc...
update("should");
sleep(1000);
update("update");
System.err.println("completed");
}
};
longRunning.start(); //Start this process
}
//Rejoin the UI Thread and update it...
public void update(final String text){
//reference to label 'lbl'
Platform.runLater(new Runnable(){
lbl.setText(text);
});
}
I'm writing an application that reads in audio, analyses this data and then displays the results in realtime. Currently I am using a SwingWorker to run the loop that initiates the background analysis and calling the SwingUtilities.invokeLater method inside the loop to update the GUI components each time analysis has completed. At the moment, the GUI seems to be updating randomly, and occasionally not at all.
The following code shows how I'm trying to accomplish this. The TunerListener is an inner class of a JPanel subclass. PrevNote, nextNote, frequency, and the light variables are all components in the JPanel subclass that I want to update:
private class TunerListener implements ActionListener {
private boolean firstUpdate = true;
private boolean executing = false;
private TunerWorker tunerWorker = null;
private final class TunerWorker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() {
while (!this.isCancelled()) {
// Audio analysis in worker thread
model.update(firstUpdate);
// Update components in EDT
if (!this.isCancelled()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
prevNote.setText(model.getPrev());
currentNote.setText(model.getNote());
nextNote.setText(model.getNext());
frequency.setText("Frequency: "
+ model.getFrequency());
switch (model.getOffset()) {
case -2:
light_2.setIcon(onRed);
light_1.setIcon(off);
light0.setIcon(offBig);
light1.setIcon(off);
light2.setIcon(off);
break;
case -1:
light_2.setIcon(off);
light_1.setIcon(onRed);
light0.setIcon(offBig);
light1.setIcon(off);
light2.setIcon(off);
break;
case 0:
light_2.setIcon(off);
light_1.setIcon(off);
light0.setIcon(onGreen);
light1.setIcon(off);
light2.setIcon(off);
break;
case 1:
light_2.setIcon(off);
light_1.setIcon(off);
light0.setIcon(offBig);
light1.setIcon(onRed);
light2.setIcon(off);
break;
case 2:
light_2.setIcon(off);
light_1.setIcon(off);
light0.setIcon(offBig);
light1.setIcon(off);
light2.setIcon(onRed);
break;
}
firstUpdate = false;
}
});
}
}
return null;
}
#Override
protected void done() {
}
};
#Override
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand().equals("tune")) {
if (!executing) {
executing = true;
firstUpdate = true;
tune.setText("Stop Tuning");
tunerWorker = new TunerWorker();
tunerWorker.execute();
} else {
tune.setText("Start Tuning");
executing = false;
tunerWorker.cancel(true);
}
}
}
}
Edit
I notice when I use the debugger that I sometimes get to a point where it tells me the source could not be found and in the debugging window it says something about a FutureTask$Sync.innerRun. Does this narrow it down at all?
As an alternative, use an instance of javax.swing.Timer, illustrated here, to Start and Stop the playing of a selected Note, shown here. The play() method feeds a SourceDataLine, which operates asynchronously, and the enum Note makes constructing a JComboBox particularly easy.
final JComboBox combo = new JComboBox();
for (Note note : Note.values()) {
combo.addItem(note);
}
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(combo.getSelectedItem());
}
});
this.add(combo);
I'm new to Swing and I was trying to do this:
On pressing a JButton, the program will start iterating over hundreds of items, taking 1 second to process each one, and after finishing each one he should update a label to show the number of items already processed.
The problem is, the label's text is not updated until the cycle finishes iterating over all the items.
I searched online and apparently it's because this is running in the same thread, so I created a new thread to process the data and to update the variable to be used in the label (number of processed files).
But it didn't work. Then I even made another thread, which I start after the previous one, that just repaints the label. Still nothing works.
The code is like this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try { SwingUtilities.invokeLater(validateFiles); }
}); }
Runnable validateFiles = new Runnable() {
#Override
public void run() {
while(x_is_not_100) {
processLoadsOfStuff();
label.setText(x); }
}
};
Can you help me with this?
Simple - use a SwingWorker. For more information, read the Tasks that Have Interim Results tutorial.
Here's a pretty generic example that will use a JLabel to display counting from 0 to 30 -
public final class SwingWorkerDemo {
private static JLabel label =
new JLabel(String.valueOf(0), SwingConstants.CENTER);
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
JLabelSwingWorker workerThread = new JLabelSwingWorker();
workerThread.run();
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class JLabelSwingWorker extends SwingWorker<Void, Integer>{
#Override
protected Void doInBackground() throws Exception {
for(int i = 1; i < 31; i++){
Thread.sleep(1000);
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> integers) {
Integer i = integers.get(integers.size() - 1);
label.setText(i.toString());
}
}
}
The background processing must be done in a separate thread. But the label update must be done in the event dispatch thread.
So your code should look like this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// start a new thread for the background task
new Thread(validateFiles).start();
});
}
Runnable validateFiles = new Runnable() {
#Override
public void run() {
while(x_is_not_100) {
processLoadsOfStuff();
// use SwingUtilities.invokeLater so that the label update is done in the EDT:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
label.setText(x);
}
});
}
};
But you might want to use the SwingWorker class, which is designed to do that in a simpler way. Its documentation is very well done and contains examples.