Update Slider position CPU Load Javafx - java

Good day,
I try to level my JavaFx skills and I am programming a little media player.
Hence, there is a slider object which will be responsible to show the user the elapsed time (and seek the track position if the user wants to).
To monitor the current track position I use another thread which will ask for the elapsed time each 100 ms. It updates a DoubleProperty, which the slidervalue will be binded to.
This works, however CPU usage increases to 70-80 % if I do so.
Snippet:
Task task = new Task<Void>() {
#Override public Void call() throws InterruptedException {
double time=0;
double dummy=0;
while(true) {
try{
if(time == tmp.getPlaytime()){
dummy += 0.1;
}else{
dummy = time;
}
updateProgress(dummy, tracklength.getValue());
time = tmp.getPlaytime();
Thread.sleep(100);
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
};
trackposition.bind(task.progressProperty());
new Thread(task).start();
tmp is the object which will provide the elapsed time, and the method can raise an exception, hence the try/catch block.
tracklength is a DoubleProperty which provides the Length of the track in seconds.
Since I only have a time resolution of a second within the method in tmp I use the dummy variable to make a smooth slider animation.
The idea for this thread - approach, was taken from Oracle Tutorial
I know that multithreading and GUI is a difficult topic, but do you have any idea how drop the CPU usage ?
(Will the runLater() approach work better in your opinion ? Since runLater means I have no idea when it will be executed, I can not provide a "smooth" transition and wouldn't "want" to use it then )
Thank you

I've created this short sample to test your task, and it's working flawlessly, no relevant CPU usage.
private volatile boolean running = true;
#Override
public void start(Stage primaryStage) {
final Media song = new Media(<Media URL>);
MediaPlayer media_player = new MediaPlayer(song);
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws InterruptedException {
double time=0;
double dummy=0;
while(running) {
try{
if(time == media_player.getCurrentTime().toSeconds()){
dummy += 0.1;
}else{
dummy = time;
}
updateProgress(dummy, media_player.getTotalDuration().toSeconds());
time = media_player.getCurrentTime().toSeconds();
Thread.sleep(100);
}
catch(Exception e){
System.out.println(e.getMessage());
running=false;
}
}
return null;
}
};
Slider slider = new Slider(0,100,0);
slider.valueProperty().bind(task.progressProperty().multiply(100));
media_player.setOnReady(() -> {
new Thread(task).start();
media_player.play();
});
StackPane root = new StackPane();
root.getChildren().add(slider);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
Try to run it, and check if it works, and how is the CPU usage.

Related

Timer in JavaFX updating Label (Bindings)

I want to add a clock to my application that tells how long you have been doing the task. To simplify it, I have included a counter that increments every second in a new thread and update the label 'setTimer' with the counter number. For this I have a label fx:id="setTimer" in my .fxml file and imported it into my class.
#FXML
private Label setTimer;
And created another class in my class that extends the thread TimerTask and increments the counter by one on each call. Created a new Object 'text', which should always be updated with the current value of the counter.
SimpleStringProperty text = new SimpleStringProperty("undefined");
public class MyTask extends TimerTask {
#Override
public void run() {
counter++;
text.set(Integer.toString(counter));
}
}
To have this class called every second I created a timer in the initialize method and set it to one second.
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
MyTask myTask = new MyTask();
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(myTask, 0 , 1000);
setTimer.textProperty().bind(text);
}
At the moment I get the exception 'Not on FX application thread; currentThread = Timer-0'.
I've tried many ways to solve my problem, but I haven't gotten to the right point.
My idea of ​​what I want to do should be clear, and I would be happy if someone could help me.
My Problem is to update the changes of the counter in the GUI.
It doesn't have to be solved the way I thought it would, just need a tip on how to best implement it.
Thank you
Ok, my comments are too long. This is how I would try to do it.
Start the stopwatch on the application being loaded
Create a new thread that launches itself every so often.
Inside there, get the time from the Stopwatch in seconds (sw.getTime(TimeUntis.seconds)). Convert that to hours and minutes if you want like shown in this SO post
Then, write the time to the UI using Platform.runLater(new Runnable(){ /* access ui element and write time here */ });
Using Platform.runLater() in a background thread is kind of a messy kludge that should probably be avoided. JavaFX has mechanisms to handle this kind of thing which you should use. Specifically, Task<> is designed to allow background threads to update data which is connected to JavaFX screen elements which need to be updated on the FXAT.
You CAN do what you're trying to do with a JavaFX Task, but using the Java Timer inside of it seems impossible, since there doesn't seem to be any way for a Java thread to wait on a Timer to complete. So, instead I've used a "for" loop with a sleep to do the same thing. It's clumsy, but it does demonstrate how to connect partial results from a Task to screen display:
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer1(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer1 extends VBox {
public Timer1() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
Task<Integer> timerFxTask = new Task<>() {
{
updateValue(0);
}
#Override
protected Integer call() throws Exception {
for (int counter = 0; counter <= 1000; counter++) {
sleep(1000);
updateValue(counter);
}
return 1000;
}
};
stopButton.setOnAction(stopEvt -> timerFxTask.cancel());
time.textProperty().bind(Bindings.createStringBinding(() -> timerFxTask.getValue().toString(),
timerFxTask.valueProperty()));
Thread timerThread = new Thread(timerFxTask);
timerThread.start();
});
}
}
But there is a better way to do what you're trying to do, which is essentially an animation - and JavaFX has a facility to do exactly this. Usually, people use animations to morph the appearance of JavaFX screen elements, but you can also use it to animate the contents of a Text over time as well. What I've done here is create an IntegerProperty which can be transitioned from a start value to an end value interpolated linearly over time and then bound that value to the TextProperty of a Text on the screen. So you see it update once per second.
public class Sample1 extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Timer2(), 300, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
public class Timer2 extends VBox {
public Timer2() {
Text time = new Text();
Button startButton = new Button("Start");
Button stopButton = new Button("Stop");
getChildren().addAll(time, startButton, stopButton);
startButton.setOnAction(startEvt -> {
IntegerProperty counter = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1000), new KeyValue(counter, 1000)));
stopButton.setOnAction(stopEvt -> timeline.stop());
time.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(counter.get()), counter));
timeline.play();
});
}
}

How to run some code repeatedly after an interval that can changes?

I'm using a thread in JavaFX to repeat my code after an interval (initially 1s), but I want to be able change the interval that the thread is using to 500ms or 333ms based on user choice (I have a button in a menu bar to change for each choice). I did tried things like shutDown() if the user clicks on one of the buttons and initiate it again with the new value, but didn't work. Any ideas?
Here's the relevant part of my code:
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(() -> {
//refresh users, line and "guiche"
updateFila(usuarios, guiches, fila);
updateGuiche(guiches, fila, graphicsContext);
turno++;
//ends the code after the end of the line
if (done) {
exec.shutdown();
}
}, 0, 1000, TimeUnit.MILLISECONDS); //This is interval that I need to change after user choice
I know that I'm executing scheduleAtFixedRate() right now, but it was just to see if the logic was fine.
Additionally, I need to pause, resume and reset the thread, all based on user click.
You could use a Timeline to execute a event handler every second and set the rate at which the animation runs to the number of times the update should happen per second, i.e. 2 or 3...
In the following example I use 5 instead of 3 for a more recognizable effect:
#Override
public void start(Stage primaryStage) {
Line line = new Line(25, 125, 125, 125);
Rotate rotate = new Rotate(0, 125, 125);
line.getTransforms().add(rotate);
ToggleButton btn = new ToggleButton();
btn.textProperty().bind(Bindings.when(btn.selectedProperty()).then("5 Hz").otherwise("2 Hz"));
StackPane.setAlignment(btn, Pos.BOTTOM_LEFT);
// rotate by one 60th of a full rotation each time
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), evt -> rotate.setAngle((rotate.getAngle() + (360d / 60d)) % 360)));
timeline.setCycleCount(Animation.INDEFINITE);
// rate depends on button state
timeline.rateProperty().bind(Bindings.when(btn.selectedProperty()).then(5d).otherwise(2d));
Pane linePane = new Pane(line);
linePane.setMinSize(250, 250);
linePane.setMaxSize(250, 250);
StackPane root = new StackPane();
root.getChildren().addAll(linePane, btn);
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
timeline.play();
primaryStage.show();
}
The binding is simply an example of setting the update frequency. You could of course use different means to assign this value, e.g.
ComboBox<Duration> combo = new ComboBox<>();
Duration initial = Duration.seconds(1);
combo.getItems().addAll(initial, Duration.seconds(1/3d), Duration.seconds(1/2d));
combo.setValue(initial);
combo.valueProperty().addListener((observable, oldValue, newValue) -> timeline.setRate(1/newValue.toSeconds()));
If you use only single thread you can create your own implementation based on classic thread.
public class Worker extends Thread {
private static final Logger logger = Logger.getLogger(Worker.class);
private volatile int delayInSec = 1;
private CountDownLatch latch;
private final int STARTED = 0;
private final int STOPPED = 1;
private volatile int state = STOPPED;
public Worker(){}
#Override
public void run() {
logger.debug("enter to execution method");
while (!isInterrupted()) {
// stop if needed (it's additional feature)
if (state == STOPPED) {
logger.debug("stopped and locked");
try {
latch = new CountDownLatch(1);
latch.await();
} catch (InterruptedException e) {
logger.warning("got interruption while waiting for action ", e);
break;
}
logger.debug("awake");
}
// do your stuff here
try {
// then delay
logger.debug("go to sleep for %s sec.",delayInSec);
latch = new CountDownLatch(1);
latch.await(delayInSec, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.warning("got interruption while waiting for action ", e);
break;
}
}
logger.debug("exit from execution method");
}
public void startJob(){
state = STARTED;
logger.debug("started");
if (latch!=null)
latch.countDown();
}
public void stopJob(){
state = STOPPED;
logger.debug("stopped");
}
public void shutdown(){
logger.debug("shutdown");
interrupt();
}
public void changeDelay(int delayInSec) {
logger.debug("set new delay %s", delayInSec);
this.delayInSec = delayInSec;
}
}

Run Handler Task in Android successively

atm i have a problem with my actual Android application.
For explanation:
At first i want to show a Text in a TextView Char by Char. This is my actual Code for this
tvIntro.setText("");
final Handler textHandler = new Handler();
for(int i=0; i<intro.length();i++){
final int finalCount = i;
textHandler.postDelayed(new Runnable() {
#Override
public void run() {
tvIntro.setText(tvIntro.getText() + (intro.charAt(finalCount)+""));
}
}, 150 * i);
}
After the whole text is displayed, i want to play a sound and continuously change the Color of the Screen for 5 Seconds. For this, my code is:
myBackground.setBackgroundColor(Color.RED);// set initial colour
final Thread blink = new Thread(new Runnable() {
public void run() {
while (getRunning()) {
try {
Thread.sleep(100);
if(start[0] !=1){
mp.start();
start[0] = 1;
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
updateColor(myBackground);
whichColor = !whichColor;
}
}
});
private void updateColor(final RelativeLayout myBackground) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (whichColor)
myBackground.setBackgroundColor(Color.RED);
else
myBackground.setBackgroundColor(Color.GREEN);
}
});
}
All the functions are working, but i want too finish the first handler, before the second handler is executed. Furthermore the Second handler should stop after x seconds.
I have some problems understanding how the handler and the Threads work.
Would be nice if someone of you have a solution for me.
To delay the performing of tasks until a specified thread (or threads) finishes, add this line immediately after the thread you wish to wait on:
myThread.join();
And then immediately follow it with the code you wish to run after it finishes.
For your second problem, you can set a variable to be the value (in milliseconds) for the amount of time you want to wait before ending the thread, and then decrease that value by 100 (or whatever amount you choose to tell it to sleep for) each time the code runs. Have a check for the value to be less than or equal to zero, and then if that condition returns true, end the thread with an interrupt. So basically:
long timeToRun = 5000, sleepTime = 100;
// Your code here...
Thread.sleep(sleepTime);
timeToRun -= sleepTime;
if(timeToRun <= 0) {
myThread.interrupt();
}
There are likely more graceful ways to accomplish this, but at the very least this should solve your problems.

JProgressBar not working properly

So my JProgressBar I have set up doesn't work the way I want it. So whenever I run the program it just goes from 0 to 100 instantly. I tried using a ProgressMonitor, a Task, and tried a SwingWorker but nothing I tried works.
Here is my program:
int max = 10;
for (int i = 0; i <= max; i++) {
final int progress = (int)Math.round(
100.0 * ((double)i / (double)max)
);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
jProgressBar2.setValue(progress);
}
});
}
#MadProgrammer Here is my attempt at making a swing worker and writing each name to the document and updating the progress bar. The program gets to around 86 percent and stops, never creating the finished document. The program creates a blank document. Here are the two methods first is the SwingWorker object I made:
public class GreenWorker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
int max = greenList.size();
XWPFParagraph tmpParagraph;
XWPFRun tmpRun;
FileInputStream file =
new FileInputStream(location + "GreenBandList.docx");
gDocument = new XWPFDocument(OPCPackage.open(file));
for (int i = 0; i < max; i++) {
tmpParagraph = gDocument.getParagraphs().get(0);
tmpRun = tmpParagraph.createRun();
if (greenList.get(i).length() == 1) {
tmpRun.setBold(true);
tmpRun.setText(greenList.get(i));
tmpRun.setBold(false);
} else {
tmpRun.setText(greenList.get(i));//Write the names to the Word Doc
}
int progress = Math.round(((float) i / max) * 100f);
setProgress(progress);
}
return null;
}
}
And here is the code for the button that starts it and has my property change event.
private void GenerateGreenList() throws IOException, InterruptedException {
//Need to fix the bug that removes the Letter Header in Yellow Band list
//********************************************************************\\
//Delete the old list and make a new one
File templateFile = new File(location + "\\backup\\GreenTemplate.docx");
FileUtils.deleteQuietly(new File(location + "GreenBandList.docx"));
FileUtils.copyFile(templateFile, new File(location +
"GreenBandList.docx"));
//Get the New Entries
String[] entries = jTextPane3.getText().split("\n");
for (String s : entries) {
if (s != null) {
greenList.add(s);
}
}
//Resort the list
Collections.sort(greenList);
//Write the names to the document
GreenWorker worker = new GreenWorker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
jProgressBar2.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
if (worker.isDone()) {
try {
gDocument.write(new FileOutputStream(new File(location + "GreenBandList.docx")));
////////////////////////////////////////////////////////////
} catch (IOException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
JOptionPane.showMessageDialog(null, "Green Band List Created!");
jProgressBar2.setValue(0);
}
}
I used the property change listener from one of your other posts but I don't really understand what the one you wrote does or what it does in general?
Swing is a single threaded environment, that is, there is a single thread which is responsible for processing all the events that occur within the system, including repaint events. Should anything block this thread for any reason, it will prevent Swing from processing any new events, including, repaint events...
So all this ...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex); }
jProgressBar2.setValue(progress);
}
});
Is constantly pausing the Event Dispatching Thread, preventing it from actually doing any updates (or at least spacing them randomly)...
It's also likely that your outer loop is been run from within the context of the EDT, meaning that until it exists, nothing in the Event Queue will be processed. All your repaint requests will be consolidated down to a single paint request and voila, instant filled progress bar...
You really should use a SwingWorker - I know you said you tried one, but you've not shown any code as to your attempt in this regards, so it's difficult to know why it didn't work, however...
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and dual welding JProgressBar example
SwingWorker and JProgressBar example
And forgive me if we haven't said this a few times before :P
You are evoking Thread.sleep inside the EvokeLater which means that it is running on another thread than your for loop. i.e., your for loop is completing instantaneously (well, however long it takes to loop from 1 to 100, which is almost instantaneously).
Move Thread.sleep outside of EvokeLater and it should work as you intend.
int max = 10;
for (int i = 0; i <= max; i++) {
final int progress = (int)Math.round(
100.0 * ((double)i / (double)max)
);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
jProgressBar2.setValue(progress);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
}
Edit: agree with #MadProgrammer. It appears this is just an illustrative question, but you should make sure whatever you're trying to accomplish here you use a SwingWorker for.

Screen Sometimes Blank at Game Start

I've been working on a simple game engine. All was going pretty well, and I got the foundation finished. By that, I mean, the game sets up with user defined display settings, initializes stuff, and starts the game loop. Everything works great, except I have an issue in both Windowed mode and full screen:
In Windowed mode, sometimes the screen will appear blank, until I resize or minimize the window.
In full screen, I only get a blank screen if I use the default Display resolution. Most of the other resolutions appear just fine.
Here is some code relevant to displaying the screen:
This is performed when the use presses the play button on the display settings dialog
public void actionPerformed(ActionEvent e) {
DisplayMode dm = modes[resolutionBox.getSelectedIndex()];
boolean fs = fullscreenBox.getSelectedItem().equals ("Fullscreen");
boolean vs = vSyncCheckBox.isSelected();
game.initScreen (dm, fs, vs);
runGame (game);
f.dispose();
}
});
private final void runGame(EGame g)
{
Thread t = new Thread (g);
t.start();
}
This is the run method in the EGame class
public final void run() {
start();
isRunning = true;
gameLoop();
endGame();
}
The user of the engine will call the method setGameCanvas() in the start() method. Other things can go in the start() method, but for my test games, it just that method:
protected final void setGameCanvas(EGameCanvas c)
{
screen.remove (canvas);
canvas = c;
canvas.addKeyListener (keyboard);
screen.add (canvas);
}
And finally, here is the gameLoop:
private final void gameLoop()
{
// Final setup before starting game
canvas.setBuffer (2);
screen.addKeyListener (keyboard);
canvas.addKeyListener (keyboard);
int TARGET_FPS = screen.getTargetFps();
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
long lastLoopTime = System.nanoTime();
long now = 0;
long lastFpsTime = 0;
long updateLength = 0;
double delta = 0;
int fps = 0;
while (isRunning)
{
now = System.nanoTime();
updateLength = now - lastLoopTime;
lastLoopTime = now;
delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength;
fps++;
if (lastFpsTime >= 1000000000)
{
screen.setFps (fps);
lastFpsTime = 0;
fps = 0;
}
keyboard.poll();
keyboardEvents();
update (delta);
render();
try {
Thread.sleep( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 );
}
catch (Exception ex) {}
}
}
Now, keep in mind, neither of my issues existed, until I added the run method and ran it in a separate thread. I can about guarantee it's a threading issue.
But, I'm no expert, so any advice would be appreciated.
I believe the issue is that you're trying to modify GUI-related components outside of the event dispatch thread - that is the only thread allowed to modify the graphical interface, otherwise you might encounter issues like this.
To check this try if the following solves the issue:
try {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeAndWait( new Runnable() {
#Override
public void run() {
// Do rendering and other stuff here...
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
If that helps, I'd recommend taking a look on the Concurrency in Swing tutorial.

Categories