Задать вопрос
zorgrhrd
@zorgrhrd

Как правильно обновлять Progressbar из цикла?

Привет мой спаситель!

Проблема в том что Progressbar обновляется только после окончания цикла, не могу никак заставить обновится Progressbar в процессе работы, пробовал и invokeLater, и SwingWorker, и Task, ничего не выходит, обновление происходит только после завершения цикла.

Вот код метода, он вызывается из GUI потока:
private void runCommand(String... args) throws IOException {
	
                Runtime rt = Runtime.getRuntime();
                rt.exec(args);
				
                BufferedReader stdInput = new BufferedReader(new
                        InputStreamReader(proc.getInputStream()));

                BufferedReader stdError = new BufferedReader(new
                        InputStreamReader(proc.getErrorStream()));

                appendToLog("-");

                String s = null;
                try {
                    while ((s = stdInput.readLine()) != null) {
                        appendToLog(s + "\n");
					}
                } catch (IOException e) {
                    e.printStackTrace();
                }

                try {
                    while ((s = stdError.readLine()) != null) {
                            Double progress = Double.parseDouble(s.split("%")[0]) / 100;
                            progressbar.setProgress(progress));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
			}


В месте "progressbar.setProgress(progress));", нужно обновить данные прогрес бара, в данном случае конечно он обновится только после окончания работы, т.к. основной поток занят, но странно то что даже с SwingWorker ничего не вышло. Делал SwingWorker с publish и process, ставил точки останова, publish и process срабатывали, в процессе работы цикла новые значения поступали в process, там был код обновляющий прогресс бар согласно последнего значения в чанке "progressbar.setProgress(chunk.get(chunk.size()-1)));", но сам прогресс бар все равно обновлялся только после окончания цикла, когда SwingWorker выходил в done(); и завершался.

Хотя на сколько вычитал, publish и process как раз и нужны для вывода данных в GUI поток, или я что то не верно понимаю?

Прошу помощи.
  • Вопрос задан
  • 8743 просмотра
Подписаться 3 Средний 1 комментарий
Решения вопроса 1
zorgrhrd
@zorgrhrd Автор вопроса
Прошу прощения, два дня бился головой о стену в попытке понять, почему оно не работает, оказывается искал не в том месте, дело в том что буфер сначала накапливался, а потом уже после того как поток закрылся, разом отдавал вывод в цикл и прогрессбар молниеносно набирал 100%. Т.е. с обновлением бара проблем нет, дело было в самом буфере, переписал метод:

private void runCmdWithProgress(String... args) throws IOException {
        Process proc = Runtime.getRuntime().exec(args);
        InputStream inputStream = proc.getErrorStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String s = null;

        while ((s = bufferedReader.readLine()) != null) {
                Double progress = Double.parseDouble(s.split("%")[0]) / 100;
                Platform.runLater(() -> tab_adb_progressbar.setProgress(progress));
        }
        try {
            proc.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


Плюс к ответу Александра, да, данный метод из GUI потока нужно вызывать обязательно в отдельном, я делаю так же как у вас:
new Thread(() -> {
	                        try {
	                            runCmdWithProgress(commands);
	                            Platform.runLater(() ->  showDialogInformation(positive);
	                        } catch (IOException e) {
	                            e.printStackTrace();
	                        }
	                    }).start();


Всем спасибо!
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
alexplot
@alexplot
Студент, программист
Когда в твоём приложение появляется работа, которая выполняется довольно долго, как например твои циклы, это останавливает обновление UI в целом. Чтобы твой код работал его нужно запустить в отдельном потоке, а так как JavaFX не многопоточна, то операции типа setProgress(double v) должны быть возвращены обратно в главный поток приложения методом Platform.runLater().

Platform.runLater(() -> progress.setProgress( step / size ));


А использовать Swing классы для работы с JavaFX, как мне кажется, не правильно.
Ответ написан
foxmuldercp
@foxmuldercp
Системный администратор, программист, фотограф
в c# я это делаю так:
фоновая задача в бекграунде, которая что-то долго делает, допустим в цикле.
в цикле есть вызов функции report.progress(int someint)

на функцию report.progress(int bla) висит код, который может взаимодействовать с gui и обновлять счетчик прогрессбара
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы