【问题标题】:Updating an ImageView from another thread javafx从另一个线程 javafx 更新 ImageView
【发布时间】:2017-07-20 00:38:50
【问题描述】:

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

所以,有一个主线程,它控制机器,还有一个 FX 应用程序线程,它控制 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、任务、检索控制器、将控制器传递为某个类的参数等

我有一个 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>

有一个控制器连接到它。我认为那是我们应该倾听图像变化或其他东西的地方。

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用作启动机制。

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

【问题讨论】:

    标签: java multithreading javafx


    【解决方案1】:

    您应该将start() 方法视为应用程序入口点,而不是main(...) 方法,因此您应该从start() 而非@987654324 启动其他线程(控制“机器”) @。这样,您甚至没有在main() 中检索对控制器的引用的问题(您基本上不能这样做,因为您无法在此处获得对Application 子类实例的引用)。 main() 方法应该通过调用 Application.launch() 简单地引导 JavaFX 工具包的启动,而不做任何其他事情。 (注意在某些部署场景下,你的main(...)方法甚至没有被调用,Application子类的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 完全分离(这可能是一个好主意),这样做并不难。把机器放在另一个班级。标签的更新实际上是消耗(处理)String(并且为了更新图像,它可能会消耗一些其他类型的数据)。您可以通过将其表示为java.util.Consumer&lt;String&gt; 来进行此抽象。所以你可以这样做

    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。你的 start(..) 方法现在是

      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() 方法启动机械线程也可能有意义。

    【讨论】:

      【解决方案2】:

      这是一个您可以尝试的可执行示例。它遵循詹姆斯回答中概述的一些原则,因此我不会在 cmets 方面添加太多额外内容。如果您还有其他问题,请在答案下方的 cmets 中提问。

      有很多方法可以解决这个问题,这只是说明了我想出的一个简单的例子(例如 James 的答案中的消费者机制比这个答案中的事件通知机制更优雅)。它可能不是您的案例的最佳结构,但希望它能为您提供一些关于如何解决问题的见解。

      示例程序提供了一个工厂视图,其中工厂由四台机器组成。每台机器可以处于空闲状态或烘焙状态,并且每台机器的状态独立更改,工厂中的每台机器都在自己的线程上运行。提供了整个工厂的图形视图。工厂中的机器视图列出了每台机器的机器 ID 和当前机器状态。提供了一个通知界面,以便图形视图可以动态地了解底层机器状态的任何变化并适当地更新自身。为确保图形视图在 JavaFX 应用程序线程上更新,Platform.runLater 用于在收到机器状态更改通知事件时在 JavaFX 应用程序线程上运行视图更新。

      import javafx.application.*;
      import javafx.geometry.Insets;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.layout.*;
      import javafx.stage.Stage;
      
      import java.util.*;
      import java.util.concurrent.*;
      
      public class FactoryConsole extends Application {
          private Factory factory = new Factory();
      
          @Override
          public void start(Stage stage) throws Exception {
              VBox layout = new VBox(10);
              layout.setPadding(new Insets(10));
              layout.setPrefSize(100, 100);
      
              for (Machine machine: factory.getMachines()) {
                  MachineView machineView = new MachineView(machine);
                  layout.getChildren().add(machineView);
              }
      
              factory.start();
      
              stage.setScene(new Scene(layout));
              stage.show();
          }
      
          @Override
          public void stop() throws Exception {
              factory.stop();
          }
      
          public static void main(String[] args) {
              launch(args);
          }
      }
      
      enum MachineState {
          IDLE, BAKING
      }
      
      class MachineStateChangeEvent {
          private final Machine machine;
          private final MachineState machineState;
      
          public MachineStateChangeEvent(Machine machine, MachineState machineState) {
              this.machine = machine;
              this.machineState = machineState;
          }
      
          public Machine getMachine() {
              return machine;
          }
      
          public MachineState getMachineState() {
              return machineState;
          }
      }
      
      interface MachineStateListener {
          void notifyStateChange(MachineStateChangeEvent machineState);
      }
      
      class MachineView extends HBox implements MachineStateListener {
          private final Machine machine;
          private final Label label;
      
          public MachineView(Machine machine) {
              super();
              this.label = new Label();
              this.machine = machine;
              machine.setMachineStateListener(this);
      
              getChildren().add(label);
          }
      
          @Override
          public void notifyStateChange(MachineStateChangeEvent event) {
              if (event.getMachine() != machine) {
                  return;
              }
      
              if (!Platform.isFxApplicationThread()) {
                  Platform.runLater(() -> updateState(event.getMachineState()));
              } else {
                  updateState(event.getMachineState());
              }
          }
      
          private void updateState(MachineState machineState) {
              label.setText(machine.getId() + ": " + machineState.toString());
          }
      }
      
      class Factory {
          private static final int N_MACHINES = 4;
          private ExecutorService pool = Executors.newFixedThreadPool(N_MACHINES);
          private List<Machine> machines = new ArrayList<>();
      
          public Factory() {
              for (int i = 0; i < N_MACHINES; i++) {
                  machines.add(new Machine());
              }
          }
      
          public void start() {
              for (Machine machine: machines) {
                  pool.submit(machine);
              }
          }
      
          public void stop() {
              // Disable new tasks from being submitted
              pool.shutdown();
              try {
                  // Wait a while for existing tasks to terminate
                  if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
                      pool.shutdownNow();
                      // Cancel currently executing tasks
                      // Wait a while for tasks to respond to being cancelled
                      if (!pool.awaitTermination(5, TimeUnit.SECONDS))
                          System.err.println("Pool did not terminate");
                  }
              } catch (InterruptedException ie) {
                  // (Re-)Cancel if current thread also interrupted
                  pool.shutdownNow();
                  // Preserve interrupt status
                  Thread.currentThread().interrupt();
              }
          }
      
          public List<Machine> getMachines() {
              return machines;
          }
      }
      
      class Machine implements Runnable {
          private static final Random random = new Random();
          private static int nextMachineId = 1;
      
          private int id = nextMachineId++;
      
          private MachineState state = MachineState.IDLE;
          private MachineStateListener stateListener;
      
          public void setMachineStateListener(MachineStateListener stateListener) {
              this.stateListener = stateListener;
          }
      
          @Override
          public void run() {
              try {
                  updateState(MachineState.IDLE);
                  while (true) {
                      Thread.sleep(1000 * random.nextInt(2));
                      updateState(MachineState.BAKING);
                      Thread.sleep(1000 * random.nextInt(3));
                      updateState(MachineState.IDLE);
                  }
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
              } catch (Throwable t) {
                  t.printStackTrace();
              }
          }
      
          private void updateState(MachineState state) {
              this.state = state;
              if (stateListener != null) {
                  stateListener.notifyStateChange(new MachineStateChangeEvent(this, state));
              }
          }
      
          public int getId() {
              return id;
          }
      
          public MachineState getState() {
              return state;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-24
        • 2017-10-07
        • 2014-07-22
        • 2017-01-04
        • 1970-01-01
        • 1970-01-01
        • 2020-09-30
        相关资源
        最近更新 更多