Javafx 2 JavaFX8UI更新滞后

Javafx 2 JavaFX8UI更新滞后,javafx-2,javafx-8,Javafx 2,Javafx 8,我有一些信号处理数据,大概是50Hz。我需要根据信号值实时更新矩形的不透明度。我正在尝试用JavaFX8开发UI 目前,我正在代码中使用JavaFX服务中的随机数生成器模拟信号值 我正在使用Platform.runLater更新UI,但是这不会实时更新值,我仔细阅读了其他人遇到的类似问题,通常的建议是不要经常调用Platform.runLater,而是批量更新 在我的例子中,如果我批量更新,不透明度更改的频率将不等于信号频率 关于如何实现这一点有什么想法吗 public class Flicke

我有一些信号处理数据,大概是50Hz。我需要根据信号值实时更新矩形的不透明度。我正在尝试用JavaFX8开发UI

目前,我正在代码中使用JavaFX服务中的随机数生成器模拟信号值

我正在使用Platform.runLater更新UI,但是这不会实时更新值,我仔细阅读了其他人遇到的类似问题,通常的建议是不要经常调用Platform.runLater,而是批量更新

在我的例子中,如果我批量更新,不透明度更改的频率将不等于信号频率

关于如何实现这一点有什么想法吗

public class FlickerController
{
@FXML
private Rectangle leftBox;
@FXML
private Rectangle rightBox;
@FXML 
private ColorPicker leftPrimary;
@FXML 
private ColorPicker leftSecondary;
@FXML 
private ColorPicker rightPrimary;
@FXML 
private ColorPicker rightSecondary;
@FXML
private Slider leftFrequency;
@FXML
private Slider rightFrequency;

@FXML
private Button startButton;
@FXML
private Label leftfreqlabel;

@FXML
private Label rightfreqlabel;

@FXML
private Label rightBrightness;
@FXML
private Label leftBrightness;


private boolean running = false;

  DoubleProperty leftopacity = new SimpleDoubleProperty(1); 
  DoubleProperty rightopacity = new SimpleDoubleProperty(1);    

  private FlickerThread ftLeft;
  private FlickerThread ftRight;

public void initialize()
{


    leftopacity.addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue)
        {
            Platform.runLater(new Runnable()
            {

                @Override
                public void run()
                {
                    double brightness = leftopacity.doubleValue();
                    leftBrightness.setText(""+brightness);
                    leftBox.opacityProperty().set(brightness);
                }

            });
        }
    });
    rightopacity.addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue)
        {
            Platform.runLater(new Runnable()
            {

                @Override
                public void run()
                {
                    double brightness = rightopacity.doubleValue();
                    rightBrightness.setText(""+brightness);
                    rightBox.opacityProperty().set(brightness);
                }

            });
        }
    });

    startButton.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event)
        {
            if(running)
            {
                synchronized(this)
                {
                    running=false;
                }
                    startButton.setText("Start");
            }
            else
            {
                running=true;
                ftLeft = new FlickerThread((int)leftFrequency.getValue(),leftopacity);
                ftRight = new FlickerThread((int)rightFrequency.getValue(), rightopacity);

                try
                {
                    ftLeft.start(); 
                    ftRight.start();
                }
                catch(Throwable t)
                {
                    t.printStackTrace();
                }
                startButton.setText("Stop");
            }
        }
    });

    leftFrequency.valueProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue)
        {
            leftfreqlabel.setText(newValue.intValue()+"");
        }
    });

    rightFrequency.valueProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue)
        {
            rightfreqlabel.setText(newValue.intValue()+"");
        }
    });
}



class FlickerThread extends Service<Void>
{
    private long sleeptime;

    DoubleProperty localval = new SimpleDoubleProperty(1) ;

    public FlickerThread(int freq, DoubleProperty valtoBind)
    {
        this.sleeptime = (1/freq)*1000;
        valtoBind.bind(localval);
    }


    @Override
    protected Task <Void>createTask()
    {
        return new Task<Void>() {

            @Override
            protected Void call() throws Exception
            {
                while(running)
                {
                    double val =  Math.random();
                    System.out.println(val);
                    localval.setValue(val);

                    Thread.sleep(sleeptime);
                }
                return null;
            }
        };
    }

}
}
公共类控制器
{
@FXML
私有矩形左盒;
@FXML
私有矩形右盒;
@FXML
私有颜色选择器leftPrimary;
@FXML
私有颜色选择器;
@FXML
私有颜色选择器rightPrimary;
@FXML
私人色彩采集器;
@FXML
私人频率;
@FXML
专用频率;
@FXML
私人按钮开始按钮;
@FXML
自有品牌;
@FXML
自有品牌;
@FXML
自有品牌;
@FXML
自有品牌;
私有布尔运行=false;
DoubleProperty leftopacity=新的SimpleDoubleProperty(1);
DoubleProperty rightopacity=新的SimpleDoubleProperty(1);
私有闪烁线程左移;
私权;
公共无效初始化()
{
leftopacity.addListener(新的ChangeListener(){
@凌驾

public void changed(observevalue您的代码中有一个计算错误

考虑:

1/100*1000=0
但是:

i、 你需要使用浮点运算,而不是

正如我在前面的评论中所指出的,代码中还有许多其他问题,所以这个答案更像是代码审查和建议的方法,而不是其他方法

您可以像James的回答中那样批量更新到runLater。但是对于最大100赫兹的更新速率,它不会对性能产生很大影响,因为JavaFX通常以60赫兹的脉冲周期运行,除非您真的过载(在您的示例中,您并没有这样做)。因此,通过限制更新所节省的成本将非常小

下面是一个您可以尝试的示例(它使用James的输入节流技术):

导入javafx.application.*;
导入javafx.beans.property.DoubleProperty;
导入javafx.concurrent.Task;
导入javafx.geometry.Insets;
导入javafx.scene.scene;
导入javafx.scene.control.*;
导入javafx.scene.layout.*;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
导入java.util.Random;
导入java.util.concurrent.AtomicLong;
公共类InputApp扩展了应用程序{
专用最终切换按钮控制按钮=新切换按钮(“开始”);
私有最终矩形框=新矩形(100100,颜色为.BLUE);
专用最终标签亮度=新标签();
专用最终标签frequencyLabel=新标签();
专用最终滑块频率=新滑块(1100,10);
私人任务;
@凌驾
public void start(Stage)引发异常{
//初始化绑定。
brightness.textProperty().bind(
box.opacityProperty().asString(“%.2f”)
);
frequencyLabel.textProperty().bind(
frequency.valueProperty().asString(%.0f)
);
frequency.valueChangingProperty().addListener((可观察、旧值、新值)->{
如果(controlButton.isSelected()){
controlButton.fire();
}
});
//启动和停止输入任务。
controlButton.selectedProperty().addListener((可观察、已选择、已选择)->{
如果(当选){
任务=新任务(
(int)frequency.getValue(),
box.opacityProperty()
);
线程输入线程=新线程(任务,“输入任务”);
inputThread.setDaemon(true);
inputThread.start();
controlButton.setText(“停止”);
}否则{
如果(任务!=null){
task.cancel();
}
controlButton.setText(“开始”);
}
});
//创建布局
VBox布局=新的VBox(
10,
频率
新HBox(5,新标签(“频率”)、frequencyLabel,新标签(“Hz”),
控制按钮,
盒子,
新HBox(5,新标签(“亮度:”),亮度)
);
布局。设置填充(新插图(10));
//显示场景
舞台场景(新场景(布局));
stage.show();
}
//模拟以给定频率接受来自输入馈送的随机输入。
类输入任务扩展任务{
私有财产;可变财产;
私人最终长时间睡眠;
最终原子长计数器=新原子长(-1);
最终随机数=新随机数(42);
公共输入任务(int-inputFrequency,DoubleProperty-changeableProperty){
this.changeableProperty=changeableProperty;
this.sleeptime=(长)((1.0/输入频率)*1_000);
}
@凌驾
受保护的Void调用()引发InterruptedException{
长计数=0;
而(!Thread.interrupted()){
计数++;
double newValue=random.nextDouble();//输入模拟
if(计数器getAndSet(计数)=-1){
Platform.runLater(()->{
changeableProperty.setValue(newValue);
计数器getAndSet(-1);
});
}
睡眠(睡眠时间);
}
1.0/100*1000=10.0
import javafx.application.*;
import javafx.beans.property.DoubleProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;

public class InputApp extends Application {
    private final ToggleButton controlButton = new ToggleButton("Start");
    private final Rectangle box = new Rectangle(100, 100, Color.BLUE);
    private final Label brightness = new Label();
    private final Label frequencyLabel = new Label();
    private final Slider frequency = new Slider(1, 100, 10);
    private       InputTask task;

    @Override
    public void start(Stage stage) throws Exception {
        // initialize bindings.
        brightness.textProperty().bind(
                box.opacityProperty().asString("%.2f")
        );

        frequencyLabel.textProperty().bind(
                frequency.valueProperty().asString("%.0f")
        );

        frequency.valueChangingProperty().addListener((observable, oldValue, newValue) -> {
            if (controlButton.isSelected()) {
                controlButton.fire();
            }
        });

        // start and stop the input task.
        controlButton.selectedProperty().addListener((observable, wasSelected, isSelected) -> {
            if (isSelected) {
                task = new InputTask(
                        (int) frequency.getValue(),
                        box.opacityProperty()
                );
                Thread inputThread = new Thread(task, "input-task");
                inputThread.setDaemon(true);
                inputThread.start();

                controlButton.setText("Stop");
            } else {
                if (task != null) {
                    task.cancel();
                }

                controlButton.setText("Start");
            }
        });

        // create the layout
        VBox layout = new VBox(
                10,
                frequency,
                new HBox(5, new Label("Frequency: " ), frequencyLabel, new Label("Hz"),
                controlButton,
                box,
                new HBox(5, new Label("Brightness: " ), brightness)
        );
        layout.setPadding(new Insets(10));

        // display the scene
        stage.setScene(new Scene(layout));
        stage.show();
    }

    // simulates accepting random input from an input feed at a given frequency.
    class InputTask extends Task<Void> {
        private final DoubleProperty changeableProperty;
        private final long sleeptime;
        final AtomicLong counter = new AtomicLong(-1);
        final Random random = new Random(42);

        public InputTask(int inputFrequency, DoubleProperty changeableProperty) {
            this.changeableProperty = changeableProperty;
            this.sleeptime = (long) ((1.0 / inputFrequency) * 1_000);
        }

        @Override
        protected Void call() throws InterruptedException {
            long count = 0 ;
            while (!Thread.interrupted()) {
                count++;
                double newValue = random.nextDouble(); // input simulation
                if (counter.getAndSet(count) == -1) {
                    Platform.runLater(() -> {
                        changeableProperty.setValue(newValue);
                        counter.getAndSet(-1);
                    });
                }
                Thread.sleep(sleeptime);
            }

            return null;
        }
    }

    public static void main(String[] args) {
        System.out.println(1.0/100*1000);
    }
}
 class FlickerThread extends Thread
{
    private long sleeptime;
    final AtomicReference<Double> counter = new AtomicReference<>(new Double(-1.0));
    private Label label;
    private Rectangle myrect;
    public FlickerThread(int freq, Label label,Rectangle rect)
    {
        this.sleeptime = (long) ((1.0/freq)*1000.0);
        System.out.println("Sleep time is "+sleeptime);
        this.label = label;
        this.myrect = rect;
    }


    @Override
    public void run() {
        double count = 1.0 ;
        while (running) {
            count = Math.random();
            if (counter.getAndSet(count) == -1) {
                updateUI(counter, label,myrect);

                try
                {
                    Thread.sleep(sleeptime);
                } catch (InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

}
private void updateUI(final AtomicReference<Double> counter,
        final Label label, final Rectangle myrect) {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            double val = counter.getAndSet(-1.0);
            final String msg = String.format("Brt: %,f", val);
            label.setText(msg);
            myrect.opacityProperty().set(val);
        }
    });
}