簡體   English   中英

JavaFX-線程掛起,我不知道如何在UI線程外運行循環

[英]JavaFX - Thread hangs, and I can't figure out how to run my loop outside the UI thread

朋友。 這是stackoverflow的新功能,也是JavaFX的新功能。 抱歉,如果我弄亂了任何命名法,並且有點冗長,但是我確實將代碼縮減為一個最小的“有效”示例。 (下面的代碼會掛起,除非您注釋掉while循環,如代碼本身所述)

我試圖弄清楚如何使Slider在單擊“喚醒”按鈕后連續更改其值,並在單擊“睡眠”按鈕后停止更改值。 為了便於討論,可以說它隨機將值更改為0到99之間的整數。

從我這么 (我已經讀了很多,但這些都是最近的兩個例子),看來我應該能夠使用Platform.runLater()updateMessenger()來避免運行無限-直到UI線程上的my-sleep-button-breaks-it循環為止。 到目前為止,我沒有實現前者,但是我仍然不確定如何實現后者。

我還嘗試學習有關屬性綁定的信息,以便可以在更改中間變量(我稱為state並創建)時在UI線程中更改Slider.valueProperty (在UI線程中更改UI元素是有意義的...對嗎?)。從背景線程上的stateObject類中stateObject 后台線程計算完要提供給滑塊的值后,它將更改state的值,該值綁定到Slider的值。 我以為將值計算分為后台線程,將Slider值更改為UI線程可以解決問題,但我一直遇到掛斷或臭名昭著的IllegalStateException異常, Not on FX application thread

我不完全確定Platform.runLater()魔術應該如何工作,但是無論我在其中運行可運行接口還是在Thread() ,我仍然遇到麻煩。

對於這些嘗試為什么失敗/如何使事情起作用的任何幫助或解釋,將不勝感激。

干杯,邁克

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;

//Not sure which of these, if any, I actually need
import javafx.event.*;
import javafx.concurrent.*;
import javafx.beans.property.*;

public class MWE extends Application {

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

    //Class fields
    Button sleepBtn, wakeBtn;
    Slider displaySldr;
    boolean wakeful = true;

    //state property class
    class stateObject {
        //Store the property
        private DoubleProperty state = new SimpleDoubleProperty();
        //Define getter for property value
        public final double getState() {
            return state.get();
        }
        //Define setter for property value
        public final void setState(double val) {
            state.setValue(val);
        }
        //Define getter for property itself
        public DoubleProperty stateProperty() {
            return state;
        }
    }

    //Create state
    stateObject state = new stateObject();

    //Start method
    @Override
    public void start(Stage primaryStage) {

        //Create top-level pane to store nodes into for the scene
        VBox mainPane = new VBox(20);
        mainPane.setAlignment(Pos.CENTER);

        //Create buttons
        sleepBtn = new Button("Sleep");
        sleepBtn.setOnAction(e -> NPSleep());
        wakeBtn = new Button("Wake");
        wakeBtn.setOnAction(e -> NPWake());

        //Lay out buttons
        VBox buttonsBox = new VBox(10);
        buttonsBox.getChildren().addAll(wakeBtn,sleepBtn);
        buttonsBox.setAlignment(Pos.CENTER);

        //Create slider to be change continuously by clicking wakeBtn once
        //Slider should stop changing when sleepBtn is clicked
        displaySldr = new Slider(0,100,50);
        displaySldr.setMajorTickUnit(25);
        displaySldr.setMinorTickCount(24);
        displaySldr.setSnapToTicks(true);
        displaySldr.setShowTickMarks(false);
        displaySldr.setShowTickLabels(true);

        //Make property event. When 'state' changes, change displaySldr value
        state.stateProperty().addListener(
            (observable,oldvalue,newvalue) ->
                    displaySldr.setValue(newvalue.intValue())
        );


        //Organize labels and sliders
        VBox LSPane = new VBox(10);
        LSPane.getChildren().addAll(displaySldr);
        LSPane.setAlignment(Pos.CENTER);

        //Organize sub-panes into top-level pane, mainPane
        mainPane.getChildren().addAll(LSPane,buttonsBox);

        //Set the scene
        Scene mainScene = new Scene(mainPane);

        //Set the stage
        primaryStage.setTitle("oh god please work");
        primaryStage.setScene(mainScene);
        primaryStage.show();

    }

    private void NPWake() {

        Runnable wakeyWakey = () -> {
            while (wakeful == true) { //Commenting out the while loop and 
                                        //leaving the command allows program to run fine
                                        //but slider changes only once, not continuously
                state.setState( Math.floor(Math.random()*100) );

                try{
                    Thread.sleep(300);
                } catch (InterruptedException ie) {
                }
            }
        };

        Platform.runLater(wakeyWakey);
//        Thread wakeThread = new Thread(wakeyWakey); //
//        wakeThread.start();                         // This doesn't work either
    }


    private void NPSleep() {
        if (wakeful == true) {
            wakeful = false;
        }
    }

}

您不需要具有State Object ,因為您仍然必須更新JavaFX Application Thread上的值,因此它違背了創建此類對象的想法。 您可以刪除所有與State Object相關的代碼。 以下代碼是您的解決方案的修復程序:

private void NPWake() {
    Thread t = new Thread(() -> {
        while (wakeful) {
            try {
                Platform.runLater(() -> {
                    displaySldr.setValue(Math.floor(Math.random() * 100));
                });

                Thread.sleep(300);
            }
            catch (Exception e) {}
        }
    });
    t.start();
}

Button onAction是在JavaFX Application Thread上處理的,在任何情況下,除非它是它自己的內部結構,否則我們都不希望在該線程上運行無限循環,因為該循環只會使該線程掛起。 因此,我們創建了一個new Thread t ,該new Thread t將具有該循環,並且在其主體中將調用JavaFX Application Thread以提交更改。 收集到的信息,必須在JavaFX Application Thread上對UI / JavaFX屬性進行更改。 Platform.runLater( "some code" )意味着將來將在JavaFX Application Thread上(如果線程不忙,幾乎是立即執行)執行“某些代碼”。

至於此類應用程序的設計,我建議您看看AnimationTimer - https: //docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.htmlTimeline - https:/ /docs.oracle.com/javase/8/javafx/api/javafx/animation/Timeline.html 它們都在JavaFX Application Thread上運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM