2014-06-30 166 views
0

我有一些信號處理數據大致以50Hz的頻率輸入。我需要根據信號值實時更新矩形的不透明度。我正在嘗試在JavaFX 8中開發UI。Java FX8用戶界面更新延遲

暫時我正在模擬使用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

不綁定與被修改的屬性在場景圖中的東西(例如,節點不透明度)在另一個線程(它可能會導致不可預知的行爲)。另外,我認爲你不應該在不透明度改變偵聽器中運行調用,也不應該在JavaFX應用程序線程上同步語句。這只是一個快速審查,你可能有其他問題。此外,leftFrequency滑塊的值範圍是多少? – jewelsea

+0

兩個滑塊的頻率範圍在1-100之間 – abacusreader

+0

我發現這個[http://stackoverflow.com/questions/23488280/throttling-javafx-gui-updates],我正在修改我的代碼來使用AtomicReference來實現我想要更新 – abacusreader

回答

0

您的代碼中存在計算錯誤。

考慮:

1/100*1000=0 

但是:

1.0/100*1000=10.0 

即你需要使用浮點運算,而不是integer arithmetic

在我之前的評論中,您的代碼有很多其他問題,所以這個答案更像是代碼審查和建議方法。

您可以按照James的回答Throttling javafx gui updates批量更新runLater。但是對於最大100赫茲的更新速率,JavaFX通常在60赫茲的脈衝週期上運行時,性能方面不會有太大的差異,除非你真的超載了它(你並沒有真正在做你的工作例)。所以節流更新所節省的費用將非常小。

這裏有一個例子,你可以嘗試(它使用詹姆斯的輸入節流技術):

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

非常感謝您花時間解決我的問題,您對我的睡眠時間計算是正確的。然而,UI滯後的原始問題已由我在下面發佈的代碼解決。 – abacusreader

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

只是一個更新,我重新測試了上面的代碼,沒有使用Atomic引用,UI更新與原子引用一樣好。它看起來更像是正確實施Platform.runlater。 – abacusreader