I am a beginner in javaFX and am stuck in this one area. Any help will be appreciated a lot.
This is sample app I have made for clear understanding using scene builder. There is a text area and a button.I want to set data into the text area on the button click. The setting happens in another thread.
The code is as follows:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class TpController{
#FXML
private ScrollPane scrollPane;
#FXML
private Button button;
#FXML
public TextArea txtArea ;
private Stage stage;
public void setTextArea(TextArea txt)
{
this.txtArea = txt ;
}
public TextArea getTextArea()
{
return txtArea;
}
public void setStage(Stage stage)
{
this.stage = stage;
}
public Stage getStage()
{
return stage;
}
public void setTopText(String text) {
// set text from another class
txtArea.setText(text);
}
public void buttonHandler()
{
tpThread t = new tpThread();
t.start();
}
The tpThread class is as follows:
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
public class tpThread extends Thread {
#Override
public void run() {
// TODO Auto-generated method stub
FXMLLoader loader = new FXMLLoader(getClass().getResource("Justtp.fxml"));
try {
Parent root = (Parent) loader.load();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
TpController myController = loader.getController();
TextArea t = myController.getTextArea();
String data = "hi\nhello\nhow are you\nnice to meet you\nhahaha";
//System.out.println(t.setData("hi"));
myController.setTopText(data);
}
Instead of using setTopText, i have also directly used
t.setText(data);
But no use. My final output does nothing on the button click.
There are many issues with your code.
Modifications to the active scene graph off of the JavaFX application thread must be performed via Platform.runLater().
You don't need another thread to accomplish something on a button click.
You can just define an action handler for the button using setOnAction().
Event handler methods on controllers can also be cross-referenced in FXML via onAction="#handleButtonAction" where handleButtonAction is defined as a public void handleButtonAction(ActionEvent event) method in your controller.
All event handlers are invoked by the JavaFX runtime on the JavaFX application thread, so you don't need to worry about multi-threading when writing event handlers - the JavaFX event handling programming model is single threaded.
Loading an FXML as you do in your code and not attaching the resultant node to a scene is pointless as the user will never see anything that is not attached to a scene.
There may be other issues with your code which cause it not to work as you expect.
In general, for assistance debugging an issue, provide an mcve. Note it should be both minimal and complete so that somebody could copy and paste the code to replicate the issue (and pretty much nothing else).
Related
I'm new guy here :)
I have a small problem which concerns binding in JavaFX. I have created Task which is working as a clock and returns value which has to be set in a special label (label_Time). This label presents how many seconds left for player's answer in quiz.
The problem is how to automatically change value in label using the timer task? I tried to link value from timer Task (seconds) to label_Time value in such a way...
label_Time.textProperty().bind(timer.getSeconds());
...but it doesn't work. Is it any way to do this thing?
Thanks in advance for your answer! :)
Initialize method in Controller class:
public void initialize(URL url, ResourceBundle rb) {
Timer2 timer = new Timer2();
label_Time.textProperty().bind(timer.getSeconds());
new Thread(timer).start();
}
Task class "Timer2":
public class Timer2 extends Task{
private static final int SLEEP_TIME = 1000;
private static int sec;
private StringProperty seconds;
public Timer2(){
Timer2.sec = 180;
this.seconds = new SimpleStringProperty("180");
}
#Override protected StringProperty call() throws Exception {
int iterations;
for (iterations = 0; iterations < 1000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
// From the counter we subtract one second
sec--;
//Block the thread for a short time, but be sure
//to check the InterruptedException for cancellation
try {
Thread.sleep(10);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
}
}
return seconds;
}
public StringProperty getSeconds(){
return this.seconds;
}
}
Why your app does not work
What is happening is that you run the task on it's own thread, set the seconds property in the task, then the binding triggers an immediate update of the label text while still on the task thread.
This violates a rule for JavaFX thread processing:
An application must attach nodes to a Scene, and modify nodes that are already attached to a Scene, on the JavaFX Application Thread.
This is the reason that your originally posted program does not work.
How to fix it
To modify your original program so that it will work, wrap the modification of the property in the task inside a Platform.runLater construct:
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
}
});
This ensures that when you write out to the property, you are already on the JavaFX application thread, so that when the subsequent change fires for the bound label text, that change will also occur on the JavaFX application thread.
On Property Naming Conventions
It is true that the program does not correspond to JavaFX bean conventions as Matthew points out. Conforming to those conventions is both useful in making the program more readily understandable and also for making use of things like the PropertyValueFactory which reflect on property method names to allow table and list cells to automatically update their values as the underlying property is updated. However, for your example, not following JavaFX bean conventions does not explain why the program does not work.
Alternate Solution
Here is an alternate solution to your countdown binding problem which uses the JavaFX animation framework rather than the concurrency framework. I prefer this because it keeps everything on the JavaFX application thread and you don't need to worry about concurrency issues which are difficult to understand and debug.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountdownTimer extends Application {
#Override public void start(final Stage stage) throws Exception {
final CountDown countdown = new CountDown(10);
final CountDownLabel countdownLabel = new CountDownLabel(countdown);
final Button countdownButton = new Button(" Start ");
countdownButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent t) {
countdownButton.setText("Restart");
countdown.start();
}
});
VBox layout = new VBox(10);
layout.getChildren().addAll(countdownLabel, countdownButton);
layout.setAlignment(Pos.BASELINE_RIGHT);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
class CountDownLabel extends Label {
public CountDownLabel(final CountDown countdown) {
textProperty().bind(Bindings.format("%3d", countdown.timeLeftProperty()));
}
}
class CountDown {
private final ReadOnlyIntegerWrapper timeLeft;
private final ReadOnlyDoubleWrapper timeLeftDouble;
private final Timeline timeline;
public ReadOnlyIntegerProperty timeLeftProperty() {
return timeLeft.getReadOnlyProperty();
}
public CountDown(final int time) {
timeLeft = new ReadOnlyIntegerWrapper(time);
timeLeftDouble = new ReadOnlyDoubleWrapper(time);
timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(timeLeftDouble, time)
),
new KeyFrame(
Duration.seconds(time),
new KeyValue(timeLeftDouble, 0)
)
);
timeLeftDouble.addListener(new InvalidationListener() {
#Override public void invalidated(Observable o) {
timeLeft.set((int) Math.ceil(timeLeftDouble.get()));
}
});
}
public void start() {
timeline.playFromStart();
}
}
Update for additional questions on Task execution strategy
Is it possible to run more than one Task which includes a Platform.runLater(new Runnable()) method ?
Yes, you can use multiple tasks. Each task can be of the same type or a different type.
You can create a single thread and run each task on the thread sequentially, or you can create multiple threads and run the tasks in parallel.
For managing multiple tasks, you can create an overseer Task. Sometimes it is appropriate to use a Service for managing the multiple tasks and the Executors framework for managing multiple threads.
There is an example of a Task, Service, Executors co-ordination approach: Creating multiple parallel tasks by a single service In each task.
In each task you can place no runlater call, a single runlater call or multiple runlater calls.
So there is a great deal of flexibility available.
Or maybe I should create one general task which will be only take data from other Tasks and updating a UI?
Yes you can use a co-ordinating task approach like this if complexity warrants it. There is an example of such an approach in in Render 300 charts off screen and save them to files.
Your "Timer2" class doesn't conform to the JavaFX bean conventions:
public String getSeconds();
public void setSeconds(String seconds);
public StringProperty secondsProperty();
is there a way how to create javafx.scene.control.Tooltip in thread which is not "JavaFX Application Thread"?
Because of performance I prepare some visual elements in one thread and keep them in memory. And when I need them I use Application Thread for showing them. But new Tooltip() seems to require Application Thread only.
is there a way how to create javafx.scene.control.Tooltip in thread which is not "JavaFX Application Thread"?
For JavaFX 2.2 - No.
See this JavaFX issue tracker tiecket: Tooltip of Tab (and other controls) cannot be set of FXApplicationThread.
There is a suggested workaround in the ticket to create the Tooltip in a Platform.runLater call.
package fxthread_25127_wrongthread;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.Tooltip;
import javafx.stage.Stage;
/**
*
* #author mickf
*/
public class Main extends Application {
Tooltip tooltip;
public static void main(String[] args) {
Application.launch(args);
}
#Override public void init() throws Exception {
Platform.runLater(new Runnable() {
#Override public void run() {
tooltip = new Tooltip("Top tip : use runLater()");
}
});
/*
* this will cause an ISE
*/
//tooltip = new Tooltip("flip");
}
#Override public void start(Stage s) {
System.exit(0);
}
}
A description of how Platform.runLater works from it's javadoc:
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater.
so i'm trying to set up an application where i have multiple panels inside a jframe. lets say 3 of them are purely for display purposes, and one of them is for control purposes. i'm using a borderLayout but i don't think the layout should really affect things here.
my problem is this: i want the repainting of the three display panels to be under the control of buttons in the control panel, and i want them to all execute in sync whenever a button on the control panel is pressed. to do this, i set up this little method :
public void update(){
while(ButtonIsOn){
a.repaint();
b.repaint()
c.repaint();
System.out.println("a,b, and c should have repainted");
}
}
where a,b, and c are all display panels and i want a,b,and c to all repaint continously until i press the button again. the problem is, when i execute the loop, the message prints in an infinite loop, but none of the panels do anything, ie, none of them repaint.
i've been reading up on the event dispatch thread and swing multithreading, but nothing i've found so far has really solved my problem. could someone give me the gist of what i'm doing wrong here, or even better, some sample code that handles the situation i'm describing? thanks...
The java.util.concurrent package provides very powerful tools for concurrent programing.
In the code below, I make use of a ReentrantLock (which works much like the Java synchronized keyword, ensuring mutually exclusive access by multiple threads to a single block of code). The other great thing which ReentrantLock provides are Conditions, which allow Threads to wait for a particular event before continuing.
Here, RepaintManager simply loops, calling repaint() on the JPanel. However, when toggleRepaintMode() is called, it blocks, waiting on the modeChanged Condition until toggleRepaintMode() is called again.
You should be able to run the following code right out of the box. Pressing the JButton toggle repainting of the JPanel (which you can see working by the System.out.println statements).
In general, I'd highly recommend getting familiar with the capabilities that java.util.concurrent offers. There's lots of very powerful stuff there. There's a good tutorial at http://docs.oracle.com/javase/tutorial/essential/concurrency/
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RepaintTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel()
{
#Override
public void paintComponent( Graphics g )
{
super.paintComponent( g );
// print something when the JPanel repaints
// so that we know things are working
System.out.println( "repainting" );
}
};
frame.add( panel );
final JButton button = new JButton("Button");
panel.add(button);
// create and start an instance of our custom
// RepaintThread, defined below
final RepaintThread thread = new RepaintThread( Collections.singletonList( panel ) );
thread.start();
// add an ActionListener to the JButton
// which turns on and off the RepaintThread
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
thread.toggleRepaintMode();
}
});
frame.setSize( 300, 300 );
frame.setVisible( true );
}
public static class RepaintThread extends Thread
{
ReentrantLock lock;
Condition modeChanged;
boolean repaintMode;
Collection<? extends Component> list;
public RepaintThread( Collection<? extends Component> list )
{
this.lock = new ReentrantLock( );
this.modeChanged = this.lock.newCondition();
this.repaintMode = false;
this.list = list;
}
#Override
public void run( )
{
while( true )
{
lock.lock();
try
{
// if repaintMode is false, wait until
// Condition.signal( ) is called
while ( !repaintMode )
try { modeChanged.await(); } catch (InterruptedException e) { }
}
finally
{
lock.unlock();
}
// call repaint on all the Components
// we're not on the event dispatch thread, but
// repaint() is safe to call from any thread
for ( Component c : list ) c.repaint();
// wait a bit
try { Thread.sleep( 50 ); } catch (InterruptedException e) { }
}
}
public void toggleRepaintMode( )
{
lock.lock();
try
{
// update the repaint mode and notify anyone
// awaiting on the Condition that repaintMode has changed
this.repaintMode = !this.repaintMode;
this.modeChanged.signalAll();
}
finally
{
lock.unlock();
}
}
}
}
jComponent.getTopLevelAncestor().repaint();
You could use SwingWorker for this. SwingWorker was designed to perform long running tasks in the background without blocking the event dispatcher thread. So, you need to extend SwingWorker and implement certain methods that will make sense to you. Note that all long running action should happen in the doInBackground() method, and the Swing UI elements should be updated only on the done() method.
So here is an example :
class JPanelTask extends SwingWorker<String, Object>{
JPanel panel = null;
Color bg = null;
public JPanelTask(JPanel panel){
this.panel = panel;
}
#Override
protected String doInBackground() throws Exception {
//loooong running computation.
return "COMPLETE";
}
#Override
protected void done() {
panel.repaint();
}
}
Now, in your "control" button's action performed event, you could do the following :
controlButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
JPanelTask task1 = new JPanelTask(panel1);
task1.execute();
JPanelTask task2 = new JPanelTask(panel2);
task2.execute();
//so on..
}
});
Another way is using javax.swing.Timer. Timer helps you to fire a change to your ui elements in a timely fasthion.This may not be the most appropriate solution. But it gets the work done too.
Again you should be careful about updating UI elements in right places.
import com.sun.lwuit.Button;
import com.sun.lwuit.Command;
import com.sun.lwuit.Display;
import com.sun.lwuit.Label;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.layouts.BorderLayout;
import com.sun.lwuit.plaf.UIManager;
import com.sun.lwuit.util.Resources;
import java.io.IOException;
public class Ruwwa extends javax.microedition.midlet.MIDlet
implements ActionListener{
Form f;
Button mybutton1;
Button mybutton2;
Command exit;
Command ok;
public void startApp() {
Display.init(this);
f = new Form();
try {
Resources r = Resources.open("/mairuwa.res");
UIManager.getInstance().setThemeProps(r.getTheme("Mairuwa Theme"));
} catch (IOException ioe) {
ioe.printStackTrace();
}
mybutton1=new Button("Report A Problem");
mybutton2=new Button("Request Info");
f.setLayout(new BorderLayout());
f.addComponent(BorderLayout.CENTER, new Label("The Mairuwa Portal"));
ok = new Command("OK");
exit = new Command("Exit");
f.addCommand(ok);
f.addCommand(exit);
f.addCommandListener(this);
f.show();
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void actionPerformed(ActionEvent ae) {
notifyDestroyed();
}
}
I would like to add another label under the "The Mairuwa Portal" and also place two buttons ("Report A Problem","Request Information") beneath this as well. An illustration of what I am describing is
label: The Mairuwa Portal
then another label beneath it: I want to:
Then two buttons beneath this Button:Report Problem Button: Request Information
I have been able to add OK and EXIT button to the project,but this above buttons I talked about should as I described.
These buttons will carry functionality. I hope this can be done in LWUIT.
You need to include all JSR's when compiling a LWUIT application in the IDE. LWUIT doesn't require them all to run but requires 184, 226, MMAPI & file connector to compile. This is causing your verification error.
I would recommend developing with the Sun/Oracle simulators and using the more device like emulators for QA.
The exception you got means your application was built incorrectly, see that Ruwwa is in the jar file that was produced by your build. If not fix your build.
I'm trying to play background audio in a loop in a JavaFX 2.0 application using JavaFX SDK 2.0.1. I decided to use a MediaPlayer created by the following piece of code:
MediaPlayerBuilder
.create().media(BACKGROUND_MEDIA)
.cycleCount(MediaPlayer.INDEFINITE);
This basically works, but when a new cycle starts there is a tiny (latency?) gap between the end and the start of the audio. So it's not a working option for me since it's not playing a clean loop.
I decided to build a new MediaPlayer object and start playback everytime Media ends. This works fine so far. Additionally, I use a button playing a short AudioClip when clicked.
I discoverd that frequent and fast clicking this button leads to interrupts in the background audio. I created an example to reproduce this behaviour by inifinitely playing an AudioClip with volume 0 when the button is clicked once. The example is not self contained, since the required audio files are missing. It requires to place 2 audio files in the project's source directory:
click.wav (a really short click sound ~300ms)
background.wav (~5 seconds of audio)
How do I achieve playing a clean audio loop in background without these interrupts when other one-shot audio sounds are played? Is it just a performance issue?
Example:
package mediatest;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayerBuilder;
import javafx.stage.Stage;
public class MediaTest extends Application {
private static final AudioClip CLICK_AUDIOCLIP = new AudioClip(MediaTest.class.getResource("/click.wav").toString());
private static final Media BACKGROUND_MEDIA = new Media(MediaTest.class.getResource("/background.wav").toString());
private MediaPlayerBuilder builder;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 250);
this.builder = MediaPlayerBuilder
.create()
.media(BACKGROUND_MEDIA)
.onEndOfMedia(new Runnable() {
public void run() {
MediaPlayer player = MediaTest.this.builder.build();
player.play();
}
});
MediaPlayer player = this.builder.build();
player.play();
Button btn = new Button();
btn.setText("Repeat playing short audio clip");
btn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
//Simulation of many button clicks
MediaTest.CLICK_AUDIOCLIP.setCycleCount(AudioClip.INDEFINITE);
MediaTest.CLICK_AUDIOCLIP.play(0);
}
});
root.getChildren().add(btn);
primaryStage.setScene(scene);
primaryStage.show();
}
}
have you looked into ExecutorService? You would then have a number of predefined threads like so:
ExecutorService service = Executors.newFixedThreadPool(4);
where 4 is the number of threads it makes.
It will improve performance because it uses already made threads rather than making a new one each time you want to run something.
You would create a Runnable and execute it with the service like so:
Runnable r = new Runnable() {
#Override
public void run() {
playSound();
}
};
service.execute(r);
Not only would this improve performance but it automatically assigns the job to a not-currently-busy thread in its thread pool.
Also look at this: Playing sound loops using javafx which I believe solves your small latency problem.
EDIT: damn sorry, I didn't know this post was that old. It was a top result in google.
Using another thread at
public void handle(ActionEvent event) {
Platform.runLater(new Runnable()
{
#Override
public void run()
{
//Simulation of many button clicks
MediaTest.CLICK_AUDIOCLIP.setCycleCount(AudioClip.INDEFINITE);
MediaTest.CLICK_AUDIOCLIP.play(0);
}
});
}
});
May solve the problem of the interruption