I have a Webview component inside a JavaFX Program (using FXML) and I want to open it to an external html which is supposed to be a loading screen. (it is necessary due to other background tasks getting perfomed like checking for the existence of a file every 5 seconds)
Then after 5 seconds It is supposed to open the real html file (in this example : "google.com") and should open it.
The problem I've run into is that when i put a delay right after the first engine.load(""); and then after the delay another engine.load(""); with the proper website it will wait that time and then just open the second website without ever opening the first.
So Using a delay without an additional Thread made the Program only open after that time has passed and only opened the second WebEngine.
Using a Thread worked when i put in Thread.sleep(5000); and after that only System.out.println("Hello World!"); but if I put anything related to JavaFX it gives the error that it cannot update the UI outside of Main Thread
I haven't found an option to update the WebEngine Method to load a different website since i am unable to access it outside of my loading Method and Updating it from a different Thread is not allowed.
Using Platform.runLater() has given me nothing but errors because the WebEngine is either not recognized as the same Engine and gives the error that it cannot find the object or it gives the error that JavaFX cant update UI Elements outside of its own Thread.
#FXML
private void initialize() throws InterruptedException {
WebEngine engine = webView.getEngine();
String url = "";
try {
url = getClass().getResource("/index.html").toExternalForm();
} catch (Exception e) {
}
loadSub(url, engine);
}
boolean test = false;
private void loadSub(String website, WebEngine engine) {
engine.load(website); // load loading Screen
//wait 5 seconds
engine.load("Google.com"); //load appropiate website
}
Related
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);
}
I want to use JavaFX WebEngine in such a way that I can execute Javascript code in order to fill one form and click submit button on the website I opened. I was using ChromeDriver but I didn't like certain aspects of it, WebView fits better for my needs, but as far as I researched, the only way to make sure a page is loaded is to add a state listener to WebEngine and when it's SUCCEEDED, make operations on webpage. However, when I execute Javascript and submit a form, a new webpage opens so it's SUCCEEDED for the second time (first time, opens webpage that contains the form) and it executes the same form-filling code in listener. I want to execute other Javascript code after submitting the form but I couldn't find a good way to do that. In ChromeDriver, I could simply wait for certain amount of time, but with WebView case, because it executes in UI thread, I can't do that. Thanks.
I'm not sure if that's what you mean. SUCCEEDED is just a information about state of engine, to be more accurate you should check other parameters, for instance: If you want to handle different pages inside one listener, you can use location-based (currently loaded url) verification.
WebEngine engine = webView.getEngine();
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != State.SUCCEEDED) {
return;
}
String location = engine.getLocation();
if (location.contains("page1")) {
// Do something
return;
}
if (location.contains("page2")) {
// Do something
return;
}
System.out.println("Unhandled location: " + location);
});
I am writing some automated tests using Fluentlenium and PhantomJS. I am having trouble accessing the id "#title". The test I have written is as follows:
#Test
public void testCreateButton() {
startAppWithCallback(new F.Callback<TestBrowser>() {
public void invoke(TestBrowser browser) throws InterruptedException {
CalendarPage calendarPage = browser.createPage(CalendarPage.class);
calendarPage.withDefaultUrl(BASE_URL);
calendarPage.go();
calendarPage.selectCreateButton();
calendarPage.typeTitle("Java Fundamentals");
browser.await().atMost(3, TimeUnit.SECONDS);
}
});
}
The test is running, and seems to be able to select the Create button, which should then open up a modal window, but for some reason it is having trouble seeing the id on this modal. The error message that I get is as follows:
org.openqa.selenium.NoSuchElementException: No element is displayed or enabled. Can't set a new value.
Is there something I am not doing when it comes to accessing the id on the modal window? Any help at all would be much appreciated.
Usually modal windows take some time to attach to the DOM of the page you are accessing. Though you have added 3 seconds to wait for the element to appear/ attach to the DOM but the time is not sufficient. I would not recommend to increase the timeout but would recommend to wait until for the element to appear and then move forward. for e.g. you could do following thing to wait for an element to appear on the page instead of waiting statically:
FluentWaitMatcher matcher = page.await().atMost(, TimeUnit.SECONDS).until(findPattern);
I did this program that opens multiple link with default browser:
ArrayList<String> linkList = new ArrayList<>();
for (int i = 0; i < linkList.size(); i++) {
ClassRunnable_OpenLink obj = new ClassRunnable_OpenLink ( linkList.get(i) );
Thread thread = new Thread(obj);
thread.start();
}
private class ClassRunnable_OpenLink implements Runnable {
private URL link;
private String string;
public ClassRunnable_OpenLink (String string) {
this.string = string;
}
private void OpenWithBrowser () {
try {
link = new URL ( string );
edu.stanford.ejalbert.BrowserLauncher launcher = null;
try {
launcher = new BrowserLauncher(null);
} catch ( BrowserLaunchingInitializingException | UnsupportedOperatingSystemException ex) { }
launcher.openURLinBrowser( link );
} catch ( MalformedURLException ex | IOException | URISyntaxException ex) { }
}
#Override
public void run() {
OpenWithBrowser( );
}
}
This works great only if browser (say it's firefox) is already opened, but if it's not, my program only opens the first link and then I have a firefox message that tells me the the browser is still running so I need to close it first.
Same thing with chromium.
So I thought, if I had a way to check when the browser is closed I could use ProcessBuilder to open new firefox process, but I don't know if it's the best way to do this. Besides my java program allows user to select default browser so it could be complicated to use ProcessBuilder in that case.
So do you a have any idea to solve my problem? Maybe I could set a delay between each Thread in this way the system has the time to execute browser process first time, then opening first link and after the browser is running, opening other links, but how about the delay time in seconds? I'm not able to know the time that browser needs to open so it's not a good idea.
I hope you can help me. Thanks
Since java 6 you don't have to use 3rd party implementations to open a webpage with the standard browser. Try
java.awt.Desktop.getDesktop().browse(uri);
Since this maps to the underlying OS functions chances are high multiple calls will work as expected.
A workaround to this (without knowing your exact expectations), could be to do the following:
After first URL open, you could build a delay of for example 10 seconds.
Then in any consecutive URL openings, you can assume that the browser is now surely open, and start opening the next URL's fast after each other.
One note though: Always add some delay to opening URL's (not sure if your framework already does this), because else the browser might crash from the number of URL openings.
UPDATE: You say that Thread.sleep() causes your program to block, this should never happen.
You should always seperate long-during actions from the rest of your program (The (Graphical) User Interface).
So it would be better to write your 'URL opener facility' in another thread.
You can read more about that here: http://docs.oracle.com/javase/tutorial/essential/concurrency/
I'm trying to load a URL into a JEditorPane or JTextPane but the URL is a dynamically generated PHP page. I then want to process the output from the PHP page in my Java application. The PHP page will always output at least one String that I can use to check that it's generated correctly.
If I try and process the page straight after setting the page using
JEditorPane.setPage(URL);
if( outputTracker.getText().contains("desktop_process") )
it returns a blank HTML page, even if I specify a text/plain content type for the JEditorPane:
System.out.println(outputTracker.getText() );
I assume this is because the PHP page hasn't finished loading as the method below will return the correct output every time.
At the moment I'm setting the page and then starting a Swing Timer and checking every 200ms if the page contains the correctly generated String:
private void getPageBtnActionPerformed(java.awt.event.ActionEvent evt) {
outputTracker.setPage("URL_GOES_HERE?variables=x&y=a");
check_response_timer.start();
}
ActionListener checkAction = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if( outputTracker.getText().contains("desktop_process") ) {
System.out.println("Checking results...");
check_response_timer.stop();
process_response();
} else {
num_checks++;
System.out.println("Checking results...");
if( num_checks == 10 ) {
check_response_timer.stop();
checkLater = true;
responseLbl.setText("Connection timeout. Please click reconnect to check again.");
num_checks = 0;
}
}
}
};
private Timer check_response_timer = new Timer(200,checkAction);
This works fine.
At current server loads and the current complexity of the PHP pages being loaded the loop stops after the first iteration but it just seems a little inefficient and I don't want to have to keep checking if the page is going to take several seconds to load during heavy server loads or on more complex PHP pages.
I'm wondering if there's a better way to check that the page has finished loading.
I was wondering if I could use the JEditorPane.read(inputStream) method or set the page in a Swing Background Worker thread and then process the output of the PHP page when the worker's done() is called.
And is there a better way of loading the PHP output, reading it straight into a string from the dynamically generated output without the use of an editor pane as the editorpane isn't visible anyway?
Hope this is clear enough.
Thanks.
You can add a PropertyChangeListener to the editor pane:
editorPane.addPropertyChangeListener("page", ...);
However, this will only tell you when the main page is loaded. It won't tell you when images or other related files have finised loading.