Компонент индикатора выполнения JavaFX активирован из метода Spring Listener

spring user-interface javafx

502 просмотра

1 ответ

У меня есть трехслойное (gui-> services-> dao) настольное приложение (JavaFX + Spring 4). Некоторые из моих методов, реализованных на уровне сервисов (без зависимости от FX), требуют много времени. Я хотел бы сообщить об их прогрессе в графическом интерфейсе.

Но есть проблема. Мой сервисный уровень не имеет прямого доступа к графическому интерфейсу, ведет журнал операций ввода-вывода и возвращает только его сводку. Слой GUI, не знает о прогрессе промежуточных операций.

Я пытаюсь сделать общий компонент прогресса. Это может быть реализовано как Spring Bean с методом слушателя, расположенным на уровне графического интерфейса пользователя. Мои сервисы могут публиковать ApplicationEvent, которые можно получить через компонент прогресса в графическом интерфейсе. Когда он получит событие, следует активировать индикатор выполнения и сообщить о следующих событиях.

В методах инициализации я начал ProgressTask, и связать progressPropertyс моим графическим индикатором прогресса.

Вызовите метод моих ProgressTaskпробежек, в то время как фактический счетчик меньше максимального количества шагов.

Вот мой компонент:

@Component
@Controller
public class ProgressBarComponent  {

    @FXML
    private ProgressBar progressBar;



    private ProgressTask actTask;

    @FXML
    public void initialize(){
    }

    private void initProgressBar(int aMax){
        actTask=new ProgressTask();
        actTask.initState(aMax);
        progressBar.progressProperty().bind(actTask.progressProperty());
    new Thread(actTask).start();
    }

    @EventListener
    public void handleProgressEvent(ProgressEvent event) {
        if(event.getGetActStep()==0){
            initProgressBar(event.getMaxStep());
        }
        actTask.changeState(event.getGetActStep());
    }


    class ProgressTask extends Task<Object>{
        private int max=100;
        private int act=0;
        private volatile boolean cancelled;

        public void initState(int aMax){
            this.max=aMax;
        }

        public void changeState(int newVal){
            act=newVal;
            updateProgress(act, max);
            updateMessage("progress: " + act);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new IllegalArgumentException(e);
            }
        }

        @Override
        protected Object call() throws Exception {
            while(act<max){
            }
            return null;
        }

    }

}

Но это не работает. Контроль за индикатором хода активируется, но за один шаг и сразу он имеет 100-процентное значение.

Я нашел много примеров управления Progress Bar FX, но все они предполагают, что основная логика вычисляется внутри call()метода ProgressTask. В моем случае это невозможно.

Можно ли сделать ProgressBarКомпонент, который активируется из метода слушателя? Если да, какой код должен быть в ProgressTask.call()методе?

Автор: user2427814 Источник Размещён: 08.11.2019 11:01

Ответы (1)


2 плюса

Решение

Суть в Taskтом, чтобы определить некоторые функции, которые должны быть запущены в фоновом потоке. Так что, если эта функциональность уже реализована в другом месте, нет необходимости в какой-либо задаче.

Предполагая, что служба вызывается в фоновом потоке (то есть не в потоке приложений FX), вы должны просто сделать:

@Component
@Controller
public class ProgressBarComponent  {

    private int max ;

    @FXML
    private ProgressBar progressBar;

    @FXML
    public void initialize(){
    }

    private void initProgressBar(int aMax){
        this.max = aMax ;
    }

    @EventListener
    public void handleProgressEvent(ProgressEvent event) {
        if(event.getGetActStep()==0){
            Platform.runLater(() -> initProgressBar(event.getMaxStep()));
        }
        Platform.runLater(() -> 
            progressBar.setProgress(1.0 * event.getGetActStep() / max));
    }
}

Вот полный SSCCE:

ProgressViewer.java:

package springevent;

import org.springframework.context.event.EventListener;

import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ProgressViewer {

    private Stage stage = new Stage();
    private Scene scene ;
    private ProgressBar progressBar ;

    public ProgressViewer() {
        progressBar = new ProgressBar();
        StackPane root = new StackPane(progressBar);
        root.setPadding(new Insets(20));
        scene = new Scene(root);
        stage.setScene(scene);
    }

    @EventListener
    public void progressUpdate(ProgressEvent event) {
        if (event.getCurrent() < event.getMax()) {
            Platform.runLater(stage::show);
        } else {
            Platform.runLater(stage::hide);
        }
        final double progress = 1.0 * event.getCurrent() / event.getMax() ;
        Platform.runLater(() -> progressBar.setProgress(progress));
    }
}

Service.java:

package springevent;

import javax.inject.Inject;

import org.springframework.context.ApplicationContext;

public class Service {

    @Inject
    private ApplicationContext context ;

    public void runService() {
        context.publishEvent(new ProgressEvent(0, 100));
        try {
            for (int i = 1 ; i <= 100; i++) {
                Thread.sleep(200);
                context.publishEvent(new ProgressEvent(i, 100));
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

ProgressEvent.java

package springevent;

public class ProgressEvent {

    private final int current ;
    private final int max ;

    public ProgressEvent(int current, int max) {
        this.current = current ;
        this.max = max ;
    }

    public int getCurrent() {
        return current ;
    }

    public int getMax() {
        return max ;
    }
}

MainController.java

package springevent;

import java.util.concurrent.Executor;

import javax.inject.Inject;

import org.springframework.context.event.EventListener;

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class MainController {

    @Inject
    private Executor exec ;

    @Inject
    private Service service ;

    @FXML
    private Button startButton ;

    public void startProcess() {
        exec.execute(service::runService);
    }

    @EventListener
    public void progressUpdate(ProgressEvent event) {
        startButton.setDisable(event.getCurrent() < event.getMax());
    }

}

Main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="springevent.MainController">
    <padding>
        <Insets top="20" bottom="20" left="20" right="20"/>
    </padding>
    <Button fx:id="startButton" text="Start process" onAction="#startProcess" />
</StackPane>

Main.java

package springevent;

import java.io.IOException;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    private AbstractApplicationContext context ;

    @Override
    public void start(Stage primaryStage) throws IOException {
        context = new AnnotationConfigApplicationContext(AppConfig.class);
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
        loader.setControllerFactory(context::getBean);
        Scene scene = new Scene(loader.load());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

и AppConfig.java:

package springevent;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public Service service() {
        return new Service();
    }

    @Bean
    public MainController mainController() {
        return new MainController();
    }

    @Bean
    public ProgressViewer progressViewer() {
        return new ProgressViewer();
    }

    @Bean
    public Executor exec() {
        return Executors.newCachedThreadPool(runnable -> {
            Thread t = new Thread(runnable);
            t.setDaemon(true);
            return t ;
        });
    }
}
Автор: James_D Размещён: 20.08.2016 06:20
Вопросы из категории :
32x32