I have a JavaFx TextArea where i update the text from another thread.
/**
* Start a new thread to update the text in the textarea. The progressmanager is called in a loop to deleiver the updated text
*/
private void startTextAreaUpdate() {
final Task<Void> updateTextAreaTask = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Start updating the progresstext
while (!prog.getIsDone()) {
updateMessage(prog.getProgressText());
Thread.sleep(100);
}
return null;
}
};
//Here the TextArea text is bound to the task
taProgressText.textProperty()
.bind(updateTextAreaTask.messageProperty());
// Start the action in a new thread
Thread th = new Thread(updateTextAreaTask);
th.setDaemon(true);
th.start();
}
Because the text doesn't fit in the textarea i always want to show the last line of the text.
In the main thread i add a ChangeListener.
taProgressText.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
int lastPos = taProgressText.getText().length();
taProgressText.positionCaret(lastPos);
}
});
However the position doesn't change. What is going wrong here?
I think the position of the carat changes, but it doesn't actually scroll.
Try this (ugly hack...) instead of the binding and the current listener you have:
updateTextAreaTask.messageProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> obs, String oldMessage, String newMessage) {
taProgressText.setText(newMessage);
ScrollBar scrollBar = (ScrollBar) taProgressText.lookup(".scroll-bar:vertical");
if (scrollBar != null) {
scrollBar.setValue(scrollBar.getMax());
}
}
});
Related
With the following code,the thread is activated on button click in the controller class. Thing is, I want to be able to change the values in he Line array, to have to anchor pane erase old lines and add new lines.This is in the controller.java after main in launched.
static startVal = -1,endVal = -1;
Lines L;//this is supposed to store new lines after some methods are called
//this method is called on button click and I want it to start again every time the button is clicked
public void drawPath(){
Task task = new Task<Void>() {
#Override
public Void call() throws Exception {
while(true){
int i = 0;
while (i<5) {
final int finalI = i;
Platform.runLater(new Runnable() {
#Override
public void run() {
anchPane.getChildren().add(L[finalI]);
if(startVal == -1)
startVal = anchPane.getChildren.size()-1;
else
endVal = anchPane.getChildren().size()-1;
}
});
i++;
Thread.sleep(2000);
}
//removeLines();
anchPane.getChildren().remove(startVal,endVal);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
I have this simple code below which updates the TextArea during updateProgress:
textArea = new TextArea();
textArea.setEditable(false);
textArea.setFocusTraversable(false);
StackPane root = new StackPane();
root.setPadding(new Insets(10));
root.getChildren().add(textArea);
Scene scene = new Scene(root, 600, 350);
primaryStage.setTitle("JavaFX Concurrency");
primaryStage.setScene(scene);
primaryStage.show();
task = new Task<Integer>(){
#Override
protected Integer call() throws Exception {
int i;
for(i = 1; i <= 100; i++){
updateProgress(i, 100);
}
return i;
}
};
task.stateProperty().addListener(new ChangeListener<State>(){
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State state) {
System.out.println(state);
}
});
task.progressProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number val) {
Platform.runLater(new Runnable(){
#Override
public void run() {
textArea.appendText("Value : " + val.intValue() + "\n");
}
});
}
});
new Thread(task).start();
But unfortunately the result was wrong. Here's the output:
I was expecting that the output should be Value : 1 to Value : 100..
I was just trying to test the concurrency package in JavaFX. Can someone tell me what's going on?
If you insert a simple:
Thread.sleep(10);
after you call updateProgress(i, 100);, everything will be better.
This comes from the documentation of updateProgress:
Calls to updateProgress are coalesced and run later on the FX application thread, and calls to updateProgress, even from the FX Application thread, may not necessarily result in immediate updates to these properties, and intermediate workDone values may be coalesced to save on event notifications. max becomes the new value for totalWork.
After this you can experience that more text-lines are appended to your text box, but:
This does not ensure that all of your updates will be done on your textbox!
If you want to ensure this, call the GUI update directly in the call() of task wrapped in the Platform.runlater(...) block.
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 want to set focus to a specific node in the content of a Tab. I added a ChangeListener to selectedItem property as follows (assume that the class contains a field named secondNode of type Node):
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
#Override
public void changed(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue) {
if (newValue != null) {
Platform.runLater(new Runnable() {
#Override
public void run() {
secondNode.requestFocus();
}
});
}
}
});
However, this does not work. I assume the reason is because TabPane performs some additional actions, which affect focus, after the new tab has been selected (but, looking through TabPane source code, I can't figure out what). If I single-step through this code in a debugger, it works as expected, so it appears to be a race condition. If this is so, how can this be resolved?
You could try replacing the Runnable with a Task:
new Thread( new Task<Void>()
{
#Override
public Void call() throws Exception // This is NOT on FX thread
{
Thread.sleep(100);
return null;
}
#Override
public void succeeded() // This is called on FX thread.
{
secondNode.requestFocus();
}
}).start();
I'm trying to put the progress to 100% after meet condition OK. But it not finds the variable "progressBar", if instead of set the progress, I set the visibility, works.
columSituacao.setCellValueFactory(new Callback<CellDataFeatures<Tabela, HBox>, ObservableValue<HBox>>() {
public ObservableValue<HBox> call(CellDataFeatures<Tabela, HBox> p) {
final Tabela tabela = p.getValue();
final ProgressBar progressBar = new ProgressBar(0.0);
progressBar.setPrefWidth(columSituacao.getWidth());
progressBar.progressProperty().bind(tabela.progressProperty());
final HBox box = new HBox();
box.setPrefHeight(Progress.PREF_HEIGHT);
final Text text = new Text();
text.textProperty().bind(tabela.etapaProperty());
final BorderPane border = new BorderPane();
border.setTop(text);
border.setBottom(progressBar);
BorderPane.setAlignment(text, Pos.CENTER);
tabela.etapaProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.equals(ConstantesEtapa.ETAPA_OK)) {
progressBar.setProgress(1.0);//Here is the exception
}
}
});
box.getChildren().add(border);
return new SimpleObjectProperty<HBox>(box);
}
});
Tank's
I think your issue is that you cannot set a unidirectionally bound value.
Unbind the value before you try to set it.
For example:
if (newValue.equals(ConstantesEtapa.ETAPA_OK)) {
progressBar.progressProperty().unbind();
progressBar.setProgress(1.0);
}