Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX UI Freezing Issue

I am trying to show the progress and the status of the each task. Each task represents by a row into TableView. Each task executs parallel in each Thread. Please refer image of TableView.

enter image description here

For "Progress" TableColumn I have set Cell Factory to render `ProgressBar".

  public static class ProgressBarTableCell<S, T> extends TableCell<S, T> {

        private final ProgressBar progressBar;
        private ObservableValue<T> ov;

        public ProgressBarTableCell() {
            this.progressBar = new ProgressBar();
            progressBar.setPrefHeight(23);
            setAlignment(Pos.CENTER);
        }

        @Override
        public void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null) {
                setGraphic(null);
                setText(null);
            } else {
                if (item.toString().equalsIgnoreCase("Processing")) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {

                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(-1);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(-1);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                } else {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            if (getGraphic() == null) {
                                setGraphic(progressBar);
                                progressBar.setProgress(0);
                            } else {
                                ProgressBar objpProgressBar = (ProgressBar) getGraphic();
                                objpProgressBar.setProgress(0);
                            }
                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                        }
                    });
                }
            }
        }
    } 

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? FYI: At a time only 5 threads allowed to execute parallel.

Edited -

according to suggestion of Mr. Andy Till I have updated my code but still no luck -

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);
    }

And not calling any background thread to update UI but no luck.

like image 515
Ashish Pancholi Avatar asked Dec 12 '12 14:12

Ashish Pancholi


2 Answers

Your 'QueueData' class should have methods to set double property of existing progress, it seems you are creating new SimpleDoubleProperty each and every time on setting progress status in setprogressBar(Double sType) method.

Update this as -

 public void setprogressBar(Double sType) {
        this.progressBar.set(sType);
    }

Just create object of progressBar once at start then update existing one.

like image 197
Neelam Sharma Avatar answered Nov 09 '22 17:11

Neelam Sharma


As I understand your code you are flipping the visibility to trigger the updateItem method on the table cell? This means that any number of threads could be trying to change the visibility every second and repaint endlessly.

I know you are already quite far with your implementation but the JavaFX service class already implements a progress feature which you can update which would be much simpler than how you are doing it now.

It would be much simpler and efficient to have a progress double property which you update from your thread and bind this to the ProgressBar.progressProperty() so everything would automatically update. This is how the Service class works but it is easy to implement yourself.

Also make sure you are disposing your threads, this is easy to check. Debug in eclipse and in the debug perspective it will show you the list of threads, make sure it doesn't grow endlessly.

For scheduling a Runnable checkout the ScheduledExecutor class, you can easily create it by using Executors.newScheduledThreadPool(numOfThreads).

like image 5
Andy Till Avatar answered Nov 09 '22 16:11

Andy Till