This discussion is archived
2 Replies Latest reply: Dec 13, 2012 9:12 PM by 875776 RSS

How to make my JavaFX application user interface (UI) responsive.

875776 Newbie
Currently Being Moderated
I am trying to show the progress and the status of the each task in the JavaFX TableView. Each task represents by a row into a TableView. Each task executes in a separate Thread. Task may be like - downloading files from server, copy files from one account to another account etc. Please refer image. Every thing is working absolutely fine except JavaFX UI is freezing. May be because this muti-threaded environment.

As you can see in the image for Progress TableColumn I have set Cell Factory to render ProgressBar.
  
           TableColumn tableColumn_Progress = new TableColumn("Progress");
            tableColumn_Progress.setCellValueFactory(new PropertyValueFactory<QueueData, Double>("progressBar"));
            tableColumn_Progress.setPrefWidth(100);

            Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>> attachmentCellFactory = //
                    new Callback<TableColumn<QueueData, Double>, TableCell<QueueData, Double>>() {
                @Override
                public TableCell call(final TableColumn param) {
                    final ProgressBarTableCell cell = new ProgressBarTableCell() {
                        @Override
                        public void updateItem(Object item, boolean empty) {
                            super.updateItem(item, empty);
                        }
                    };
                    return cell;
                }
            };
            tableColumn_Progress.setCellFactory(attachmentCellFactory); 
In QueueData Class -
    DoubleProperty progressBar = null;
    
    public DoubleProperty progressBarProperty() {
            return progressBar;
        }
    
        public Double getprogressBar() {
            return progressBar.get();
        }
    
        public void setprogressBar(Double sType) {
            this.progressBar = new SimpleDoubleProperty(sType);
        }
The TableView was not showing updated status and progress of tasks, so I am running a thread periodically in background, which modifies TableView when I call startUpdatingTableProgress() and stops when I call stopUpdatingTableProgress().
     public void stopUpdatingTableProgress() {
        keepUpdating = false;
    }

    public void startUpdatingTableProgress() {
        keepUpdating = true;
        TableProgressBarUpdator tpu = new TableProgressBarUpdator(table);
        tpu.start();
    }

    class TableProgressBarUpdator implements Runnable {

        TableView table;

        public TableProgressBarUpdator(TableView fxtable) {
            table = fxtable;

        }

        public void start() {
            new Thread(this).start();
        }

        public void run() {

            while (keepUpdating) {
                try {
                    updateProgressbar();
                    Thread.sleep(1000);
                } catch (Exception e) {
                    LogHandler.doErrorLogging("Error while updating tables cell", e);
                }
            }
            LogHandler.doDebugLogging("Table process repainting is completed.");
        }

        private void updateProgressbar() throws Exception {
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    ((TableColumn) table.getColumns().get(0)).setVisible(false);
                    ((TableColumn) table.getColumns().get(0)).setVisible(true);
                }
            });


        }
   }
But now My JavaFX app's UI freezes after consecutive times. Is there any alternative way to call updateProgressbar() in background thread that triggers automatically after specific time interval and do not freeze my `JavaFX` App's UI?



I do not know whether I am on the right way to update status of tasks where each task runs in a separate thread. If I would like to implement this with the help of `javafx.concurrent package` then can anybody show me some guidance or flow? What will go into the Task and what will go into Service Class? How I can schedule it to update progress bar? How to bind ProgressBar.progressProperty()?
  • 1. Re: How to make my JavaFX application user interface (UI) responsive.
    JonathanGiles Journeyer
    Currently Being Moderated
    If you could attach a self-contained and executable program I will happily take a look.

    -- Jonathan
  • 2. Re: How to make my JavaFX application user interface (UI) responsive.
    875776 Newbie
    Currently Being Moderated
    The sample does not show what exactly I am doing in my app but I tried to demonstrate what issue I am facing.
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.application.Application;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.collections.ObservableList;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.ProgressBarTableCell;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.StackPaneBuilder;
    import javafx.stage.Stage;
    
    public class UpdateTableView extends Application {
    
        private TableView<Person> personTable = new TableView<>();
        private TableColumn colName = new TableColumn<>("Name");
        private TableColumn colProgressBar = new TableColumn("Progress Bar");
    
        @Override
        public void start(Stage primaryStage) {
            showDialog(primaryStage);
    
            colName.setCellValueFactory(new PropertyValueFactory("name"));
            colProgressBar.setCellValueFactory(new PropertyValueFactory("progressBar")); // "name" here is for just to render the column
            colProgressBar.setCellFactory(ProgressBarTableCell.forTableColumn());
            personTable.getColumns().addAll(colName, colProgressBar);
    
            for (int i = 0; i < 10; i++) {
                Person person = new Person();
                person.setProgressBar(-1.0);
                person.setName("Before" + i);
                personTable.getItems().add(person);
            }
    
    
            Thread threadAction1 = new Thread() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(UpdateTableView.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    ObservableList<Person> items = personTable.getItems();
                    for (int i = 0; i < items.size(); i++) {
                        Person person = items.get(i);
                        person.setName("After" + i);
                        person.setProgressBar(0.0);
                    }
                }
            };
            threadAction1.start();
        }
    
        public void showDialog(Stage primaryStage) {
            try {
                StackPane root = StackPaneBuilder.create().children(personTable).build();
                primaryStage.setScene(new Scene(root, 300, 250));
                primaryStage.show();
            } catch (Exception ex) {
                System.out.println("Exception caught while showing dialog" + ex.getMessage());
            }
        }
    
        public static class Person {
    
            private StringProperty name;
    
            public void setName(String name) {
                this.name = new SimpleStringProperty(name);
            }
            private DoubleProperty progressBar;
    
            private Person() {
            }
    
            public double getProgressBar() {
                return progressBar.get();
            }
    
            public void setProgressBar(double surname) {
                this.progressBar = new SimpleDoubleProperty(surname);
            }
    
            public Person(String name, double surname) {
                this.name = new SimpleStringProperty(name);
                this.progressBar = new SimpleDoubleProperty(surname);
    
            }
    
            public String getName() {
                return name.get();
            }
    
            public StringProperty nameProperty() {
                return name;
            }
    
            public DoubleProperty progressBarProperty() {
                return progressBar;
            }
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    When you run the program you will see the TableView with two columns 1. Name and 2. Progress. On start-up Name will will be "Before" for each row whereas the you will see the progress bar running.

    After some time I am trying to update the text with "After" under "Name" column for each row and stop the progress bar running but TableView does not get updated. So to update the table view you need to click on columns header.

    How to make TableView update aromatically? In background, if I run a thread that triggers automatically after specific time interval for refreshing the TableView making my UI freeze.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points