I used to have a simple, timed (more-or-less) infinite loop:
while (!canrun) {
do_stuff(); //simple calculation
update_gui(); //updates some labels
Thread.sleep(waittime);
}
Which, naturally freezes the JavaFX-Application until it is finished with all calculations (canrun is set to false).
I replaced it with a timeline:
event = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
do_stuff();
update_gui();
}
};
keyframe = new KeyFrame(Duration.millis(waittime), event);
timeline = new Timeline(Timeline.INDEFINITE, keyframe);
do_stuff() looks like this:
public void do_stuff() {
do_the_actual_stuff();
if (stuff_finished) timeline.stop();
}
(waittime is here a number between 1 and 1000 (ms)).
On Buttonclick I start it with timeline.play(), and in do_stuff() I stop it when the calculations are done with timeline.stop().
I also have a function to change the waittime (even when it runs):
public void changewaittime() {
if (!(timeline.getStatus() == Animation.Status.RUNNING)) {
keyframe = new KeyFrame(Duration.millis(waittime), event);
timeline = new Timeline(Timeline.INDEFINITE, keyframe);
} else {
timeline.stop();
keyframe = new KeyFrame(Duration.millis(waittime), event);
timeline = new Timeline(Timeline.INDEFINITE, keyframe);
timeline.play();
}
}
And now my problem is, the Timeline only runs once, and not continuously, it doesn't even enter do_stuff() again, only if I call timeline.play() again. Even directly calling timeline.setCycleCount(Timeline.INDEFINITE) doesn't help.
Anything I missed?
Edit: I was unable to find my mistake, so I rewrote the complete GUI and now it is working.
In the Timeline constructor you are setting the frame rate using a constant meant for cycle count.
Use http://docs.oracle.com/javafx/2/api/javafx/animation/Animation.html#setCycleCount(int).
Solved the problem by rewriting my complete GUI and Timeline. Don't know why it didn't work before, but now it is working.
Related
Im trying to update a label every certain seconds, I tried using a normal Timer but since its in another thread it cannot make changes to the label, this is the Timer:
public void setTimer(Timer timer, int seconds, String userName, String content, VBox tabContent,ArrayList<Integer> countTweetsArray, Label statusLabel) {
TabContent tabContentObj = new TabContent();
timer.schedule(new TimerTask() {
#Override
public void run() {
setTweet(userName, content);
//tabContentObj.createStatusScreen(tabContent, countTweetsArray, remainingTweets);
System.out.println(content+" after "+seconds);
System.out.println("countTweetsArray: "+countTweetsArray.get(0));
statusLabel.setText(countTweetsArray.get(0).toString());
countTweetsArray.set(0, (countTweetsArray.get(0)+1));
tabContentObj.timersMap.put(userName, timer);
}
}, (seconds*1000));
}
I read that I can make periodic changes to a label using TimeLine but I cant understand how it works the keyvalues and the keyframes, Is there a way to just trigger a void method without any animation involved?
You can use the KeyFrame constructor that takes a Duration and an event handler:
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(seconds), e -> {
// code to execute here...
})
);
timeline.play();
Update: if you need a button to stop it, you can do that with
Button button = new Button("Stop");
button.setOnAction(e -> timeline.stop());
public void close(){
KeyValue opacity = new KeyValue(canvas
.opacityProperty(), 0);
KeyFrame end = new KeyFrame(Duration.millis(500),
opacity);
Timeline t = new Timeline(end);
t.play();
t.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
stage.close();
}
});
}
I have such a method, that changes opacity before quitting from app. It works fine, but during these 0.5 seconds Im still able to interact with UI. The question is how to stop everything except this Timeline execution before quitting?
Depends on how you set up your project. You could add a pane over the entire scene. Well, that doesn't prevent anyone from using the keyboard. So an alternative would be to consume all events like this:
root.addEventFilter(Event.ANY, e -> {
e.consume();
});
Call
stage.getScene().getRoot().setDisable(true);
before you start the timeline
I'm developing a game where LittleMan can move from block to block, and certain blocks are moving. I'm trying to detect when he moves to a new moving block, if that block is beneath him or if it has moved. I'm using Andengine's TimerHandler to check every 0.1 seconds but it is not working. Here's the code:
private void OnMovingBlock(final int CurrentPosRow, final int CurrentPosColumn) {
this.getEngine().registerUpdateHandler(new TimerHandler(0.1f, true, new ITimerCallback() {
#Override
public void onTimePassed(final TimerHandler pTimerHandler) {
if (LittleManPos[0] == CurrentPosRow && LittleManPos[1] == CurrentPosColumn) {
if (!LittleMan.collidesWith(MapRectangles[CurrentPosRow][CurrentPosColumn])) {
RestartScene();
}
}
}
}));
}
It seems he can move to the block and sit there with it moving in and out beneath him but it doesn't call RestartScene() UNTIL I move him again. Any idea where I am going wrong? Or is there another way to do this?
Why don't create a Timer?
TimerTask task = new TimerTask(){
#Override
public void run(){
// your code goes here
}
};
Timer timer = new Timer();
timer.sechedule(task, 0, 100); // 100 --> 0.1 second
To cancel the Timer, call timer.cancel();.
I'm currently working with two controller classes.
In Controller1 it creates a new stage that opens on top of the main one.
Stage stage = new Stage();
Parent root = FXMLLoader.load(getClass().getResource("Controller2.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
Now once that stage is open, I want it to stay open for about 5 seconds before closing itself.
Within Controller2, I've tried implementing something like
long mTime = System.currentTimeMillis();
long end = mTime + 5000; // 5 seconds
while (System.currentTimeMillis() > end)
{
//close this stage
}
but I have no idea what to put inside the while loop to close it. I've tried all sorts and nothing works.
Use a PauseTransition:
PauseTransition delay = new PauseTransition(Duration.seconds(5));
delay.setOnFinished( event -> stage.close() );
delay.play();
Doing it your way, this would work:
long mTime = System.currentTimeMillis();
long end = mTime + 5000; // 5 seconds
while (mTime < end)
{
mTime = System.currentTimeMilis();
}
stage.close();
You need to save your stage into a variable.
Maybe it is better to run that in a Thread, so that you can do something within the 5 seconds.
Another way would be to run a Thread.sleep(5000); and this would also be more performant than the while loop.
This code sets the text of a TextArea element and makes it visible for a certain amount of time. It essentially creates a pop up system message:
public static TextArea message_text=new TextArea();
final static String message_text_style="-fx-border-width: 5px;-fx-border-radius: 10px;-fx-border-style: solid;-fx-border-color: #ff7f7f;";
public static int timer;
public static void system_message(String what,int set_timer)
{
timer=set_timer;
message_text.setText(what);
message_text.setStyle("-fx-opacity: 1;"+message_text_style);
Thread system_message_thread=new Thread(new Runnable()
{
public void run()
{
try
{
Thread.sleep(timer);
}
catch(InterruptedException ex)
{
}
Platform.runLater(new Runnable()
{
public void run()
{
message_text.setStyle("-fx-opacity: 0;"+message_text_style);
}
});
}
});
system_message_thread.start();
}
This solution is completely general. You can change the setStyle methods to any code that you want. You can open and close a stage if you like.
I have a method in Swing to hide and show some buttons, called setScreen(int stage), which will hide or not certain buttons depending on the stage parameter. I want to call the method, then wait a few seconds and then call it again, like in this code:
... //Here stage has been assigned to some value
setScreen(stage);
if (stage != STAGE_TWO) {
sleep(WAIT_TIME * 1000);
stage = WELCOME;
setScreen(stage);
}
The code for setScreen(int stage) is something like this:
void setScreen(int stage) {
switch (stage) {
case WELCOME:
screen.infoLabel.setText("Welcome!");
screen.startButton.setVisible(true);
break;
case STAGE_TWO:
screen.infoLabel.setText("We are in stage two!");
screen.startButton.setVisible(false);
break;
}
screen.validate();
}
Where screen is an instantiation of a class extending JFrame.
The problem here is that the first setScreen(stage) is never displayed, since the thread goes to sleep before the changes have been commited. I have tried substituting the sleep for a while loop checking the time of the system, but the effect is the same.
**EDIT: ** I have found in a recommended StackOverflow thread some information on Swing Timer that may be useful. I'll work with it and upload any useful advances I make.
You are sleeping event dispatch thread. Don't do it and use:
javax.swing.Timer timer = new javax.swing.Timer(WAIT_TIME * 1000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (stage != STAGE_TWO) {
stage = WELCOME;
setScreen(stage);
}
}
});
timer.start();
The if condition may be for entire timer as per your requirement