2015-01-07 101 views
0

我想在自己的线程中有一个JavaFX窗口对象,我可以从外部实例化和更新(通常每秒钟10-20次)。该窗口应该可以查看图像并可以从其他类更新。实例化JavaFX窗口

主类

ImageViewer window = new ImageViewer("Preview"); 
window.show(); 
window.updateFrame(image); 

图像浏览器类

public class ImageViewer extends Application{ 

private StackPane pane; 
private ImageView imgView; 
private Stage primaryStage; 

public ImageViewer() { 
} 

public void start(Stage primaryStage) { 
    this.primaryStage = primaryStage; 
    pane = new StackPane(); 
    imgView = new ImageView(); 
    pane.getChildren().add(imgView); 
    Scene scene = new Scene(pane); 
    primaryStage.setScene(scene); 
    primaryStage.show(); 
} 

public void updateFrame(Image image) { 
    imgView = new ImageView(image); 
} 

public void show() { Application.launch(); } 

构造遗憾的是不带任何参数,当我尝试在此对象设置任何值,没有任何反应。为什么是这样?

+0

'start(...)'方法是您的应用程序应该启动的地方。 'Application.launch()'方法将为你创建一个你的'Application'子类的实例。如果你想要从应用程序的其他部分重用的类,它们不应该是'Application'的子类。您可能需要重新构建此应用程序。 –

+0

你介意多解释一下吗?我无法完全理解JavaFX的概念。我怎样才能从我的主要应用程序分开(线程明智)窗口加载图像? – TomTom

+0

你的问题并没有什么意义:像几乎所有的UI工具包一样,JavaFX是单线程的,所有UI元素都必须从FX应用程序线程访问。我将尝试展示我将如何编写您所描述的应用程序的结构。 –

回答

2

Application类表示整个应用程序,其start(...)方法是在应用程序启动(你真的应该想start(...)法作为main(...)方法在传统的Java应用程序的等效)的方法。你的问题似乎意味着你认为Application类代表一个窗口; Stage类代表一个窗口。但是,据我所知,您的应用程序无论如何都只需要一个窗口。

当你调用Application.launch()(或Java中8只执行JVM指定Application子类作为主类),外汇工具包创建您的Application类本身的实例,启动FX工具包和FX应用程序线程,以及调用在Application实例上的start(...)方法,传入Stage。你应该永远不要实例化你自己的Application子类;如果你这样做,你将会有一个与start(...)方法被调用的实例不同的实例。

与几乎所有的UI工具包一样,JavaFX是单线程的。必须在JavaFX应用程序线程上执行“活动”场景图元素上的所有操作。您当然可以拥有后台线程,但是如果他们需要更新UI,他们应该通过调用Platform.runLater(...)或利用javafx.concurrent API来安排FX应用程序线程上的更新。 (短语“在自己的线程中有一个JavaFX Window对象”对我来说甚至没有意义,线程没有对象,堆中存在对象,线程只是独立序列的可执行语句的抽象。

所以,如果你想有一个JavaFX应用程序有一个后台线程,在一个图像视图定期更新的形象,你可以做这样的事情:

// imports omitted 

public class MyImageUpdatingApp extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     Label label = new Label("Main application"); 
     BorderPane root = new BorderPane(label); 
     Scene scene = new Scene(root, 600, 400); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 

     startImageUpdateThread(); 
    } 

    private void startImageUpdateThread() { 
     ImageView imageView = new ImageView(); 
     BorderPane root = new BorderPane(imageView); 
     Scene scene = new Scene(root, 600, 400); 
     Stage imageViewWindow = new Stage() 
     imageViewWindow.setScene(scene); 
     imageViewWindow.show(); 
     final int pause = 50 ; 
     Thread t = new Thread(() -> { 
      while (moreImagesToGet()) { 
       Image image = getNextImage() ; 
       Platform.runLater(() -> imageView.setImage(image)); 
       try { 
        Thread.sleep(pause); 
       } catch (Exception exc) { 
        exc.printStackTrace(); 
        break(); 
       } 
      } 
     }); 
     t.setDaemon(true); // this thread won't prevent application exit 
     t.setName("Image update thread"); 
     t.start(); 
    } 

    // ... 
} 

显然,可以因数startImageUpdateThread代码为如果你想要的话,可以单独一堂

请注意,此答案仅用于演示线程生成映像和UI之间的关系。在现实生活中,您可能需要一些更复杂的代码才能充分发挥其性能;您不太可能可以快速生成并显示图像,因此您可能需要一个BlockingQueue<Image>作为图像缓冲区,其中一个线程生成图像,一个线程消耗它们。

+0

我现在看得更清楚了。谢谢。我有跟进问题:当你想使用JavaFX,GUI总是成为你的主要应用程序,而不是你的实际程序?其实我需要多个窗口。我设法通过将另一个阶段放入'startImageUpdateThread'并实例化创建图像的程序类来获得它们。这是应该如何完成的? – TomTom

+0

这大多是正确的。 “舞台”是一个窗口。要创建更多的窗口,只需实例化更多的“舞台”。但是你不应该实例化你的'Application'类。 '应用程序'相当于你的“主要类”;系统只是为您创建第一个阶段,并将其传递给'start(...)'方法。我更新了答案以显示创建新窗口。 –