从另一个线程javafx更新ImageView [英] Updating an ImageView from another thread javafx

查看:171
本文介绍了从另一个线程javafx更新ImageView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家。
我有一个程序,它应该自动控制一些机器。我需要javaFX来展示研讨会的临时状态。有几个进程一个接一个地执行,对于每个进程我需要更新屏幕上的图像(让我们更简单,并说我们需要更新标签)。

everybody. I have a program, which should automatically control some machinery. I need javaFX to show the temprorary state of the workshop. There are several processes executing one after another and for each of them i need to update an image on the screen (let's make it simplier and say we need to update a label).

因此,有一个主线程,它控制机器,并有一个FX应用程序线程,它控制GUI。

So, there is a main thread, which controls the machinery and there is an FX application Thread, which controls the GUI.

     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
            }

我在SO和Oracle的文档上阅读了大量的资料,现在我无法理解所有这些绑定,可观察的属性,Platform.runLater,Tasks,检索控制器,传递控制器作为某个类的参数等

I've read a whole bunch of materials here on SO and on the Oracle's docs and now I can't make sense out of all these bindings, observable properties, Platform.runLater, Tasks, retrieving the controller, passing controller as a parameter to some class etc

我有一个fxml文件,假设它只显示一个标签:

I have an fxml file, let's say it only shows a label:

    <?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>

附有一个控制器。我认为这是我们应该倾听图像变化的地方。

There is a controller attached to it. I assume that is the place where we should listen for the change of the image or something.

package sample;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;

import java.net.URL;
import java.util.ResourceBundle;

public class Controller implements Initializable {

  @FXML
  public void initialize(URL location, ResourceBundle resources) {
    //some listeners?
  }

  @FXML
  private Label label;

  public void setlabel(String s) {
    label.setText(s);
  }
}

并且有一个Display.java用作启动机制。

And there is a Display.java which is used as a start up mechanism.

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()?有很多信息如何在控制器之间传递数据,如何调用控制器中的方法,但我完全迷失了我的问题。

And, finally, the question is: how to update the label in the controller from the main()? There is a whole lot of info how to pass data between controllers, how to invoke methods in the controller, but I'm totally lost with my question.

推荐答案

您应该将 start()方法视为应用程序入口点,而不是 main(...)方法,所以你应该从 start()启动你的其他线程(控制机器),而不是从启动main()的。这样你甚至没有在 main()中检索对控制器的引用的问题(你基本上不能这样做,因为你无法获得对那里的应用程序子类实例)。 main()方法应该通过调用 Application.launch()来简单地引导JavaFX工具包的启动,并且没有其他的。 (请注意,在某些部署方案中,甚至不会调用 main(...)方法,并且应用程序子类的 start()方法由其他机制调用。)

You should think of the start() method as the application entry point, not the main(...) method, and so you should launch your other threads (that control the "machinery") from start(), not from main(). That way you don't even have the issue of retrieving the reference to the controller in main() (which you basically cannot do, since you can't get the reference to the Application subclass instance there). The main() method should simply bootstrap the launch of the JavaFX toolkit by calling Application.launch(), and do nothing else. (Note that in some deployment scenarios, your main(...) method is not even called, and the Application subclass's start() method is invoked by other mechanisms.)

所以重构如下:

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完全分开(这可能是个好主意),那么这样做并不难。把机器放在另一个班级。标签的更新实际上是消耗(处理) String 的东西(并且为了更新图像,它可能会消耗一些其他类型的数据)。您可以通过将其表示为 java.util.Consumer< String> 来实现此抽象。所以你可以做到


If you want to separate the machinery completely from the UI (which is probably a good idea), it's not too hard to do this. Put the machinery in another class. The update of the label is effectively something that consumes (processes) a String (and for updating the image, it might consume some other kind of data). You can make this abstraction by representing it as a java.util.Consumer<String>. So you could do

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
    }
}

注意本课程完全独立于UI。您的开始(..)方法现在将

Note this class is completely independent of the UI. Your start(..) method would now be

  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();

    Machinery machinery = new Machinery(text ->
        Platform.runLater(() -> controller.setLabel(text)));

    Thread machineryThread = new Thread(machinery::doMachineryWork);
    machineryThread.start();
  }

根据应用程序结构的其他方面,它也可能有意义从控制器的 initialize()方法启动机械线程,而不是从 start()方法启动。

Depending on other aspects of how your application is structured, it might also make sense to start the machinery thread from the controller's initialize() method, instead of from the start() method.

这篇关于从另一个线程javafx更新ImageView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆