another class is the controller, in a non-controller class of javafx, I can get the Label, but can't update the text value, and there is no any error... any one have the experience, pls help
trying to set the Label text value from a non-controller class
fxml:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.ning.fx1.HelloController">
<children>
<TextField fx:id="tf1" layoutX="66.0" layoutY="52.0" />
<TextField fx:id="tf2" layoutX="67.0" layoutY="102.0" />
<TextField fx:id="tf3" layoutX="68.0" layoutY="151.0" />
<Button fx:id="btn1" layoutX="117.0" layoutY="228.0" mnemonicParsing="false" onAction="#handlerBtn1" text="Button" />
<Label fx:id="label1" layoutX="402.0" layoutY="106.0" text="" />
<Label fx:id="label2" layoutX="429.0" layoutY="293.0" text="init" />
</children>
</AnchorPane>
this is the controller:
public class HelloController{
@FXML
public Label label1;
@FXML
public Label label2;
@FXML
private TextField tf1;
@FXML
private TextField tf2;
@FXML
private TextField tf3;
@FXML
private Button btn1;
@FXML
private void handlerBtn1(ActionEvent actionEvent) throws IOException, SchedulerException {
double num1 = Double.parseDouble(tf1.getText());
double num2 = Double.parseDouble(tf2.getText());
tf3.setText(String.valueOf(num1+num2));
Timer timer = new Timer();
label1.textProperty().bind(timer.messageProperty());
new Thread(timer).start();
JobDetail job1 = JobBuilder.newJob(QuartzJob.class)
.withIdentity("job1", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("job1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(4)
.repeatForever())
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job1,trigger);
label2.setText("handler");
}
}
I want update label2.
public class QuartzJob implements Job, Initializable {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("jobbbbbbbbbb");
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Parent root = null;
try {
root = fxmlLoader.<Parent>load();
} catch (IOException e) {
throw new RuntimeException(e);
}
HelloController controller = fxmlLoader.getController();
controller.label2.setText("2222222");
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
System.out.println("init");
}
}
from the quartz task class, I use "controller.label2.setText("2222222");" I debug it, it can get the Label object, but have no effect
another uncorrelated class:
public class Timer extends Task<String> {
@Override
protected String call() throws Exception {
for (int i=0; i<10; i++){
System.out.println("calling "+i);
Thread.sleep(1000);
updateMessage(String.valueOf(i));
}
return null;
}
@Override
protected void updateMessage(String s) {
super.updateMessage(s);
}
}
sorry, my code is nonsence
after
now I can update the element value by set the scene as a static object. but it looks weird ...
public class HelloApplication extends Application {
static Scene scene;
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
scene = new Scene(fxmlLoader.load(), 500, 500);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
// Thread.setDefaultUncaughtExceptionHandler((t, e) ->
// {
// System.out.println("ttttttttttttt"+t);
// System.out.println("eeeeeeeeeeeeeeeeeeeee"+e.getStackTrace());
// Alert alert = new Alert(Alert.AlertType.ERROR);
// alert.setTitle("Information Dialog");
// alert.setHeaderText("Look, an Information Dialog");
// alert.setContentText("I have a great message for you!");
//
// alert.showAndWait();
//
// }
//
// );
}
public static void main(String[] args) {
launch();
}
}
in a task class :
Label label = (Label)HelloApplication.scene.lookup("#label1");
label.textProperty().unbind();
label.setText("success");
------ in the end-----
actually, when I load the FXML again, then I get the Scene , but the scene is not the original scene.
I found there is many ways to finish it, such as us construsctor to pass the Label, thanks guys, this is not a good question.
Every time you call
FXMLLoader::loadyou are creating a new object graph and controller. That means the controller instance you "get" in your job is not the same instance attached to the UI. In other words, the label you are updating is not the one visible to the user. You need to find a way to pass the controller, or at least the label, to your job.I do not have much experience with Quartz, but from browsing the documentation and reading a few other Stack Overflow Q&As, there are three main ways to do this:
Put the FXML controller instance in the job's data map.
If you do this, make sure the scheduler's data store does not support persistence. Even if the FXML controller could be easily serialized, which should not be attempted, you would not get the correct FXML controller instance back when it is deserialized.
From what I can tell, the
RAMDataStoreimplementation, which I believe is the default, is the best for your use case.Put the FXML controller instance in the scheduler's context.
Set your own job factory on the scheduler so that you can manually pass the FXML controller instance to your job when the factory is invoked.
For approaches 1 and 2, you can get the stored object in the job via the
JobExecutionContext.I also recommend you use a proper application architecture such as MVC or MVVM. Ideally, Quartz should not know about JavaFX and your view classes should not know about Quartz. And if you use MVC, MVVM, or similar, then you would pass the model to the Quartz job, not the FXML controller (nor any other view class). Additionally, a Quartz job is not an FXML controller, meaning it makes no sense for the job class to implement
Initializable.That all said, Quartz seems like overkill for what you are trying to do. It would probably be much easier to use JavaFX's
ScheduledServiceor even standard Java'sScheduledExecutorService. Though in both cases, you will still need a way to reference the correct FXML controller (or model) instance.And finally, regardless of which technology you use, you must ensure the label's text is updated on the JavaFX Application Thread.
Example
Here is an example of passing an FXML controller to a Quartz job via the job's data map. Note it is only meant to demonstrate passing "custom" objects to a job, not necessarily how to structure an application.
MainView.java
MainView.fxml
UpdateLabelJob.java
Main.java