从另一个线程javafx更新ImageView
大家好。 我有一个程序,可以自动控制一些机器。我需要javaFX来显示研讨会的临时状态。有几个进程一个接一个地执行,对于每个进程,我都需要更新屏幕上的图像(让我们简化一下,说我们需要更新标签) 因此,有一个主线程控制机器,还有一个FX应用程序线程控制GUI从另一个线程javafx更新ImageView,java,multithreading,javafx,Java,Multithreading,Javafx,大家好。 我有一个程序,可以自动控制一些机器。我需要javaFX来显示研讨会的临时状态。有几个进程一个接一个地执行,对于每个进程,我都需要更新屏幕上的图像(让我们简化一下,说我们需要更新标签) 因此,有一个主线程控制机器,还有一个FX应用程序线程控制GUI public static void main(String[] args) { //some processes in the main thread before launching GUI (like connecting
public static void main(String[] args) {
//some processes in the main thread before launching GUI (like connecting to the database)
Thread guiThread = new Thread() {
@Override
public void run() {
DisplayMain.launchGUI();
}
};
guiThread.start();
//some processes after launching the GUI, including updating the image on the screen
}
我在这里和Oracle的文档上读了一大堆资料,现在我无法理解所有这些绑定、可观察属性、Platform.runLater、任务、检索控制器、将控制器作为参数传递给某个类等等
我有一个fxml文件,假设它只显示一个标签:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Label?>
<GridPane alignment="center"
hgap="10" vgap="10"
xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8"
fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<Pane prefHeight="200.0" prefWidth="200.0">
<children>
<Label fx:id="label" text="Label" />
</children>
</Pane>
</children>
</GridPane>
还有一个Display.java,用作启动机制
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Display extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(loader.load(), 800, 400));
primaryStage.show();
}
static void launchGUI() {
Application.launch();
}
}
最后,问题是:如何从main()更新控制器中的标签?关于如何在控制器之间传递数据,如何调用控制器中的方法,有很多信息,但是我完全不懂我的问题。你应该把
start()
方法看作是应用程序的入口点,而不是main(…)
方法,因此你应该启动其他线程(控制“机器”)从start()
,而不是从main()
。这样,您甚至不存在在main()
中检索对控制器的引用的问题(这基本上是无法做到的,因为您无法获取对应用程序子类实例的引用)。main()
方法只需通过调用Application.launch()
引导JavaFX工具包的启动,而无需执行其他操作。(请注意,在某些部署场景中,甚至不会调用main(…)
方法,而应用程序的子类的start()
方法由其他机制调用。)
因此,重构如下:
public class Main { // or whatever you called it...
public static void main(String[] args) {
Application.launch(Display.class, args);
}
}
然后在开始时:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Display extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(loader.load(), 800, 400));
primaryStage.show();
Controller controller = loader.getController();
Thread machineryThread = new Thread(() -> {
// some processes launching after the GUI, including updating the label
// which you can now easily do with
Platform.runLater(() -> controller.setLabel("Some new text"));
});
machineryThread.start();
}
}
如果您想将机器与UI完全分离(这可能是个好主意),那么这样做并不难。把机器换成另一类。标签的更新实际上是使用(处理)一个字符串的东西(对于更新图像,它可能会使用其他类型的数据)。您可以通过将其表示为java.util.Consumer
来实现此抽象。所以你可以
public class Machinery {
private final Consumer<String> textProcessor ;
public Machinery(Consumer<String> textProcessor) {
this.textProcessor = textProcessor ;
}
public void doMachineryWork() {
// all the process here, and to update the label you do
textProcessor.accept("Some new text");
// etc etc
}
}
根据应用程序结构的其他方面,从控制器的initialize()
方法而不是从start()
方法启动机器线程也可能有意义。您应该将start()
方法作为应用程序入口点,而不是main(…)
方法,因此您应该从start()
而不是从main()
启动其他线程(控制“机器”)。这样,您甚至不存在在main()
中检索对控制器的引用的问题(这基本上是无法做到的,因为您无法获取对应用程序子类实例的引用)。main()
方法只需通过调用Application.launch()
引导JavaFX工具包的启动,而无需执行其他操作。(请注意,在某些部署场景中,甚至不会调用main(…)
方法,而应用程序的子类的start()
方法由其他机制调用。)
因此,重构如下:
public class Main { // or whatever you called it...
public static void main(String[] args) {
Application.launch(Display.class, args);
}
}
然后在开始时:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Display extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(loader.load(), 800, 400));
primaryStage.show();
Controller controller = loader.getController();
Thread machineryThread = new Thread(() -> {
// some processes launching after the GUI, including updating the label
// which you can now easily do with
Platform.runLater(() -> controller.setLabel("Some new text"));
});
machineryThread.start();
}
}
如果您想将机器与UI完全分离(这可能是个好主意),那么这样做并不难。把机器换成另一类。标签的更新实际上是使用(处理)一个字符串的东西(对于更新图像,它可能会使用其他类型的数据)。您可以通过将其表示为java.util.Consumer
来实现此抽象。所以你可以
public class Machinery {
private final Consumer<String> textProcessor ;
public Machinery(Consumer<String> textProcessor) {
this.textProcessor = textProcessor ;
}
public void doMachineryWork() {
// all the process here, and to update the label you do
textProcessor.accept("Some new text");
// etc etc
}
}
根据应用程序结构的其他方面,从控制器的initialize()
方法而不是从start()
方法启动机器线程也可能有意义。这里是一个可执行示例,您可以尝试。它遵循了James回答中概述的一些原则,所以我不会在评论方面添加太多额外的内容。如果你还有其他问题,请在回答下面的评论中提问
有很多方法可以解决这个问题,这只是说明了我提出的一个快速示例(例如,James答案中的消费者机制比这个答案中的事件通知机制更优雅)。对于您的案例来说,它可能不是一个最佳结构,但希望它能为您提供一些关于如何着手解决问题的见解
示例程序提供工厂视图,其中工厂由四台机器组成。每台机器可以处于空闲状态,也可以处于烘焙状态,并且每台机器的状态都会独立改变,工厂中的每台机器都在其自己的线程上运行。提供了整个工厂的图形视图。工厂中的机器视图列出了每台机器的机器id和当前机器状态。提供了一个通知界面,以便图形视图能够动态地了解底层机器状态的任何更改,并适当地更新自身。为确保在JavaFX应用程序线程上更新图形视图,当收到机器状态更改通知事件时,用于在JavaFX应用程序线程上运行视图更新
导入javafx.application.*;
导入javafx.geometry.Insets;
导入javafx.scene.scene;
导入javafx.scene.control.Label;
导入javafx.scene.layout.*;
导入javafx.stage.stage;
导入java.util.*;
导入java.util.concurrent.*;
公共类FactoryConsole exte