Updating GUI with SwingWorker - java

I posted a question yesterday about preventing the UI from freezing when using a scheduledexecutorservice here What should I do in order to prevent the UI from freezing(scheduledexecutorservice) and most people suggested using SwingWorker instead of scheduledexecutorservice. However, I've been stuck since then trying to figure out how a SwingWorker thread would work in my case.
I have the following pseudocode:
createGraph(){
if(rule1)
n = new Node()
graph.add(n)
animateGraph()
createGraph()
if(rule2)
...
I have a recursive algorithm which creates a graph based on certain rules and I want to update the UI when a rule is matched and a new vertex/edge is added to the graph. My question is how can I display the graph whenever a new node/edge is added to it? This should happen in the animateGraph() method and when it hits this method, it should update the actual UI, preferably wait for 1500ms and do that until the whole graph is built.
I tried creating a SwingWorker thread. In this case it doesn't show the intermediate steps of the graph creation but only the final graph. First, it executes all calls to doInBackground() and then it goes to done().
NB: I create a new SwingWorker thread every time a new vertex/edge is created as I read that doInBackground() is only called once.
private void animateGraph() {
swingWorker = createRunnable();
swingWorker.execute();
}
private void displayGraph() {
JPanel.add(graph);
}
private SwingWorker<Object, Object> createRunnable() {
swingWorker = new SwingWorker<Object, Object>() {
#Override
protected Void doInBackground() throws Exception {
System.out.println("Start sleeping.. " + new Date());
Thread.sleep(1500);
publish(new NodeTuple(new Node("A"), new Node("B")));
return null;
}
protected void process(List<NodeTuple> chunks) {
System.out.println("In process.. " + new Date());
NodeTuple nodeTuple = chunks.get(chunks.size() - 1);
graph.addVertex(nodeTuple.source);
graph.addVertex(nodeTuple.target);
checkIfToAddEdge(nodeTuple.source, nodeTuple.target);
createDiagram();
}
}
};
return swingWorker;
}
Edit: I updated doInBackground() and process() methods. However, I still don't get what I really want. No intermediate steps are shown and only the final graph is displayed.

You should probably use the publish/process API of the SwingWorker (see the second example of the SwingWorker API doc for code).
This will allow you create nodes recursively and off the EDT, then publish new nodes matching your rule, before finally processing these nodes on the EDT for display or animation.
Adding animation will need it's own thread, and I suggest you add that as a separate task, but at least you should be able to see new nodes showing up as they are added to the graph.

To see the intermediate steps, you have to publish() each new Node as it's created and process() it on the event dispatch thread, like they show in Tasks that Have Interim Results.
class FlipTask extends SwingWorker<List<Node>, Node> {
#Override
protected List<Node> doInBackground() {
…
publish(new (Node);
…
}
protected void process(List<Node> list) {
// add each new Node to the view
}
}

Related

Updating JavaFX ProgressIndicator multiple times from a Thread

I am working on the design of a multi-threading app in Javafx and would like to have a TableView with columns for Name and Progress of each Thread. After doing much research I found a similar example of what I am trying to accomplish here:
JavaFX Update progressbar in tableview from Task
(Which points to this: 'https://community.oracle.com/message/10999916')
The problem I am running into, however, is illustrated well in this example; how can you call a 'Task' object multiple times to update a ProgressIndicator?
My understanding from Oracle's documentation is that a Task object "is a one-shot class and cannot be reused". It would seem then that one can only invoke the call() method of a Task object once. I need to update the Task multiple times as it progresses through a Thread class, not call it once and arbitrarily increment through a For loop.
I have read about binding to Listeners and creating Service classes, but I am unsure if those are actual resolutions to this problem. I would therefore like to ask if this is even possible in Javafx, or if perhaps I am overlooking something. In the event someone has accomplished this in the past, it would be tremendously helpful if you might be able to illustrate how through the example provided previously.
Any direction on this would be appreciated, thank you.
-Drew
EDIT 1: I edited my wording as it was inaccurate.
EDIT 2: Here is an example with some pseudo code. Say I had a class with the following code:
public static class TaskEx extends Task<Void>{
#Override
protected Void call(){
updateProgress(.5, 1);
return null
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
//Some code for data in table.
TableColumn progressColumn = new TableColumn ("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactor("progress");
table.setItems(<data>);
table.getColumns();addAll(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(<SomeNumber>);
for(TaskEx task : table.getItems(){
Threading.ThreadClass newThread = new Threading.ThreadClass(task);
executor.submit(newThread, <uniqueID>);
}
}
Then say I had a second class for Threading with this logic:
static class ThreadClass extends Thread{
Task progressTask;
public ThreadClass(Task task, Integer id){
progressTask = task;
}
public void run(){
ExecutorService executor = Executors.newFixedThreadPool(<someNumber>);
//This invokes the Task call for the correct progressIndicator in the Tableview.
//It will correctly set the progressIndicator to 50% done.
executor.submit(progressTask);
/* Main logic of the Threading class that involves the 'id' passed in. */
//This will do nothing because you cannot invoke the Task call more than once.
executor.submit(progressTask);
}
}
That is the sort of workflow I need, but I'm unsure how to accomplish this.
It seems like you don't get what we were talking about. You are trying to do your logic in the Thread.run(), and then each thread is creating a Task just to do the update of progress.
What you need is really to shift your logic from Thread.run() to Task.call(). Your thread is really just a thread, and all it does is to run a Runnable object (which is the Task).
public class TaskEx extends Task<Void> {
#Override
protected Void call() {
// Do whatever you need this thread to do
updateProgress(0.5, 1);
// Do the rest
updateProgress(1, 1);
}
}
public static void callThread() {
TableView<TaskEx> table = new TableView<TaskEx>();
ObservableList<TaskEx> data = FXCollections.observableArrayList<>();
data.add(new TaskEx()); // Add the data you need
TableColumn progressColumn = new TableColumn("Progress");
progressColumn.setCellValueFactory(new PropertyValueFactory("progress"));
progressColumn.setCellFactory(column -> {
return new TableCell<TaskEx, Double> {
private final ProgressBar bp = new ProgressBar();
#Override
public void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
}
else {
bp.setProgress(item.doubleValue());
setGraphic(bp);
}
}
}
});
table.setItems(data);
table.getColumns().add(progressColumn);
ExecutorService executor = Executors.newFixedThreadPool(data.size());
for (TaskEx task : table.getItems()) {
executor.submit(task);
}
}
This implement removes ThreadClass because there should not be any logic that must be done at a thread sub-class. If you really need to access the thread object as part of your logic, call Thread.getCurrentThread() from your TaskEx.call().
This implement also opens multiple threads doing exactly the same thing (which is quite meaningless). If you need to do a set of different logics, you can either make a set of different Task subclasses, or add a constructor taking in Runnable objects in TaskEx.
E.g.
public class TaskEx extends Task<Void> {
private final Runnable[] logics;
public TaskEx(Runnable[] logics) {
this.logics = logics;
}
#Override
protected Void call() {
for (int i = 0; i < logics.length; i++) {
logics[i].run();
updateProgress(i, logics.length);
}
}
}

Thread.sleep stops all nested Asyntasks

I am following tutes from codelearn, and trying create an AsyncTask which generates tweets and executes another AsyncTask to write to a cache file.
I have Thread.sleep, so the UI on first load waits until the Tweets are written to cache file. First I execute AysncTask new AsyncWriteTweets(this.parent).execute(tweets); then sleep for 10 secs.
But in logcat I can see that AsyncWriteTweets also gets executed after 10 sec sleep. Hence onPostExecute gets executed before the tweets are written to the cache file, giving a blank screen.
public class AsyncFetchTweets extends AsyncTask<Void, Void, Void> {
private TweetListActivity parent;
ArrayList<Tweet> tweets = new ArrayList<Tweet>();
ArrayList[] temp;
public AsyncFetchTweets(TweetListActivity parent){
this.parent = parent;
}
#Override
protected Void doInBackground(Void... params) {
int result = 0;
Log.d("ASync", "Calling asycn");
for (int i=0;i<4;i++){
Tweet tweet = new Tweet();
tweet.setTitle("Title Async Very New" + i);
tweet.setBody("Body text for tweet no " + i);
tweets.add(tweet);
}
new AsyncWriteTweets(this.parent).execute(tweets);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result){
Log.d("Async", "on Post execute");
this.parent.renderTweets();
}
}
PS: My assumption is AsyncTask should create a new thread, hence
Thread.sleep in parent should not stop child. If it is otherwise
please advise how can I overcome this issue.
This:
new AsyncWriteTweets(this.parent).execute(tweets);
is wrong, AsyncTask must be executed on UI thread and not Worker thread. You might use Handler and post runnable to execute it safely.
For reference look into Threading rules:
execute(Params...) must be invoked on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
another part of above link of interest is Order of execution, :
Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
so your first asynctask must end before next one might start, but you migt bring back previous parallel behaviour by using executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR. Still execute must be done on UI thread.
As per documentation on execute() method, a single thread is used for all async tasks. So, if you are sleeping in your async tasks, it will affect other async tasks.
Give executeOnExecutor a try.

suspend the process execution up to displaying UI [duplicate]

i have created a wizard programatically. it contain 3 panels. the second one is devicePane and third one is detailsPane. the third panel consist of a progress bar. i want my program to start a function process() after displaying the third panel? is it possible using thread?
else if(ParserMainDlg.this.POSITION==1){
if(sqlConnectionPane.executeProcess()==true){
devicePane.setDeviceList();
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
}
else if(ParserMainDlg.this.POSITION==2){
System.out.println("position:"+ParserMainDlg.this.POSITION);
if(devicePane.executeProcess()==true){
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
I want sqlConnectionPane.executeProcess() to call a function which starts executing after displaying the devicePane Panel?
You can definitly use a thread to execute your task, this is the preferred way of handling a long running task.
You have multiple options here. All options include making a callback to your wizard, to update the progressbar.
You can make your own task class wich does exactly this, or you can use the existing SwingWorker. "SwingWorker itself is an abstract class; you must define a subclass in order to create a SwingWorker object; anonymous inner classes are often useful for creating very simple SwingWorker objects."
Using the swing worker we just learned about you can use something like this:
SwingWorker<Integer, Integer> backgroundWork = new SwingWorker<Integer, Integer>() {
#Override
protected final Integer doInBackground() throws Exception {
for (int i = 0; i < 61; i++) {
Thread.sleep(1000);
this.publish(i);
}
return 60;
}
#Override
protected final void process(final List<Integer> chunks) {
progressBar.setValue(chunks.get(0));
}
};
backgroundWork.execute();
Note that you will have to break your task down into smaller parts to actually be able to display progress.

how to suspend execution in java using thread

i have created a wizard programatically. it contain 3 panels. the second one is devicePane and third one is detailsPane. the third panel consist of a progress bar. i want my program to start a function process() after displaying the third panel? is it possible using thread?
else if(ParserMainDlg.this.POSITION==1){
if(sqlConnectionPane.executeProcess()==true){
devicePane.setDeviceList();
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
}
else if(ParserMainDlg.this.POSITION==2){
System.out.println("position:"+ParserMainDlg.this.POSITION);
if(devicePane.executeProcess()==true){
ParserMainDlg.this.POSITION++;
fireStateChanged(oldValue);
}
I want sqlConnectionPane.executeProcess() to call a function which starts executing after displaying the devicePane Panel?
You can definitly use a thread to execute your task, this is the preferred way of handling a long running task.
You have multiple options here. All options include making a callback to your wizard, to update the progressbar.
You can make your own task class wich does exactly this, or you can use the existing SwingWorker. "SwingWorker itself is an abstract class; you must define a subclass in order to create a SwingWorker object; anonymous inner classes are often useful for creating very simple SwingWorker objects."
Using the swing worker we just learned about you can use something like this:
SwingWorker<Integer, Integer> backgroundWork = new SwingWorker<Integer, Integer>() {
#Override
protected final Integer doInBackground() throws Exception {
for (int i = 0; i < 61; i++) {
Thread.sleep(1000);
this.publish(i);
}
return 60;
}
#Override
protected final void process(final List<Integer> chunks) {
progressBar.setValue(chunks.get(0));
}
};
backgroundWork.execute();
Note that you will have to break your task down into smaller parts to actually be able to display progress.

JTree Not Refreshed When Called on Different Location But On Same Event-Dispatching Thread

I have been struggling with JTree. I cannot refresh it after I add a new tree node (DefaultMutableTreeNode). I am able to refresh it when the code that adds the tree node is called from within the GUI class, but not outside it. Here is the code that actually adds the node to the JTree:
public class TreeViewer extends JPanel implements TreeSelectionListener {
JTree tree;
DefaultMutableTreeNode rootNode;
DefaultTreeModel treeModel;
public void modifyJTree(String name) {
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(name);
treeModel.insertNodeInto(childNode, rootNode, rootNode.getChildCount());
}
}
When it is called in the main method, the GUI failed to refresh itself after the node is added. I experimented several ways to put it on the Event-Dispatching Thread, but it does not work. I also tried it on the main thread, and it also failed. The code examples are provided below:
public static void main(String[] args) throws Exception {
final TreeViewer viewer = new CaseViewer();
// I omit the code that sets up the GUI and displays it
// This calls modifyJTree on the Event-Dispatching Thread
// And it does not work
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewer.modifyJTree("InvokeLater");
}
});
// This also calls modifyJTree on the Event-Dispatching Thread
// And it still does not work
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewer.modifyJTree("InvokeLater");
}
});
// Using a SwingWorker. Still no luck.
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Thread.sleep(5000);
return null;
}
#Override
protected void done() {
viewer.modifyJTree("SwingerWorker");
}
};
// Now I tried to call it on the main thread, but this cannot work
viewer.modifyJTree("main thread");
}
However, if the call is from within the class, it works. For example, in the constructor of my TreeViewer class, as shown below:
public TreeViewer() {
rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());
tree = new JTree(treeModel);
tree.setEditable(false);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
// Listen for when the selection changes.
tree.addTreeSelectionListener(this);
// omitting other initialization stuff
// Using a SwingWorker. It is the same SwingWorker, but this one works!
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Thread.sleep(5000);
return null;
}
#Override
protected void done() {
viewer.modifyJTree("InsideSwingerWorker");
}
};
}
So my impression is that the call must be coming from inside the TreeViewer class. However, if the method cannot be called from outside the class, my TreeViewer is basically useless.
I can't help but suspect this is a bug of JVM. Or am I ignorant of some best practices regarding JTrees that caused this weird error?
Update: Problem solved. It actually has nothing to do with JTrees. The JTree instance I was modifying was not correctly added into the GUI that I was looking at.
Settle down. First, if you haven't already, read the How to Use Trees tutorial. Once you feel comfortable with the material, you can focus your attention on the Dynamically Changing a Tree partition. Therein you'll find sample code that will hopefully make things a little bit more clear for you.
But remember, you're absolutely right in what you're trying to do. That is, respect Swing's single-thread model. Using mechanisms, such as SwingUtilities and SwingWorker to modify a Swing component when in another thread is absolutely correct, although this does not seem to be applicable in your case. I think you're just a little misguided, or overwhelmed.

Categories