I have a function to change an image and its opacity in a JavaFX GUI:
private static Image image = null;
private static ImageView imageView = new ImageView();
// some code to add image in GUI
public static void changeImage() {
imageView.setOpacity(0.5);
imageView.setImage(null);
}
When I call this function within the JavaFX instance, the image disappears or is changing if I use an image instead of null for setImage(). I tried calling the function by pressing a button.
In this case all works as I expected.
When I call this function from another class, the actual image will change its opacity, but the image itself is never changing. I call the function the following way:
public static void changeImg() {
Platform.runLater(() -> FX_Gui.changeImage());
}
Changing labels, progess bars... all works, but I did not manage to change an image.
There's a lot of aspects to this question that don't make sense.
Generally speaking, the GUI in JavaFX is intended to be self-contained and non-linear in it's execution. Programming an outside method to assume some state of the GUI, and then to directly manipulate the GUI based on that assumption is not the correct approach. So any attempt to know the state of the GUI by kludging in a Thread.sleep() call is inherently incorrect.
The new JFXPanel() call is not needed, as Application.launch() will initialize JavaFX. Presumably, this was added before the sleep(500) was put in, since calling changeImg() would fail if run immediately after the Thread.start() command, since the launch() wouldn't have time to even start yet.
As has been noted, having some kind of startup image that's replaced once the screen completes initialization should be done from within the FX_Min.start(Stage) method, although it's highly unlikely that you'd even see the first image.
The question seems to be aimed at designing a kind of application where the GUI is just some small part of it and the main application is going to go on to do lengthy processing and then trigger the GUI to something in response to the results of that processing. Or perhaps the main application is monitoring an external API and feeding updates to GUI periodically. In most cases, however, the GUI is usually initialized so that it can take control of the operation, launching background threads to do the lengthy processing and using JavaFX tools to handle the triggering of GUI updates and intake of results.
In the instance that the design really needs to have something other than the GUI be the central control, then use of Application does not seem appropriate. It is, after all, designed to control the Application, and monitors the status of the GUI once it's been launched to shut everything down when the GUI is closed. This is why the OP had to put the Application.launch() call in a separate thread - launch() doesn't return until the GUI shuts down.
If the application outside of the GUI is going to control everything then it's best to manually start JavaFX with Platform.startup(), and handle all the monitoring manually. The following code doesn't do any monitoring, but it does start up the GUI and change the image without any issues:
public class Control_Min {
public static void main(String[] args) {
Platform.startup(() -> new Fx_Min().start(new Stage()));
Platform.runLater(() -> Fx_Min.changeImage());
}
}
Note that no changes are required to the OP's code in Fx_Min. However, there's no reason for Fx_Min to extend Application any more, and the code from its start() method can be placed anywhere.
It should be further noted that, although this works, it's really way outside the norm for JavaFX applications. It's possible that the OP's situation really does require this kind of architecture, but that would place it into a very small minority of applications. Designing the application around Application.launch() and initiating lengthy processing in background threads through the JavaFX tools provided is almost always a better approach.
OK, so given new information from the OP it's clear that this should be based on Application and that the GUI should launch some kind of socket listener that would presumably block waiting for input.
Anything that blocks can't run on the FXAT, and there needs to be a way to allow the socket listener to communicate back to the GUI when it receives data. Ideally, the socket listener should be JavaFX unaware, and just plain Java.
IMO, the best way to do this is to provide a Consumer to accept information from the socket listener, and to pass it to the socket listener in it's constructor. That way, the GUI knows nothing about the nature of the socket listener except that it has a dependency on requiring a message consumer. Similarly, the socket listener has no knowledge about what invoked it, just that it has given it a message consumer.
This limits your coupling, and you are free to write your GUI without worrying about any of the inner workings of the socket listener, and visa versa.
So here's the GUI, cleaned up and simplified a bit so that the socket listener stuff is easier to follow. Basically, the GUI is just going to throw the message from the socket listener into a Text already on the screen. The message consumer handles the Platform.runLater() so that the socket listener isn't even aware of it:
public class Fx_Min extends Application {
#Override
public void start(Stage primaryStage) {
ImageView imageView = new ImageView(new Image("/images/ArrowUp.png"));
Text text = new Text("");
primaryStage.setScene(new Scene(new VBox(10, imageView, text), 800, 600));
primaryStage.setResizable(true);
primaryStage.show();
imageView.setImage(new Image("/images/Flag.png"));
new SocketListener(socketMessage -> Platform.runLater(() -> text.setText(socketMessage))).startListening();
}
public static void main(String[] args) {
launch(args);
}
}
Here's the socket listener. Clearly, this isn't going to listen on a socket, but it loops around a sleep() to simulate action happening on the Pi. The message format here is String, just to keep everything simple, but obviously this is the worse possible choice for an actual implementation of this. Build a special message class:
public class SocketListener {
private Consumer<String> messageConsumer;
public SocketListener(Consumer<String> messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void startListening() {
Thread listenerThread = new Thread(() -> listenForIRCommand());
listenerThread.setDaemon(true);
listenerThread.start();
}
private void listenForIRCommand() {
for (int x = 0; x < 100; x++) {
try {
Thread.sleep(5000);
messageConsumer.accept("Station " + x);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
It should be really clear that since the call to listenForIRCommand() is executed from inside a background thread, that it's completely freed from any JavaFX contstraints. Anything that generally possible in Java can be done from there without worrying about it's impact on the GUI.
In the meantime I found out that the reason for not changing the image is that I run changeImage() before the initialization of the GUI is completed. If I wait about 500 mS before I sent the changeImage() command all works fine.
Below is the minimal code that demonstrates the issue I had:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
public class Control_Min {
public static void changeImg() {
Platform.runLater(() -> Fx_Min.changeImage());
}
public static void main(String[] args) {
new Thread() {
public void run() {
Application.launch(Fx_Min.class);
}
}.start();
// JFXPanel will initialize the JavaFX toolkit.
new JFXPanel();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
changeImg();
}
}
And the Gui itself:
public class Fx_Min extends Application {
private static Stage stage;
private static GridPane rootPane;
private static Scene scene;
private static Image image = null;
private static ImageView imageView = new ImageView();
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
rootPane = new GridPane();
scene = new Scene(rootPane,800,600);
try {
image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image1.jpg"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
imageView.setImage(image);
rootPane.add(imageView, 1, 0);
stage.setScene(scene);
stage.setResizable(true);
stage.show();
System.out.println("Gui is ready");
}
public static void changeImage() {
try {
image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image2.jpg"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
imageView.setImage(image);
System.out.println("Image Changed");
}
public static void main(String[] args) {
launch(args);
}
}
This code works fine.
In the console I get:
Gui is ready
Image Changed
When I remove the Thread.sleep(500) the image will not change.
In the console I get:
Image Change
Gui is ready
My conclusion is that I have send the runlater method before the FX runtime has been initialized.
(Have not fixed the static issue yet as this was not the issue. I will do in my original program later.)
My task is the following:
I program a GUI for my internet radio player on my PC.
The GUI controls the radio and polls what is playing.
I want to control the radio by an IR remote control too.
I have already a Raspberry Pi that communicates with the remote.
Therefore, my plan is to run a server socket on the PC, that receives the commands from the Raspberry Pi.
The server will run in its own thread. I want to use the runLater command to update the GUI.
Is there a better way to update the GUI from the server?
Goal is that the GUI will update immediately when I press a button on the remote.
With my latest learnings about JavaFX I will start the application now directly in the FX class and get the server thread started from the FX class
Related
i am trying to create a turn-base card game in Vaadin-Java, everything was going well so far, but i have a problem with pushing Vaadin Images to other UI. I did copy Broadcast/BroadcasterView Class from Vaadin Documentation and it works as intended, but not for images.
public class Broadcaster {
static Executor executor = Executors.newSingleThreadExecutor();
static LinkedList<Consumer<String>> listeners = new LinkedList<>();
public static synchronized Registration register(
Consumer<String> listener) {
listeners.add(listener);
return () -> {
synchronized (Broadcaster.class) {
listeners.remove(listener);
}
};
}
public static synchronized void broadcast(String message) {
for (Consumer<String> listener : listeners) {
executor.execute(() -> listener.accept(message));
}
}
}
#Push
#Route("broadcaster")
public class BroadcasterView extends Div {
VerticalLayout messages = new VerticalLayout();
Registration broadcasterRegistration;
// Creating the UI shown separately
#Override
protected void onAttach(AttachEvent attachEvent) {
UI ui = attachEvent.getUI();
broadcasterRegistration = Broadcaster.register(newMessage -> {
ui.access(() -> messages.add(new Span(newMessage)));
});
}
#Override
protected void onDetach(DetachEvent detachEvent) {
broadcasterRegistration.remove();
broadcasterRegistration = null;
}
}
public BroadcasterView() {
TextField message = new TextField();
Button send = new Button("Send", e -> {
Broadcaster.broadcast(message.getValue());
message.setValue("");
});
HorizontalLayout sendBar = new HorizontalLayout(message, send);
add(sendBar, messages);
}
the code above works fine for Strings, Vaadin Icons etc, but when i replace for and naturally change the broadcast method, there is no reaction.
i've searched for the solution throughout the internet, but it seems, people don't need to push images or it's simply not possible here. I thought that this is perhaps the matter of payload, but it doesn't work even for 5px x 5px images
perhaps one of You have encountered such problem and found solution?
You need to pass data through the broadcaster, but what you write about your attempts makes me suspect that you've been trying to pass UI components (i.e. instances of com.vaadin.flow.component.html.Image). That won't work because a UI component instance cannot be attached to multiple locations (i.e. multiple browser windows in this case) at the same time.
What you can try is to pass the data (e.g. a String with the image URL) through the broadcaster and then let each subscriber create their own Image component based on the data that they receive.
While debugging an application I would like the main thread to wait after each Runnable I put on the JavaFX event queue using
Platform.runLater(new Runnable()... )
to wait until it has been executed (i.e. is visible). However there are two twists here:
First, it is not really a standard, GUI driven JavaFX app. It is rather a script showing and updating a JavaFX stage every now an then. So the structure looks something like this:
public static void main(String [] args){
//do some calculations
SomeView someView = new SomeView(data); //SomeView is basically a wrapper for a stage
PlotUtils.plotView(someView) //displays SomeView (i.e. the stage)
//do some more calculations
someView.updateView(updatedData)
//do some more calculations
}
public class SomeView {
private static boolean viewUpdated = false;
private ObservableList<....> observableData;
public void updateView(Data data){
Platform.runLater(new Runnable() {
#Override
public void run() {
observableData.addAll(data);
boolean viewUpdated = true;
}
});
//If configured (e.g using boolean switch), wait here until
//the Runnable has been executed and the Stage has been updated.
//At the moment I am doing this by waiting until viewUpdated has been
//set to true ... but I am looking for a better solution!
}
}
Second, it should be easy to disable this "feature", i.e. to wait for the Runnable to be executed (this would be no problem using the current approach but should be possible with the alternative approach as well).
What is the best way to do this?
E.g. is there something like a blocking version to execute a Runnable on the JavaFX thread or is there an easy way to check whether all events on the event queue have been executed/ the eventqueue is empty....?
There's also PlatformImpl.runAndWait() that uses a countdown latch so long as you don't call it from the JavaFX thread
This is based on the general idea from JavaFX2: Can I pause a background Task / Service?
The basic idea is to submit a FutureTask<Void> to Platform.runLater() and then to call get() on the FutureTask. get() will block until the task has been completed:
// on some background thread:
Runnable runnable = () -> { /* code to execute on FX Application Thread */};
FutureTask<Void> task = new FutureTask<>(runnable, null);
Platform.runLater(task);
task.get();
You must not execute this code block on the FX Application Thread, as this will result in deadlock.
If you want this to be easily configurable, you could do the following:
// Wraps an executor and pauses the current thread
// until the execution of the runnable provided to execute() is complete
// Caution! Calling the execute() method on this executor from the same thread
// used by the underlying executor will result in deadlock.
public class DebugExecutor implements Executor {
private final Executor exec ;
public DebugExecutor(Executor executor) {
this.exec = executor ;
}
#Override
public void execute(Runnable command) {
FutureTask<Void> task = new FutureTask<>(command, null);
exec.execute(command);
try {
task.get();
} catch (InterruptedException interrupt) {
throw new Error("Unexpected interruption");
} catch (ExecutionException exc) {
throw new RuntimeException(exc);
}
}
}
Now in your application you can do:
// for debug:
Executor frontExec = new DebugExecutor(Platform::runLater);
// for production:
// Executor frontExec = Platform::runLater ;
and replace all the calls to
Platform.runLater(...) with frontExec.execute(...);
Depending on how configurable you want this, you could create frontExec conditionally based on a command-line argument, or a properties file (or, if you are using a dependency injection framework, you can inject it).
I've used the code from Caprica's old Tutorial2B.java to play the whole file:
public class Tutorial2B {
private final EmbeddedMediaPlayerComponent mediaPlayerComponent;
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Tutorial2B(args);
}
});
}
private Tutorial2B(String[] args) {
JFrame frame = new JFrame("vlcj Tutorial");
mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
frame.setContentPane(mediaPlayerComponent);
frame.setLocation(100, 100);
frame.setSize(1050, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
mediaPlayerComponent.getMediaPlayer().playMedia("/home/me/sample.MP3");
}
}
However, I cannot get the following code to play my audio file for more than a split second:
public class Tutorial2B {
private final AudioMediaPlayerComponent mediaPlayerComponent;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Tutorial2B();
}
});
}
private Tutorial2B() {
mediaPlayerComponent = new AudioMediaPlayerComponent();
MediaPlayer mediaPlayer = mediaPlayerComponent.getMediaPlayer();
mediaPlayer.playMedia("/home/me/sample.mp3")
}
Any clues as to why this isn't working?
current code Dec 21st:
public class Tutorial2B {
static AudioMediaPlayerComponent mediaPlayerComponent = null;
public static void main(String[] args) {
mediaPlayerComponent = new AudioMediaPlayerComponent();
mediaPlayerComponent.getMediaPlayer().playMedia("/home/sss.mp3");
}
}
Removed the call SwingUtilities. Plays fine now.
The reason the tutorial application works and your modified audio player application doesn't is because of garbage collection.
In both examples, when you do SwingUtilities.invokeLater() and the run() method terminates, your entire application goes out of scope and becomes eligible for garbage collection. You are not keeping a reference to the application class.
In the case of the example application this OK because creating a Swing UI (the JFrame) is enough to prevent the application from being garbage collected.
In the case of your modified application, there is no Swing UI created and consequently nothing that will prevent the application being garbage collected. In fact, in this case your application will simply exit.
I am actually surprised you say removing the use of SwingUtilities fixes your problem as there is still nothing I can see that will keep your application from being garbage collected. When I see an application like this, I have seen on Linux it will exit just about immediately, and on Windows it will exit some unpredictable time later after a garbage collection has been executed.
The more robust solution is to use something like join() on the current thread to prevent the application from exiting, or somehow otherwise keep a reference to your application class pinned, and then for example to wait for a media player event of "finished" or "error" before terminating your application.
In "real" applications this sort of thing is generally not a concern since there are other things keeping your application from being garbage collected (like a UI, or some other application framework).
I'm staring at my code and I'm pretty stuck with one issue:
I want to make a fade out transition, and i want to block current thread, while the fade transition is running.
So, my attempt was to create a CountDownLatch, which blocks the thread, until the transition.setOnFinished() is called, where I make a latch.countdown(). In short: i want to make sure, that the transition is always visible in full length.
Seemed pretty straight forward to me, but...
The setOnFinished() doesn't get called, BECAUSE the current thread mentioned above is blocked by the countdown latch.
How can i solve this issue? Thx in advance.
private void initView() {
Rectangle rect = new Rectangle();
rect.widthProperty().bind(widthProperty());
rect.heightProperty().bind(heightProperty());
rect.setFill(Color.BLACK);
rect.setOpacity(0.8f);
getChildren().add(rect);
MyUiAnimation animator = new MyUiAnimation();
fadeInTransition = animator.getShortFadeInFor(this);
fadeOutTransition = animator.getShortFadeOutFor(this);
fadeOutTransition.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
Platform.runLater(new Runnable() {
#Override
public void run() {
latch.countDown();
setVisible(false);
}
});
}
});
}
public void hide() {
fadeInTransition.stop();
if (isVisible()) {
latch = new CountDownLatch(1);
fadeOutTransition.playFromStart();
try {
latch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You can only modify and query the active scene graph on the JavaFX application thread. All of your code works with the scene graph, so it all must be run on the JavaFX application thread. If everything is already running on the JavaFX application thread, there is no reason for concurrency related constructs in your code.
If you use blocking calls like latch.await(), you will block the JavaFX application thread, which will prevent any rendering, layout or animation steps to run. CountdownLatch should not be used in this context and should be removed from the code.
Calling Platform.runLater is unnecessary as it's purpose is to run code on the JavaFX application thread, and you are on the JavaFX application thread already. Platform.runLater should not be used in this context and should be removed from the code.
I've created a service and an installer. I've installed my service on the computer. In the services screen, I can see that the service is Starting. It won't stop Starting, I can't pause it, nothing. The only thing I can do is to deinstall.
I've tried attaching Visual Studio to the proces, but nothing really happens. How can I debug this service? I'd like to know what is going on.
This usually happens if you have too much/all of your code running inside your OnStart handler - you're meant to kick things off in there and then return. It's only once you return that your service is considered started.
You'd typically create one or more new Threads that run the code you want running all of the time, Start() them and then return. Or create objects that implicitly run their own threading (e.g. WCF's ServiceHost).
Then, it's your job in OnStop to shut them down gracefully - e.g. Set a ManualResetEvent and then Join on those threads.
e.g your class might look like this (not tested)
public class MyService : ServiceBase {
private ManualResetEvent _stop = new ManualResetEvent(false);
private Thread _worker;
public override void OnStart(string[] args) {
_worker = new Thread(DoStuff);
_worker.Start();
}
public override void OnStop() {
_stop.Set();
_worker.Join();
}
private void DoStuff() {
while(!_stop.WaitOne(0)) {
//Do something useful here.
}
}
}