我有一些信号处理数据,大约以50Hz进给。我需要实时根据信号值更新矩形的不透明度。我正在尝试在JavaFX 8中开发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;
}
};
}
}
}
答案 0 :(得分:1)
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);
}
});
}
答案 1 :(得分:0)
您的代码中存在计算错误。
考虑:
1/100*1000=0
可是:
1.0/100*1000=10.0
即。你需要使用浮点运算,而不是integer arithmetic。
我的上一条评论中指出了您的代码还存在许多其他问题,因此这个答案更多的是代码审核和建议的方法,而不是其他任何问题。
您可以按照James对Throttling javafx gui updates的回答批量更新runLater。但是对于最大100赫兹的更新速率,由于JavaFX通常在60赫兹的脉冲周期上运行,因此它不会在性能方面产生很大差异,除非你真的超载它(你实际上没有在你的例)。因此,通过限制更新获得的节省将是非常小的。
以下是您可以尝试的示例(它使用James的输入限制技术):
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);
}
}