How do I close a stage in JavaFX 2 after some specific external event has occurred? Suppose I have a stage with a simple progress bar that is filled up by a Task (borrowed from another answer):
Task<Void> task = new Task<Void>(){
#Override
public Void call(){
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
updateProgress(i, 10);
}
return null;
}
};
How do I close the window automatically (and open the next one) after the Task is done and the ProgressBar is filled to 100%?
Before return null; you can add
Platform.runLater(
new Runnable() {
public void run() {
stage.close();
}
}
);
or
progressBar.progressProperty().addListener(new ChangeListener<Number>(){
//add checking, that progress is >= 1.0 - epsilon
//and call stage.close();
})
The first is better. But note, that task is done on a separate thread. so you should put a request on stage.close() on JFX thread using special call.
Also, jewelsea provides links on stage closing questions in comment to the question.
Related
I am new to learn multi-thread programming. I am told that thread - unsafe problem is always caused by something shared across multi thread. That makes sense for me, however, that seems can not explain the issue in below code which appears nothing is shared across multi thread.
package test;
public class Outputer{
public void output(){
String name = "123456789";
int len = name.length();
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
package test;
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
Outputer outputer = new Outputer();
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 50; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output();
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 50; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output();
}
}
}).start();
}
}
what I expected is that 123456789 should be seen intact. But sometimes, I can see the output in console as below.
... ...
123456789
123456789
123456789
123456789
123456789
123456789 // expected
112323456789 // unexpected
456789 // unexpetecd
123456789
123456789
123456789
123456789
123456789
... ...
I understand the root cause is that when one thread is executing code snippet below, its cpu time segment is over so thread is not able to finish execution. Another thread get cpu time segment then start to execute below code snippet but also possible to not finish the execution. then first thread again get cup time segment then continue to execute from where it was stopped.
In a word, I am aware that the root cause is below code snippet is not Atomic operation.
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
My fix is to surround with synchronized block as below. Now it reaches my expectation. Looks good.
synchronized(this) {
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
However, I still have some doubts which is currently haunted my mind. Somebody help !!!
Is the statement below true ? Always ?
the thread - unsafe problem is ALWAYS caused by something shared across my multi thread
I am asking because I don't see any data shared across threads in my example. The variable name is local variable, not a pass-in parameter or pass-out parameter (returned parameter). So name is thread safe.
If the statement is true, what is shared by threads ?
If the statement is false, any other situation can caused thread unsafe without sharing data across threads ?
QUESTION
I've noticed that draw() cycle is interrupted by events elaboration.
In the following example the circle animation will stop at mouse click until the elaborating_function() ends.
void setup(){
size(800, 600);
background(#818B95);
frameRate(30);
}
void draw(){
background(#818B95);
//simple animation
fill(0,116,217);
circle(circle_x, 200, 50);
circle_x += animation_speed;
if(circle_x>800){ circle_x = 0; }
}
void mouseClicked() {
elaborating_function();
}
void elaborating_function(){
println("elaboration start");
delay(1000);
println("elaboration end");
}
Of course, a simple solution to run the elaboration without stopping the animation could be to thread("elaborating_function");
But my question is: if it is possible to run the draw cycle into an independent thread instead?
SOLUTION
I've found a possible solution inverting my problem and creating an "independent cycle" parallel to the draw one. Within this cycle is possible to run any function and it will not interfere with the draw execution. Every event triggered by the user needs only to set a specific variable in order to activate (once or more time) the function within the cycle.
int circle_x = 0;
int animation_speed = 5;
boolean call_elaborating_function = false;
void setup(){
size(800, 600);
background(#818B95);
frameRate(30);
IndependentCycle independentCycle = new IndependentCycle();
independentCycle.setFrequency(1);
new Thread(independentCycle).start();
}
void draw(){
background(#818B95);
//simple animation
fill(0,116,217);
circle(circle_x, 200, 50);
circle_x += animation_speed;
if(circle_x>800){ circle_x = 0; }
}
public class IndependentCycle implements Runnable{
private int frequency; //execution per seconds
public IndependentCycle(){
frequency = 0;
}
public void setFrequency(int frequency){
this.frequency = 1000/frequency;
println(this.frequency);
}
public void run(){
while(true){
print(".");
delay(this.frequency);
//DO STUFF HERE
//WITH IF EVENT == ture IN ORDER TO RUN JUST ONCE !!
if(call_elaborating_function){
call_elaborating_function = false;
elaborating_function();
}
}
}
}
void mouseClicked() {
call_elaborating_function = true;
}
void elaborating_function(){
println("elaboration start");
delay(1000);
println("elaboration end");
}
As far as I know Processing has it's own AnimationThread.
Your proposed solution to thread elaborating_function() is great.
You could have a basic class that implements Runnable if you need a bit more control. With this thread running in parallel, Processing's main animation thread should run along side it just fine without pausing rendering.
This options sounds much simpler than trying to mess with Processing's AnimationThread and potentially have to deal with unexpected behaviour.
What is the actual goal you're trying achieve ?
I have a loading gif for all backend requests. Prior to Charm 5.0.0, it worked fine in which the loading gif would show, backend would finish what it needed to, then the loading gif would be hidden. Now, the loading gif shows, but it doesn't hide.
addLayerFactory(LOADING_GIF, () -> new Layer() {
private final Node root;
private final double sizeX = getGlassPane().getWidth();
private final double sizeY = getGlassPane().getHeight();
{
ProgressIndicator loading = new ProgressIndicator();
loading.setRadius(50);
loading.setStyle("-fx-text-fill:white");
root = new StackPane(loading);
root.setStyle("-fx-background-color: rgba(0,0,0,0);");
getChildren().add(root);
this.setStyle("-fx-background-color:rgba(255,255,255,0.7)");
this.setShowTransitionFactory(v -> {
FadeInTransition ft = new FadeInTransition(v);
ft.setRate(2);
return ft;
});
}
#Override
public void show() {
this.setBackgroundFade(0.0);
super.show();
Layer pane = this;
Task<Integer> task = new Task<Integer>() {
#Override
protected Integer call() throws Exception {
int iterations = 0;
int max = DataService.readOutTime / 1000;
while (iterations <= max) {
Thread.sleep(1000);
iterations++;
}
Platform.runLater(new Runnable() {
#Override
public void run() {
if (pane.isVisible()) {
pane.setShowTransitionFactory(v -> {
FadeOutTransition ft = new FadeOutTransition(v);
ft.setRate(2);
return ft;
});
pane.hide();
MobileApplication.getInstance().showMessage("There was an error in sending your data.");
}
}
});
return iterations;
}
};
Thread thread = new Thread(task);
thread.start();
}
#Override
public void hide() {
this.setBackgroundFade(0.0);
super.hide();
}
#Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate((getGlassPane().getWidth() - sizeX) / 2, (getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
});
I have a couple of utility methods that show and hide the loader:
public void showLoader() {
MobileApplication.getInstance().showLayer(App.LOADING_GIF);
}
public void hideLoader() {
MobileApplication.getInstance().hideLayer(App.LOADING_GIF);
}
Interestingly, the custom timeout I created (to hide the loader in case there is a stall in the backend) doesn't hide the layer either.
There is an issue with your code: you are overriding Layer::layoutChildren, but you are not calling super.layoutChildren().
If you check the JavaDoc:
Override this method to add the layout logic for your layer. Care should be taken to call this method in overriden methods for proper functioning of the Layer.
This means that you are getting rid of some important parts of the Layer control, such as animations, events and visibility control.
This should work:
#Override
public void layoutChildren() {
super.layoutChildren();
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(sizeX, sizeY);
resizeRelocate(getGlassPane().getWidth() - sizeX) / 2, getGlassPane().getHeight() - sizeY) / 2, sizeX, sizeY);
}
On a side note, for the hide transition, you should use setHideTransitionFactory.
So this is what I have done to solve this. From the Gluon Docs on the hide() method:
If this layer is showing, calling this method will hide it. If a hide transition is present, it is played before hiding the Layer. Care should be taken to call this only once LifecycleEvent.SHOWN has been fired.
Thus, I was realizing that the response from the backend was coming before the layer was fully shown. Thus, I modified the overridden hide() method as follows:
#Override
public void hide() {
if (this.isShowing()) {
this.setOnShown(e -> {
this.setBackgroundFade(0.0);
super.hide();
});
} else {
super.hide();
}
}
So if the layer is still in LifecycleEvent.SHOWING mode when being told to hide, make sure that it hides when it is shown. Otherwise it is already shown so hide it.
I have a TextArea that I would like to be able to append characters or words to over a period of time. I use Timer from java.util and when I run application in Eclipse everthing works ok, but when I export application into .jar I have performance issue.
Here is video from Eclipse:
http://pl.tinypic.com/r/4ftw1f/8
Here is .jar:
http://pl.tinypic.com/r/6zmoon/8
And code:
#FXML
private TextArea textarea;
public void start(KeyEvent keyEvent)
{
if (keyEvent.getCode() == KeyCode.ENTER)
{
new Timer().schedule(
new TimerTask() {
int i;
#Override
public void run() {
textarea.appendText("hey" + i + "\n");
i++;
}
}, 0, 500);
}
}
Your code has threading issues: in Java 8 it will just throw IllegalStateExceptions as you are trying to update the UI from a background thread. You need
if (event.getCode() == KeyCode.ENTER)
{
new Timer().schedule(
new TimerTask() {
int i;
#Override
public void run() {
String message = "hey"+i+"\n";
Platform.runLater(() -> textArea.appendText(message));
i++;
}
}, 0, 500);
}
I don't know if that will fix your performance issue or not. Appending text to a text area essentially involves doing lots of string concatenation; eventually (as the text in the text area gets long) this is going to be prohibitive. You might want to use a virtualized control (such as ListView), depending on the functionality you need.
I need to send a server request about once per minute, to get a new products list (in case it was changed via web).
So, i'm using DispatcherTimer
public static void Start()
{
if (timer != null) return;
timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(0.1)};
timer.Tick += Run;
timer.Start();
}
private static async void Run(object sender, EventArgs e)
{
timer.Interval = TimeSpan.FromSeconds(60); // TODO" add dynamic changes here
timer.Stop();
** Do stuff
timer.Start();
}
However, sometimes, i need to force updating. Is it correct to run
public static void ForceUpdate()
{
Run(null, null);
}
EDIT: i mean, if Do stuff is long enough, wouldn't it be called second time via timer? Or maybe i should use something else for this kind of job?
EDIT: Insert a variable which should store the last update time and check if update had been done in a certain interval.
Ah, well, it is quite simple
public static void ForceUpdate()
{
timer.Stop();
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Start();
}