Synchronous textArea.clear() followed by textArea.setText() does not clear text - java

When a button is clicked, I'd like to clear the textArea, do some work, then print results in the textArea, all in the same method, synchronously.
public void resetClicked(MouseEvent mouseEvent) {
textArea.clear();
String result = someSynchronousWork();
textArea.setText(result);
}
What happens is, the textArea is updated, but the clear action is not visible. The work takes several seconds. If I comment out everything except the textArea.clear(), it works.

As I mention in my comment, JavaFX doesn't render the next frame until a "pulse" occurs. This won't happen when you clear the text, run a long-running task, and then set the text all in one method; the pulse happens after all this occurs which means what gets rendered is the new text. Also, running a several-seconds-long task on the JavaFX Application Thread is not a good idea. All blocking and/or long-running tasks should be done on a background thread—otherwise your GUI becomes unresponsive (and your users become unhappy/nervous).
If this task is too simple to use a Task for then you could try a CompletableFuture, which may make it easier for you to invoke simple things asynchronously.
public void resetClicked(MouseEvent event) {
event.consume();
textArea.clear();
CompletableFuture.supplyAsync(this::someSynchronousWork)
.whenCompleteAsync((result, error) -> {
if (error != null) {
// notify user
} else {
textArea.setText(result);
}
}, Platform::runLater);
}
Depending on how you want to handle errors, you can do different things. For instance:
// can't ever be an error
supplyAsync(this::someSynchronousWork)
.thenAcceptAsync(textArea::setText, Platform::runLater);
// just want to show "Error" in text area on error
supplyAsync(this::someSynchronousWork)
.exceptionally(error -> "ERROR")
.thenAcceptAsync(textArea::setText, Platform::runLater);
Note: These examples will execute someSynchronousWork() using the common ForkJoinPool. You can customize this by passing an Executor to supplyAsync.
Note: You may want to disable some of the UI components (e.g. the button) while the task is running to prevent multiple tasks being launched at once. Enable the UI components when the task completes.
Also, you seem be using the onMouseClicked property of the Button to process actions. Consider using the onAction property instead; an onAction handler is notified for more than just mouse clicks (e.g. when the button has focus and Space or Enter is pressed).

Related

Value of JLabel is not being updated using setText() in Java

I know many people have asked this question before but I couldn't find any answer that solved my problem. My code is like this:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
myfunction(); //runs for a very long time
}
The original text of JLabel is "OFF". Now I want to change the text to "ON" when the mouse is clicked but the text doesn't set until myfunction() is complete (which may take several minutes).
I have tried the invalidate function, making a separate function for setting the text but nothing is working.
Please help me with this problem!
The problem is that mouseClicked(...) is executed on the UI Thread. That is the Thread that is responsible for handling all sorts of user actions (like a mouse click) and also the drawing of components (like updating the text of the label on screen). If you execute your long running method call on the UI thread, it will be blocked and can't draw anything until execution is complete. You'll have to use multi threading to get around this problem.
The following might not be the most elegant solution, but if you are new to multi threading it will get the job done:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
(new Thread() {
public void run() {
myfunction();
}
}).start();
}
It will spawn a new Thread that handles your method, which will let the UI Thread continue doing its thing. consider deactivating the button that has just been clicked, so the user can't start the execution while it is already in progress (which usually is what you want..)

How to set anchor pane visible just before subprogram call in javafx?

The purpose of the following code is to handle menu option choice. Because processing in method update() takes quite a bit time, I want to display info before this takes place.
My info is simply an anchor pane with label in it, which normally is set to be not visible. Unfortunately, the code below sets anchor pane visible only for a very short time after update() terminates. How can I do it properly?
public void updateRates(ActionEvent event)
{
updateInfo.setVisible(true);
update();
updateInfo.setVisible(false);
}
Most user interface libraries are single threaded. That thread is created by and controlled by the UI system. All listeners are called on that thread. When the UI thread is held up, such as when your update() call is running, the UI does not redraw at all, and keyboard events and mouse events will “pile up” in the event queue, appearing to be ignored, until the thread is allowed to continue running (at which point all of them will be processed immediately, in order).
For this reason, lengthy operations must never execute on the UI thread. One option is to run a Task in a different thread:
Task<Boolean> updater = new Task<Boolean>() {
#Override
public Boolean call() {
updateValue(true);
update();
return false;
}
};
updateInfo.visibleProperty().bind(updater.valueProperty());
new Thread(updater).start();
You could also create a Thread from a plain Runnable:
updateInfo.setVisible(true);
Runnable updater = new Runnable() {
#Override
public void run() {
update();
Platform.runLater(() -> updateInfo.setVisible(false));
}
};
new Thread(updater).start();
Platform.runLater is required in this case, because user interface objects, including all JavaFX Nodes, may only be accessed and modified in the UI thread. If your update() method is manipulating any Nodes, it will need to use Platform.runLater to do so.
The Task class has built-in support for showing work progress in the UI, but since you haven’t included the code for your update() method, I can’t tell you whether that support would be useful in your situation.

How to prevent input via console from taking priority over disabling buttons?

I have some code that creates a window with some buttons that when clicked prompt user for input via the console (Note: I'm running this in NetBeans). For now I want to keep using the console, so I'm not really looking for a solution where I create a new frame asking for input via a GUI.
The issue is that while console is taking input from the user the buttons are all still enabled and thus queue up any presses and those get executed when the user finishes inputting stuff into the console. I want to disable the buttons while the console is taking input. I thought the .setEnabled(false) method for buttons would do it, and I think normally it would. It seems that Java prioritizes user input and so the buttons don't get disabled until the user finishes inputting to the console. Once the user finishes input the buttons are supposed to get re-enabled anyways, so they are effectively never disabled. The code in question is below:
for(int i = 0; i < LABELS.length; i++) {
menu_a.add(new JButton(LABELS[i]));
menu_a.get(i).addActionListener((ActionEvent e) -> {
menu_a.stream().forEach((b) -> {b.setEnabled(false);});
menu_b.stream().forEach((b) -> {b.setEnabled(false);});
menu_c.stream().forEach((b) -> {b.setEnabled(false);});
if(!menu_a.get(menu_a.indexOf(e.getSource())).isEnabled()) {
MenuActions.MenuActions(menu_a.indexOf(e.getSource()) + 1, data);
menu_a.stream().forEach((b) -> {b.setEnabled(true);});
menu_b.stream().forEach((b) -> {b.setEnabled(true);});
menu_c.stream().forEach((b) -> {b.setEnabled(true);});
}
});
itemPanel.add(menu_a.get(i));
}
LABELS is just an array of strings which are the labels for the buttons.
MenuActions is a class that based on the index of the button prompts the user for different types of input via the console
data is an object that contains data that gets manipulated based on user input, not really relevant for the question I think.
So #MadProgrammer was right. I had to look-up and implement a SwingWorker to get the code to execute how I wanted it. It ended up looking like this:
EDIT: So I didn't know I was committing a crime against humanity by modifying GUI elements outside their original context. I changed my code and now it looks like this.
for(int i = 0; i < LABELS.length; i++) {
menu_a.add(new JButton(LABELS[i]));
menu_a.get(i).addActionListener((ActionEvent e) -> {
menu_a.stream().forEach((b) -> {b.setEnabled(false);});
menu_b.stream().forEach((b) -> {b.setEnabled(false);});
menu_c.stream().forEach((b) -> {b.setEnabled(false);});
new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
menuActions(menu_a.indexOf(e.getSource()) + 1);
return null;
}
#Override
public void done() {
menu_a.stream().forEach((b) -> {b.setEnabled(true);});
menu_b.stream().forEach((b) -> {b.setEnabled(true);});
menu_c.stream().forEach((b) -> {b.setEnabled(true);});
}
}.execute();
});
panel_a.add(menu_a.get(i));
}
I created an anonymous SwingWorker to take care of what I wanted. I ended up having to disable the buttons in the doInBackground() method else it wouldn't disable them, and I was stuck with my original problem. (See edit below) Also, menuActions now refers to a private method within the class, I figured since it's really only called from the GUI I might as well move it in.
EDIT: So I changed the disabling to happen outside of the SwingWorker and now it's working fine. I'll just blame it one me not saving my changes, perhaps I was recompiling old code.
I also found some more info on the whole SwingWorker and EventDispatchThread business that I feel is relevant and helped me understand things a little better.
Workflow
There are three threads involved in the life cycle of a SwingWorker :
Current thread: The execute() method is called on this thread. It
schedules SwingWorker for the execution on a worker thread and returns
immediately. One can wait for the SwingWorker to complete using the
get methods.
Worker thread: The doInBackground() method is called on this thread.
This is where all background activities should happen. To notify
PropertyChangeListeners about bound properties changes use the
firePropertyChange and getPropertyChangeSupport() methods. By default
there are two bound properties available: state and progress.
Event Dispatch Thread: All Swing related activities occur on this
thread. SwingWorker invokes the process and done() methods and
notifies any PropertyChangeListeners on this thread.
https://docs.oracle.com/javase/8/docs/api/javax/swing/SwingWorker.html
Thanks again to #MadProgrammer for pointing me in the right direction, and promptly letting me know my original code was atrocious.
Also, I derived the code from this answer: How do I use SwingWorker in Java?

GWT: How to put event in queue (equivalent to Swing invokeLater())?

Having the following szenario: blurring a textBox (input) writes text to my status-Box (in certain conditions), and clicking a button also writes text to the status-Box.
Now when clicking the button it will blur my textBox if it is focused and that will cause the status-Box to flicker as first the blurHandler will write its result and then the clickHandler.
As i want the result of the clickHandler to appear my idea is to let the blurHandler place an event at the end of the queue which checks whether a clickHandler has written a result before.
In Swing I would try SwingUtilities.invokeLater(Runnable).
The equivalent in GWT is said to be the Scheduler but those deferred or finally commands seems always to run after the current event and before the next.
So far i use Scheduler.scheduleFixedDelay with 100ms delay and hope it comes after the clickHanlder in each browser.
See similar problem with answer.
I think there must be a better solution for this.
How to really add an event to the end of the queue or is it impossible due to limitations of HTML?
Try:
Scheduler.get().scheduleDeferred(new Command() {
#Override
public void execute() {
.......
}
});
See
Class Scheduler

Requesting user input from outside the GUI code, do I need Events or Action to communicate with the GUI in this case?

I'm writing a 2D polygon and physics editor, one functionality is to set a rotation limit for joints.
To use this functionality, the user clicks and drags a line between the joint points which need to receive the limit.
The logic of determining if the pick is valid happens outside of the GUI code.
If a pick is found, I wanted to pop up a JOptionPane.showInputDialog where the user can input the limit.
Thing is, if I do it directly, the program becomes unresponsive, I figure it's because of threading.
I's there a way to define an event listener the GUI can use that doesn't require an actual GUI component?
I want to send an event that also contains a reference to the target object to that component, then telling it that a valid pick has been made and user input is required, and then send the value back via a method of the target object.
I am very inexperienced with Swing.
My hunch is that I might be able to add an ActionListener to the main window, but I don't know how I could address that listener specifically.
As in, how would I need to define an Action that only gets processed by that particular listener?
If that is actually possible, of course.
So far I have only used listeners to let the GUI talk to the logic, not the other way around...
Edit:
The program becomes unresponsive the movement I call
result = JOptionPane.showInputDialog(this,"Enter Limit.");
That just breaks it. Can't even enter anything into the textbox, nor close it, etc.
I figure it's because it spawns a modal dialog that pauses some thread, and calling it from somewhere in the bowels of non GUI code is just not the thing I should do, but I'm too inexperienced to know another way...
Edit2:
I should add that I can use JOptionPane.showInputDialog without any problems if I spawn it, for example, after clicking a button or choosing a popup menu option.
In fact that's how I rename the items I am working with.
But I assume at that point, the dialog is being spawned inside the GUI thread, or this Event Dispatcher queue thing.
The problem with this though is, that this takes visible, interactive GUI components that fire that event.
What I'd like, however, is some sort of component that would spawn JOptionPane.showInputDialog just like a clicked button or context menu would, but without having to be interacted with by the user, but instead by the code.
I guess I could use invisible buttons and emulate mouseclick events, but that's pretty hacky...
Also, I tried spawning Threads and Runnables which spawned the JOptionPane.showInputDialog, but that didn't help either.
Unless I spawn the JOptionPane from a GUI source, everything stalls, and the dialog won't work.
The publisher will have a public add/remove listener, where the subscriber will add itself or be added via another channel to the EventListenerList in the publisher.
You can create your own listener interface that extends EventListener and a function to shoot an event. Below is an example:
import java.util.EventListener;
public interface MyEventListener extends EventListener {
public void myEventOccurred(MyEvent event);
}
You can then create your custom event class, "MyEvent" in the example above like:
import java.util.EventObject;
public class MyEvent extends EventObject {
// customer fields and methods here
public MyEvent(Object source) //more possible args here {
super(source);
//other things here to do what you want
}
}
Now you can have your subscriber implement MyEventListener and override the myEventOccurred(..) method.
Another approach would be to use the SwingWorker class to execute the logic of determining the pick in a dedicated thread without blocking the GUI dispatch thread, and use its callback method to execute the GUI action (open the input dialog).
See : http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html
(This page has a better explanation of concept than I could write.)
It should be possible for your background thread to spawn a dialog with invokeAndWait():
final double[] result = new double[1];
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
result[0] = Double.parseDouble(
JOptionPane.showInputDialog("Enter value:"));
} catch(NumberFormatException e) {
result[0] = -1;
}
}
}
// ... do something with result[0]
Here I made the result an array just so that it can be final (accessible to the anonymous class) and also mutable.

Categories