Controlling JavaFX GUI from Other Methods? - java

Basically, I'm creating a program that syncs HUE lights, and I'm having trouble incorporating the Listener that detects when the light bridge has been connected with my JavaFX GUI; I want it to switch a Label from "Not Connected" to "Connected" whenever the listener detects that it has connected.
Here's some pseudocode of how the program is structured.
public class MainClass extends Application {
boolean connected;
Label label;
public static void main(){
launch(args); //Neccesary to start JavaFX
}
public static void start(){
ConnectToHueLights(); //Takes abt 30s to connect to bridge
Label label = “Searching for connection”; //Message while connecting
Window.addLabel(); //Adds label to hue lights
Window.show(); //Makes window visible
}
private HueLightsListener(){
//Once connected, can do whatever inside of a void method inside of this Listener
private void onConnectionResponds(){
label = “Connected”
connected = true;
}
}
public void ConnectToHueLights(){
create new Listener();
}
Basically, the label doesn't change whenever the listener is active, and I'm not sure how to do that.
Thanks!

Use a suitable Worker to establish the connection to the bridge. Choose Task for a single unit of work; choose Service to manage multiple tasks. Use the task's updateMessage() method to notify the worker's message property listeners. You can pass a reference to the update method to your HueLightsListener, as shown here.
HueLightsListener listener = new HueLightsListener(this::updateMessage);
Your implementation of onConnectionResponds() can then tell the reference to accept() messages as needed.
public void onConnectionResponds() {
updater.accept("Connected");
…
}
As an aside, your implementation of call(), which runs in the background, can periodically poll the connection, while checking isCancelled(), and then send more commands once connected.

Related

javafx show progressindicator while loading [duplicate]

This question already has an answer here:
JavaFX indeterminate progress bar while doing a process
(1 answer)
Closed 7 years ago.
This is the main window of my application:
I want to be able to update the window, so i wrote a method reset which ist triggered by pressing F5:
public void reset() {
final ProgressIndicator pi = new ProgressIndicator(-1.0f);
final HBox hb = new HBox(pi);
final VBox vb = new VBox(hb);
hb.setAlignment(Pos.CENTER);
vb.setAlignment(Pos.CENTER);
scene.setRoot(vb);
//this.show();
}
if i run this it looks sth like this:
However, if I uncomment the line //this.show(); this will be executed:
public void show() {
final VBox box = API_CALL_AND_BUILDING_BOX_MAGIC_WHICH_I_STRIPPED;
gp.getChildren().clear();
gp.getChildren().add(box);
scene.setRoot(gp);
stage.sizeToScene();
}
and I will never be able to see my ProgressIndicator because the application will just hang until the APIcall is finished and the new content is loaded.
I tried a bit with Threads and Platform.runLater() but I cant get it to work. My goal is to display the ProgressIndicator until the APIcall is finished, the box is build and the scene gets given gp as the new root.
I hope its somewhat understandable what my goal and my problems are :)
You need to create another JVM thread dedicated to fetch data from the remote source. The main thread of your application must never block with network operations: in the keyboard event handler you just repaint the main window with the progress indicator and then wait for another event, that can be:
background operation finished (successful, unsuccessful)
user interrupts (mouse/keyboard)
a timeout expires
Pseudocode with ListenableFuture by Guava
// Initialize in ctor use MoreExecutors
private final ListeningExecutorService service;
// Keep to call .cancel()
private ListenableFuture<APIData> currentRequest;
// Run in the GUI thread
public void onRefreshEvent() {
showProgressIndicator();
currentRequest = service.submit(/* Some Callable<APIData> */);
Futures.addCallback(currentRequest , this, this);
}
// From FutureCallback<T>
public void onFailure(Throwable t) {} // TODO
public void onSuccess(APIData d) {} // TODO
// From Executor
public void execute(Runnable cmd) {
Platform.runLater(cmd);
}
Don't forget to consider socket connect and read timeout, as well as pooling HTTP connection! Also consider if the background task should check the interrupted flag

Building Multithreading ProgressBar in JavaFx

I am attempting to build a progress bar that is being updated as my application is retrieving and populating data to the GUI. I figured that the progress bar will be reused a lot so I decided to create its own class. However, I don't believe I understand either the Worker/Task or Multi-Threading in general to create a re-usable situation. What would be the recommended approach to creating a progress bar that can listen to the application thread and update the bar accordingly. Here is my attempt:
// Simple Progress Bar View as Pop Up
public class ProgressIndicatorUtil{
#FXML
private ProgressBar progressBar;
#FXML
private Label statusLabel;
#FXML
private Button closeButton;
#FXML
private Label valueLabel;
private Worker worker;
private Stage stage;
public void setPopUpStage(Stage stage) {
this.stage = stage;
}
public void setWorker(Worker worker) {
this.worker = worker;
}
public void setLinkToMainPage(Object controller) {
((Task<String>) worker).setOnSucceeded(event -> stage.close());
((Task<String>) worker).setOnCancelled(event -> {
closeButton.setVisible(true);
stage.requestFocus();
statusLabel.setTextFill(Color.RED);}
);
valueLabel.textProperty().bind(Bindings.format("%5.1f%%", worker.progressProperty().multiply(100)));
progressBar.progressProperty().bind(worker.progressProperty());
statusLabel.textProperty().bind(worker.messageProperty());
}
#FXML
private void handleClose(ActionEvent e){
stage.close();
}
}
The Controller that calls the View Pop-Up and runs the Progress Bar Thread.
public class MyController{
//Controller calling the view and disabling the main GUI
private void loadProgressBar(Worker worker){
try{
FXMLLoader loader = new FXMLLoader(getClass()
.getClassLoader().getResource("main/resources/fxml/ProgressBar.fxml"));
AnchorPane pane = (AnchorPane)loader.load();
Stage popUpStage = new Stage();
popUpStage.initModality(Modality.WINDOW_MODAL);
Scene scene = new Scene(pane);
popUpStage.setScene(scene);
ProgressIndicatorUtil controller = loader.getController();
controller.setPopUpStage(popUpStage);
controller.setWorker(worker);
controller.setLinkToMainPage(this);
mainPane.setDisable(true);
popUpStage.showingProperty().addListener((obs, hidden, showing) -> {
if(hidden) mainPane.setDisable(false);});
popUpStage.show();
}catch(IOException e){
e.printStackTrace();
}
}
private void runProgressBar(Worker worker) {
new Thread((Runnable) worker).start();
}
//A user action that runs the progress bar and GUI
#FXML
private void aBigProcessingEvent(ActionEvent event) {
Worker worker = new Task<String>(){
#Override
protected String call() throws Exception {
updateProgress(0, 3);
updateMessage("Clearing Data");
processingEvent01();
updateProgress(1, 3);
updateMessage("Retriving Data And Adding To List");
processingEvent02();
updateProgress(2, 3);
updateMessage("Populating Data");
processingEvent03();
updateProgress(3, 3);
return "Finished!";
}
};
loadProgressBar(worker);
runProgressBar(worker);
}
}
The program works fine, visually, but it throws an Exception like this (Not On FX Application Thread) and running Platform.runLater() on my "processingEvent" methods will cause my progress bar to be 100% immediately, but it won't throw anymore Exceptions. Any suggestion to how to split the application modification methods and the worker methods apart while keeping the progression connected to the processingEvent methods? Much thanks.
There is nothing wrong with the (incomplete) code you have posted, so there errors are in other parts of your code. Since the code is incomplete, I have to make some educated guesses as to what is happening. (Note: it is actually much better if you can create complete examples when you post questions, so that you ensure the cause of the issue you are asking about is included.)
Since you are getting an IllegalStateException "Not on the FX Application Thread", you must be updating the UI from a background thread. Since the only code you've posted that runs in a background thread is in the Task you create in aBigProcessingEvent(), the UI updates must be happening in the one or more of the processingEventXX() methods you haven't shown.
If you wrap the calls to processingEventXX() in Platform.runLater(), then you won't see any progressive updates to the progress bar. Platform.runLater() schedules the runnable you provide to be executed on the FX Application Thread and exits immediately. There is no other code in the Task that takes time to run, so the entire task is completed in very little time, and by the time the FX Application Thread renders the next frame, the task is complete and the progress property is at 1.
So presumably your processingEventXX() methods take time to execute, and also update the UI. You must wrap the calls that update the UI in those methods in Platform.runLater(...). The code wrapped in Platform.runLater(...) must not include code that takes a long time to run. I.e. they should look like
private void processingEvent01() {
// some long-running process here...
Platform.runLater(() -> {
// update UI here....
});
// some other long-running process here (perhaps)
}

passing EventListener over RMI

I need to run a task inside a RMI service and it needs an event listener implemented for it. Right now when I pass the EventListener over RMI it does execute the call however the callback method of the Listener is not being invoked and it remains in the wait state. How should I get this to work?
public class MyEventListener implements Serializable, ABCEventListener {
private static final long serialVersionUID = -4686421592620210489L;
private boolean registrationCompleted = false;
public boolean getRegistrationCompleted(){
return registrationCompleted;
}
#Override
public void onSomethingDiscovered(Agent agent) {
System.out.println("Added agent "+agent.toString()+" to the set \n");
}
#Override
public void onDiscoveryComplete() {
this.registrationCompleted = true;
System.out.println("Discovery process completed. \n");
}
}
Here is where I pass the eventlistener to the RMI service 'ds'
MyEventListener myEL = new MyEventListener();
ds.discoverAsync(val, myEL);
waitForRegistration();
.
.
private void waitForRegistration() {
try{
while(!dcev.getRegistrationCompleted()){
System.out.println("Please wait...");
Thread.sleep(15000);
}
}catch(InterruptedException e){
logger.error("InterruptedException raised while waiting for registration",e);
e.printStackTrace();
}
}
The problem is that because your event listener is serializable the data fields of your event listener get sent across the wire and a new object created on the server side. The method is called on this copy of your event listener. This makes perfect sense for data objects, but for the likes of event listeners doesn't work as you want your client code to get the call.
I believe you can make this work if your event listener extends RemoteObject. If you do this, instead of your object being copied, it will be exposed as an RMI service when you call the server. The server instead of getting a copy of your object, will get a proxy to your event listener. The call to the event listener will result in an RMI call in the reverse direction to call your event listener.
See Passing Remote Objects in the RMI guide for more details.

Updating GUI from a runnable

I'm building a Swing application and one part of the functionality should be to process and output some text visually and audibly (using Mary TTS). I need some advice on the best way for the GUI and text processing classes to communicate.
The GUI class is a subclass of JPanel. Within that I have a class implementing Runnable, called LineProcesser, which prepares the text to be dispatched to an audio player. I'm using a thread executor to keep this off the EDT (that may not be the best way but it seems to achieve the result I'm after).
My intention is for LineProcessor to run through all the text and update a JTextArea at the end of each line. Additionally it will need to halt and wait for user input at certain points. After the user input has been completed the GUI class should tell it to resume processing.
The following code illustrates what I currently have:
public class MyPanel extends JPanel {
ExecutorService lineExecutor = Executors.newSingleThreadExecutor();
Runnable lineProcessor = new LineProcessor();
public class LineProcessor implements Runnable {
private int currentLineNo = 0;
public LineProcessor() {
// ...
}
#Override
public void run() {
// call getText();
// call playAudio();
currentLineNo++;
}
}
}
private JButton statusLbl = new JLabel();
private JButton mainControlBtn = new JButton();
private void mainControlBtnActionPerformed(ActionEvent evt) {
if (mainControlBtn.getText().equals("Start")) {
lineExecutor.submit(lineProcessor);
mainControlBtn.setText("Running");
}
}
}
How can LineProcessor notify GUI components that they need to change and how can it be paused and restarted from within the GUI? I'm confused as to whether I need a Swing Worker, property/event listeners or something else? The examples I've read sort of make sense but I can't see how I can apply them to the code I have here.
All you need to do is wrap any Swing calls in a Runnable, and queue it on the EDT via SwingUtilities.invokeLater(myRunnable);. That's it. No need for a SwingWorker.
e.g.,
public class LineProcessor implements Runnable {
private int currentLineNo = 0;
Runnable LineProcessor = new LineProcessor(); // won't this cause infinite recursion?
public LineProcessor() {
// ...
}
#Override
public void run() {
// call getText();
// call playAudio();
currentLineNo++;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// *** Swing code can go here ***
}
});
}
}
You will have to use both SwingWorker and Event methodology.
Place your long running code in Swing Worker.
Create new property change Event, listener, manager
In your SwingWorker, when the change event occurs, call PropertyChangeManager to notify all the liseners.
All GUI components which wants to be notified with the event should register themselves with the PropertyChangeManager.
Your PropertyChangeManager will call customProperyChange method of the PropertyChangeListener and will pass the properyChangeEvent
What you are looking for is a SwingWorker. This class allows to perform the work on a worker thread, having periodical updates on the EDT, and in the end update the EDT as well.
Several examples are available on SO and in the Swing tutorials. Just a few links
The 'How to use progress bars tutorial', which happens to update a text area as well
The 'Concurrency in Swing' tutorial, a must-read if you start with SwingWorker
The SwingWorker javadoc
Reporting progress can be done with the publish method, these results will be passed to the process method in which you can update the UI. At the end, the done method is called allowing you to perform some final UI updates.
For the pause/restart functionality ... you can use an invokeAndWait in the doInBackground method with a blocking method call (for example showing a JOptionPane asking for user input). But if you start using invokeAndWait in the doInBackground it might be overkill to use the SwingWorker and you can simply opt for the approach #Hovercraft Full Of Eels suggested

How to open new jFrame dialogue and return to main interface?

I tried this
private void botaoConfIOMouseClicked(java.awt.event.MouseEvent evt) {
ConfigurarIO popup = new ConfigurarIO();
popup.setVisible(true);
botaoConfIO.setEnabled(false); //this line to avoid multiple dialogues
setIO=popup.getConfig(); //i need to get this boolean from the dialogue "ConfigurarIO"
//part of the program only to make my logic from the setIO
if(setIO[0]==false){
jToggleButton1.setEnabled(false);
jToggleButton1.setText("Saída");
}
else{
jToggleButton1.setEnabled(true);
if(jToggleButton1.isSelected()) jToggleButton1.setText("Pino 1 ON");
else jToggleButton1.setText("Pino 1 OFF");
}
}
And this is the dialogue
public class ConfigurarIO extends javax.swing.JFrame {
boolean[] inOut=new boolean[8];
boolean ok=false;
/** Creates new form ConfigurarIO */
public ConfigurarIO() {
initComponents();
}
public boolean[] getConfig(){
return inOut;
}
public boolean getOK(){
return ok;
}
public void setOK(){
ok=false;
}
//the logic was emited
private void botaoOKMouseClicked(java.awt.event.MouseEvent evt) {
dispose();
ok=true;
System.out.println(ok);
}
The problem is that the setIO is not modified by the second interface and, If I set this to make a loop broken only by the "ok" boolean, the window with the setting interface doesn't open. This is a very explored problem but I am new to Netbeans and I couldn't find it on Google. Thanks for the attention
Print screen: http://4.bp.blogspot.com/-B7VWmPelJek/T2ysJV8PJcI/AAAAAAAABqQ/0waWxxEEHkw/s320/temp.png
You haven't said whether a frame is required for some reason, or whether a dialog would do, or whether whatever it is needs to be modal.
The reason the frame doesn't show up if you loop is that you're on the Swing dispatch thread (since you are in a routine that responded to a mouse click), and until it returns, it isn't going to update the screen.
You cannot just call a method on the "frame dialog" to get a value until you know that the dialog has set the value. I would pass my calling class to the dialog as a parameter on the constructor, and then have the dialog code call a method on the calling class when it's all done. If you need to know when this happens, then you'll have to treat it as an event in your calling class; I can't guess what you need for that without knowing more about what you're trying to do overall.
If you need to wait until the dialog is done, and don't need the user to be able to do anything until it is done, then what you want is a "modal" dialog, and I recommend looking at JOptionPane and its various dialog options for what you want to do. THEN the call from your class can be synchronous, i.e., you can call the dialog and, when the call completes, the dialog is all done. Then you don't need to pass the calling class to the frame, since it doesn't need to notify you that it's done; you know it's done when your call completes, and you can call a method such as you have already done to get the value that you want.
Incidentally, your subclass-of-JFrame constructor doesn't call super(); I recommend you do that...
rc
// we will make this modal=true, to block access to the parent frame
public class ConfigurarIO extends javax.swing.JDialog {
For more details, see:
How to Make Dialogs
How to Use Modality in Dialogs

Categories