0

I have a javafx program which I want to use to analyze a website. For the start I just want to print the sourcecode of a site into a TextArea, but before that, I write "loading website sourcecode..."

My target is to write "loading website sourcecode..." first, and then after some seconds when the parsing of the site is finished, add that sourcecode.

At the Moment when I press the button, nothing happens and after 3-5 seconds the "loading website sourcecode..." message and the sourcecode is displayed at once.

So I actually want to show strings one after another. I already googled for 2 hours and tried things with threads, invokelater, platform.runLater() and so on but nothing worked, the code is simple.

ModelView.java - Controller Class

package root;

import javafx.application.Platform;
...

public class ModelView {

    @FXML public TextField UrlInput;

    // This gets called when the button is pressed
    public void checkUrl() throws InterruptedException
    {
        String url = UrlInput.getText();

        LogWriter lw = new LogWriter();

        lw.printMsg("loading website sourcecode...");
        lw.printMsg(HTMLParser.directUrlToCode(url));
    }
}

LogWriter.java

package root;

import javafx.beans.value.ObservableValue;
...

public class LogWriter extends Thread{

    @FXML TextArea Log;

    public LogWriter()
    {
        Log = (TextArea) Main.scene.lookup("#Log");
    }

    void printMsg(String s)
    {
        Log.setText(this.Log.getText()+"\n"+s);
    }
}

EDIT:

There is not much to say about the HTMLParser methods, but I add that it extends Thread.

I tried changing ModelView.java to that:

ModelView.java - version 2

package root;

import javafx.application.Platform;
...

public class ModelView<V> {

    @FXML public TextField UrlInput;

    // This gets called when the button is pressed
    public void checkUrl() throws InterruptedException
    {
        String url = UrlInput.getText();

        LogWriter lw0 = new LogWriter();
        lw0.start();
        lw0.printMsg("loading website sourcecode...");

        HTMLParser hp = new HTMLParser();
        hp.start();

        LogWriter lw1 = new LogWriter();
        lw1.start();
        lw1.printMsg(hp.directUrlToCode(url));

    }
}

Still the same effect.

EDIT2:

This is another version I tried, in this case, "loading website sourcecode..." is not even displaying, I am going on with my tries...

ModelView.java checkUrl() - Version 3

public void checkUrl() throws InterruptedException
{
    String url = UrlInput.getText();

    Task<Void> task = new Task<Void>(){
        @Override protected Void call() throws Exception {
            if(isCancelled()) {
                updateMessage("Cancelled");
                LogWriter lw = new LogWriter();
                lw.printMsg("loading website sourcecode...");
            }
            return null;
        }
    };

    Thread t = new Thread(task);
    t.start();

    HTMLParser hp = new HTMLParser();
    LogWriter lw1 = new LogWriter();
    lw1.printMsg(hp.directUrlToCode(url));

}
Skippy
  • 3
  • 2
  • possible duplicate of [Multithreading in JavaFX](http://stackoverflow.com/questions/27646811/multithreading-in-javafx) – eckig Jan 18 '15 at 18:34
  • You need to load and parse the HTML in a background thread. What went wrong when you tried that? Please post that code. – James_D Jan 18 '15 at 18:51
  • James_D, I added a version 2 to my post. – Skippy Jan 18 '15 at 19:01
  • @Skippy Why did you check `isCancelled()` in `call()` method in Version 3? `isCancelled()` is unlikely to be true in `call()` unless the task gets cancelled upon execution. – fardjad Jan 18 '15 at 19:55

1 Answers1

0

First of all, manipulating an observable variable from outside of the JavaFX application thread is a bad idea. You won't be able to bind other variables to it (you'll get IllegalStateExceptions)

Second, I'd implement LogWriter like this:

// ...
public class LogWriter {

    private final TextArea txtLog;

    public LogWriter(TextArea txtLog) {
        this.txtLog = txtLog;
    }

    void printMsg(final String s) {
        if (!Platform.isFxApplicationThread()) {
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    printMsg(s);
                }
            });
        } else {
            txtLog.setText(txtLog.getText() + "\n" + s);
        }
    }
}

and use it this way:

//...

@FXML
private TextArea txtLog;

// ...

public void checkUrl() {
    final String url = UrlInput.getText();
    final LogWriter lw = new LogWriter(txtLog);

    Task<String> task = new Task<String>() {

        @Override
        protected String call() {
            lw.printMsg("loading website sourcecode...");
            return HTMLParser.directUrlToCode(url);
        }

        @Override
        protected void succeeded() {
            super.succeeded();
            lw.printMsg(getValue());
        }

        @Override
        protected void failed() {
            super.failed();
            if (getException() != null) {
                getException().printStackTrace();
            }
            lw.printMsg("failed!");
        }
    };

    new Thread(task).start();
}

Note that HTMLParser does not need to extend Thread.

fardjad
  • 20,031
  • 6
  • 53
  • 68
  • This works! The succeeded method never gets called currently, but I am already working on this, one last question: Who gets the "return null" from call()? I will post my changed code when I'm finished. – Skippy Jan 18 '15 at 19:37
  • You can get whatever `call()` returns in other methods like `succeeded()` or `failed()` using `getValue()`. Take a look at my edited answer and [JavaFX 2 API docs](http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html) – fardjad Jan 18 '15 at 19:48