在我的JavaFX应用程序中,有一个标签,我想使用另一个定义服务器功能的类的StringProperty更新。
在服务器类中,我定义了一个StringProperty,如下所示:
public class eol_server {
private StringProperty serverMessagesProperty;
etc..
我有一个根据要求返回StringProperty的方法:
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
在主gui类中,使用Start()方法构建Scene,然后实例化一个新的服务器对象。之后,通过将其绑定到服务器对象的StringProperty来更新场景图中的标签之一:
public void start(Stage primaryStage) {
...set up all the gui components
primaryStage.show();
dss = new eol_server();
lbl_dssMessage.textProperty().bind(dss.getServerMessagesProperty());
}
当我运行应用程序时,将按预期呈现场景,并且将lbl_dssMessage文本设置为在eol_server的构造函数中设置的值。但是,从那时起,绑定就无法正常工作,尽管我有一些操作会更新dss对象的StringProperty,但它们并没有更新GUI中的标签。
以下是生成场景的精简版本的完整文件:
public class eol_gui extends Application {
private static eol_server dss = null;
private static Stage primaryStage;
/**
* Application Main Function
* @param args
*/
public static void main(String[] args) {
launch(args);
}
/**
* @param stage
*/
private void setPrimaryStage(Stage stage) {
eol_gui.primaryStage = stage;
}
/**
* @return Stage for GUI
*/
static public Stage getPrimaryStage() {
return eol_gui.primaryStage;
}
/* (non-Javadoc)
* @see javafx.application.Application#start(javafx.stage.Stage)
*/
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
BorderPane root = new BorderPane();
Label lbl_dssMessage = new Label("initialized");
HBox topMenu = new HBox();
topMenu.getChildren().add(lbl_dssMessage);
eol_supervisor_I config1 = new eol_supervisor_I();
// Build Scene
root.setStyle("-fx-background-color: #FFFFFF;");
root.setTop(topMenu);
primaryStage.setScene(new Scene(root, 300, 50));
primaryStage.show();
dss = new eol_server();
dss.getServerMessagesProperty().addListener(new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<?> o,Object oldVal,Object newVal)
{
//Option 1: lbl_dssMessage.setText(newVal.toString());
}
});
//Option 2
lbl_dssMessage.textProperty().bind(dss.serverMessagesProperty);
}
}
如您所见,我已经尝试了bind方法和一个更改侦听器。在所有情况下,除了在服务器服务线程中运行的那些情况外,似乎bind和侦听器都在工作。由于不在JavaFX应用程序主线程上,因此它们抛出IllegalStateException。如何安全正确地从服务向主线程交换消息?
服务器在以下类中定义,该类旨在运行独立于JavaFX主线程的服务。但是我想在线程之间交换信息以显示状态。我正在尝试避免在进行服务器连接和数据交换时GUI挂起。
public class eol_server {
public StringProperty serverMessagesProperty;
public eol_server() {
/* Create Scripting Environment */
serverMessagesProperty().set("Establishing Debug Server Environment");
connect();
}
public boolean connect() {
serverMessagesProperty().set("Creating Server");
ConnectService connectService = new ConnectService();
connectService.start();
return false;
}
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
public StringProperty getServerMessagesProperty() {
return serverMessagesProperty;
}
private class ConnectService extends Service<Void> {
@Override
protected void succeeded() {
}
@Override
protected void failed() {
}
@Override
protected void cancelled() {
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
serverMessagesProperty().set("Connecting....");
Thread.sleep(5000);
// DEMO: uncomment to provoke "Not on FX application thread"-Exception:
// connectButton.setVisible(false);
serverMessagesProperty().set("Waiting for server feedback");
Thread.sleep(5000);
return null;
}
};
}
}
private class DisconnectService extends Service<Void> {
@Override
protected void succeeded() {
}
@Override
protected void cancelled() {
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
updateMessage("Disconnecting....");
serverMessagesProperty().set("Disconnecting....");
Thread.sleep(5000);
updateMessage("Waiting for server feedback.");
serverMessagesProperty().set("Waiting for server feedback.");
Thread.sleep(5000);
return null;
}
};
}
}
}
预先感谢
JW
答案 0 :(得分:0)
我似乎有一个解决方案,该解决方案源于我在runlater()Platform方法上阅读的内容。另请参阅此处的讨论
Multi-Threading error when binding a StringProperty
更改我的侦听器以通过runLater调用消息更新似乎可以解决多线程问题。是的,我希望它不是立竿见影的,但它将非常接近立竿见影且足够好。我理解JavaFX要求您不要弄乱场景图的Application线程值/节点等,但这是JavaFX库中相当复杂的部分。
这是为我工作的听众
// Get new DSS session active
dss = new eol_server();
dss.serverMessagesProperty.addListener(new ChangeListener<Object>() {
@Override
public void changed (ObservableValue<?> observable, Object oldValue, Object newValue) {
Platform.runLater(() -> lbl_dssMessage.setText(newValue.toString()));
}
});
}
很高兴进一步讨论在其他选项不起作用的情况下为何可行,这也欢迎其他更优雅的建议。
答案 1 :(得分:0)
从JavaFX Node文档中阅读以下段落。
Node对象可以在任何线程上构造和修改,只要 它们尚未附加到正在显示的窗口中的场景。一个 应用程序必须将节点附加到这样的场景或在 JavaFX应用程序线程。
解决问题的正确且实用的方法是
dss.serverMessagesProperty.addListener((observable, oldValue, newValue) -> Platform.runLater(() -> lbl_dssMessage.setText(newValue));