JavaFX Update GUI Before Entering Another Thread - java

EDIT:
I noticed that my question was linked to another. While our goals are similar, the other question's set up is different: they are creating all the GUI aspects of the program within the main class of their program, they are also setting the trigger event of the button press within the start method. Therefore the solution of using the "setOnAction(event->" coupled with Task works. It is a single class program, I was able to make the solution work if I created a new, single class program, this application does not work for me for my situation.
In my set up I am not running this event out of the main class, but out of the Controller class that is linked to my FXML and I have the event that triggers the method already defined. I did not post my entire Controller class as that seemed unnecessary. If there is a way to make the linked question's solution work for my different set up, or a link for guidance that would be stellar. I have looked into the "task" set up, taking from the linked question, but so far have not been able to get it to work successfully as pictured below:
#FXML
private void goForIt(ActionEvent event)
{
kickTheBaby();
}
private void kickTheBaby()
{
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
statusFld.setOnAction(event -> {statusFld.setText("Running");
Task task = new Task<Void>() {
#Override
public Void call()
{
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
return null;
}
};
task.setOnSucceeded(taskFinishEvent -> statusFld.setText(statusFld.getText()
+ "All done time to sleep..."));
new Thread(task).start();
});
}
I have a program in Java8 using FXML that downloads and parses data. I wish to make the program update the GUI TextField (called "statusFld" here) to say "Running" when I click the start button. Below is the method in the controller that should be responsible for this series of events.
#FXML
private void goForIt(ActionEvent event)
{
statusFld.setText("Running!");
java.util.Date now = calendar.getTime();
java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(now.getTime());
(new Thread(new EmailCommunication("", currentTimestamp, "START"))).start();
(new Thread(new DataGathering2())).start();
}
However, when I attempt to run the program the GUI does not visually update and goes straight into the other two threads. So I attempted to utilize the "Platform.runLater()" methodology in one of the other threads by passing the status field to it as so:
Platform.runLater(() ->
{
statusFld.setText("Running!");
});
But after 20 minutes it had not given a visual update to the GUI. My guess is that this is probably due to the sheer amount of data processing that I am having it do, so who knows what "later" will actually be in this case.
My question is how can I be sure that the GUI visually updates before moving on to the other, very processing intense, threads?
Thank you!

Related

JavaFX controller, implementing non-graphic events and background state machine

I started learning about JavaFX a short time ago and I am trying to switch from Swing to JavaFX. I ran into some logic implementation problem that I cannot think of a solution with JavaFX that I easily solved using Swing.
The application that I developed is huge, containing multiple already developed software modules, that interact with the graphics at some point. For example, in the application I have a smart card reader that, when a card is read on that reader and the operator is authenticating with a smart card, it displays on the graphic that a valid card is read, it display a green card icon and lets the operator enters his password. There are multiple drivers like the smart card reader and all of them generate events also with their status, are they working or not. In the current solution all modules communicate with central main software that can call functions for the Swing graphics.
The application starts with initializing a page, and when all of the devices are working and there is no error, I am showing the first page of the application. If any of them has an error, I am showing the error page. I designed some fxml and connect them with their own controller. In the controller of the initializing page in the method should look something like this:
#Override
public void initialize(URL url, ResourceBundle rb) {
if(no_error){
go to first page
}else{
go to out of order page
}
}
The first thing that I want to implement is to wait, because some of the drivers and devices won't work instantly, for example wait for 10 cycles with timeout of 1 second on each of them.
#Override
public void initialize(URL url, ResourceBundle rb) {
while (true) {
if (no_error) {
go to first page
} else {
if (timeout_expired) {
go to out of order page
} else {
wait
increase timeout
}
}
}
}
I know that purpose of the initialize method is not for this and the above code is not a solution, I am looking more for a function like doInBackground from the AsyncTask.
Also, in this application in the controller, I want to implement events that are not graphic related like the reading of the smart card. How to connect the event from the driver for the smart card, when it reads card data to send that data to a function implemented in the controller like the one below?
public void controller_smart_card_read(SmartCard smart_card){
//Check if valid card from DB
//Display result
}
Also, in some scene I want to implement an inactivity event. If there are no events for a longer period of time (both graphical and from the devices), go back to the first page for example.
To summarize this, is there a way a controller is accessed and triggered from an independent software module, and is there a way to implement a doInBackground() function while scene and controller is up and running?
Create a background thread to do this functionality and use the Platform.runLater to update the UI.
For Example
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
ScheduledExecutorService scheduledExecutorService;
ScheduledFuture<?> scheduledCheck;
public void start(Stage base) {
base.setOnCloseRequest(
scheduledExecutorService.shutdownNow();
);
scheduledExecutorService = Executors.newScheduledThreadPool(1);
Runnable doBackground = () -> {
//Do background tasks i.e. check card reader
if(devices_ready && successful_card_read)
Platform.runLater(() -> {
//Update Javafx UI
});
}
//scheduleAtFixedRate(Runnable function, wait time before starting runnable, cycle time, timeunit)
//the below thread will wait 10 seconds, then execute the doBackground every 1 second
scheduledCheck = scheduledExecutorService.scheduleAtFixedRate(doBackground,10,1, TimeUnit.SECONDS);
}

Vaadin Spring Security

I have been working from an example I found, here's the link to the Git repo:
https://github.com/basakpie/vaadin8-spring-security-sample
It works great, it's just what I need, except for one thing: I need Server Push.
Here's what I've done so far:
added the Vaadin Push dependency
added the following lines to the start of the MainUI.init() method:
getPushConfiguration().setTransport(Transport.WEBSOCKET);
getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
Added the following fields to the MainUI class:
Label time = new Label();
Timer timer;
Added the following method to the MainUI class:
private void updateTime() {
access(() -> time.setValue(String.format("The server-side time is %s", LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")))));
}
Finally, added the following to the end of the MainUI.init() method:
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
updateTime();
}
}, 1000L, 1000L);
It mostly works. I am able to see the current system time updating every second. But when I hit refresh in the browser, the application just hangs with the vaadin loading spinner. There are no error messages.
I have tried the following alternatives:
Adding the method
public void attach() {
getPushConfiguration().setTransport(Transport.WEBSOCKET);
getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
}
and removing the getPushConfiguration lines from init()
This solves the hanging problem, but the push does not work - no errors, just the time is not displayed at all.
I also tried adding a #Push annotation to MainUI. This results in the same behaviour as before - freezing on refresh.
How can I fix this? Any suggestions would be welcome.
Try out the below procedure:
Add #Push to the MainUI.java file
#Push(transport = Transport.WEBSOCKET,value = PushMode.AUTOMATIC)
Instead of :
getPushConfiguration().setTransport(Transport.WEBSOCKET);
getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
Add #PreDestroy to exit the timer when you navigate from the mainUI
#PreDestroy
void destroy() {
timer.cancel();
}
Find the complete revised code HERE
https://gist.github.com/cansoftinc/351452ee0e616d353519f147c4a961ba
I Had exactly the same problem and my solution to this was to implement the vaadin servlet. Check this for more information.

can I control the refresh rate of a Java listener?

I'm struggling with this issue since some days ago and I'm not able to find a solution.
I have a listener which receives market data (orders at bid and ask). If market is quiet (pre-market or post-market (low volatility)) everything works fine. But once the market is open the listener receives events too fast. So after a couple of minutes my app freezes.
Right now the listener only assigns the received data to a var.
orderBookBid.getBuyOrders().addListener(new ObservableListModelListener<Order>() {
#Override
public void modelChanged(final Change<? extends Order> change) {
System.out.println("bid event");
bidChange = change.getSource();
}
});
The program only freezes when uses real data. When market is closed and uses test data from a local file works fine.
Is there any way to set the maximum number of events per second? Or any way to ignore events for a short time period?
Any idea on how can I handle this would be very appreciated.
Thanks.
You could put a load balancer in your application, that way it will create a queue and will not freeze the application.
If you want to let go some events, in the logic of your listener, you should have something that check if it's been X time since the last time you managed the event.
private long timeSinceLastEventManaged = 0;
private final static long MINIMUM_TIME = 2000; //2 seconds
In your listener
public void modelChanged(final Change<? extends Order> change) {
long timeSystem = System.currentTimeMillis();
if(timeSystem - timeSinceLastEventManaged > MINIMUM_TIME){
//do your stuff
timeSinceLastEventManaged = timeSystem;
}
}
First of all you should get rid of the println as it is really slow
The rest depends on what you are doing. Right now it seems that you are just getting the value and writing it to a variable. You will only see the latest change that way and if that is what you want the solution #TyMarc suggested will work fine.
If what you showed us is just an example and you really need every change things get a bit more complicated. Your modelChanged method should be changed to add the current value to a queue (e.g a LinkedList or Stack).
public void modelChanged(final Change<? extends Order> change)
{
syncronized(syncObject)
{
//add to your preffered queue
syncObject.notifyAll()
}
}
This frees your listener from the real work and it can keep collecting data.
I added a syncronized as someone has to do the work. For this you can use a Thread that runs something like this:
Order current;
while(keeprunning)
{
syncronized(syncObject)
{
if(queue.hasNext())
{
current = queue.getNext()
}
else
{
Thread.wait()
}
}
//do the real work here
}
Now someone else has the problem. Literally. If the Thread can't handle the inflow of data the queue will grow in size until you run out of memory or hit some other limit. But that's another story.
And yes, nothing of this will compile as I only wanted to show an example

Thread in JME doesn't work

In JME I try to use threading but when I run the program the function never starts.
I have a server socket who is listening to input from Netbeans.
Listener
while (isRunning) {
//Reads and prints the input
String receivedString = (String) in.readObject();
System.out.println(receivedString);
String[] parts = receivedString.split(";");
if(parts[0].equals("craneCon"))
{
final int containerId = Integer.parseInt(parts[1]);
m.enqueue(new Callable<Spatial>(){
public Spatial call() throws Exception{
m.removeContainersFromMaritime(containerId);
return null;
}
});
}
So in the main there is the function removeContainersFromMaritime
public void removeContainersFromMaritime(final int idContainer)
{
Node container = Maritime.listOfContainers.get(idContainer);
martime.detachChild(Maritime.listOfContainers.get(idContainer));
seagoingcrane.attachChild(Maritime.listOfContainers.get(idContainer));
container.setLocalTranslation(0,5,0);
System.out.println(Maritime.listOfContainers.get(0).getWorldTranslation().z);
}
The connection is alright but the method is never executed. How can I fix this?
jMonkeyEngine uses a swing-style threading model where there is a single render thread that does all the work. Any changes to the scene graph have to be done from that render thread.
To get into the render thread you can implement AppStates, Controls or you can enqueue Callables which are then executed on the render thread in a similar way to Swing's invokeLater.
The code snippet you posted looks about right, so assuming m is your running jME3 SimpleApplication then m.enqueue() will cause the enqueued callable to be executed next time around the render loop (i.e. at the start of the next frame).
If you are not seeing it executed then either:
Your application is not running
You created more than one application and enqueued it to the wrong one
The code is actually running and you just think it isn't.
Stepping through the code in the debugger and/or adding debug statements (for example breakpoint inside removeContainersFromMaritime to see if it is actually called should allow you to narrow this down.
I might be missing something but what is "m" in m.enqueue(...)?
I'm guessing it is an executor service of some sort and it's probably where the problem lies.
You could try instead:
new Thread() {public void run()
{
m.removeContainersFromMaritime(containerId);
}}.start();
It will at least show you if the problem is coming from "m" as an executor.

How to wait for an attribution in startup() to complete before continuing?

I'm making a desktop application which watches a folder using watchservice from java.nio.file . But I need the gui to be loaded before I start watching, because the path to be watched is in a JFieldText on the UI.
public class FileArchiverApp extends SingleFrameApplication {
static FileArchiverView gui;
#Override protected void startup() {
gui = new FileArchiverView(this); //HERE0 I have to wait for this.
show(gui);
...
public static void main(String[] args) throws IOException {
launch(FileArchiverApp.class, args);
....
WatchService watcher = FileSystems.getDefault().newWatchService();
// HERE1 while(gui==null) System.out.println("hi") ;
try {
Path dir = Paths.get(gui.getOriginPath()); // HERE2 I get nullpointer if gui was not ready
WatchKey key = dir.register(watcher, ENTRY_CREATE );
} catch ( Exception x) {
System.err.println(x);
}
while(true){ /*wait for new file event loop*/ }
}
The function getOriginPath() returns the getText() form the text field I mentioned.
In HERE0 is the attribution I mentioned. I get a nullpointer in HERE2 if gui wasn't ready.
I've tried things. If I put that thing in HERE1 it works, but of course I don't want to do that.
How could I make it?
And its taking to long(like two seconds) or the gui to stop being null with this HERE1 I don't know if it is because of the println, but I was expecting it to be almost instantaneous. Is it normal?
Thanks.
Given the limited information posted, I have to make some assumptions. Assumption 1 is that you give the JTextField a default value and use that as the path to the file you wish to watch. Assumption 2 is that you have not coded with an eye towards MVC-like design.
If both are correct, then it sounds like you have the tail wagging the dog -- the view holding the critical data, not the model. Why not fix your problem by going towards MVC and not getting the critical data from the view but rather from the model. Start the model up first thing, including getting the default path from your program Properties, get your listener going, start your view, and then if the view asks the controller to change the watched file, have the controller change the model. And then listeners in the model will notify your any observers of change.

Categories

Resources