繁体   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