简体   繁体   English

处理已启动的JavaFX应用程序

[英]Handle on launched JavaFX Application

How do I get a handle on a JavaFX application started using the following code? 如何使用以下代码启动JavaFX应用程序的句柄?

CPUUsageChart.launch(CPUUsageChart.class);

CPUUsageChart extends Application from JavaFX and I am launching it from a main method of a simple Java project. CPUUsageChart从JavaFX扩展了Application,我从一个简单的Java项目的主要方法启动它。

What I ultimately want to achieve is, that I can start the App and use its methods in the simple Java code, so that I do not have to do the calling in the Constructor of the Application extending class. 我最终想要实现的是,我可以启动App并在简单的Java代码中使用其方法,这样就不必在Application扩展类的Constructor中进行调用。 I only want to use JavaFX's abilities for drawing charts and save them to HDD, for later usage, but I do not need to see any GUI made in JavaFX. 我只想使用JavaFX的功能来绘制图表并将其保存到HDD中,以备后用,但我不需要查看JavaFX中制作的任何GUI。

Proposed Solution 拟议的解决方案

You can only launch an application once , so there will only ever be a single instance of your application class. 只能启动一个应用程序一次 ,因此将永远只有一个应用程序类实例。

Because there is only a single instance of the application, you can store a reference to the instance in a static variable of the application when the application is started and you can get the instance as required from a static method (a kind of singleton pattern). 因为只有一个应用程序实例,所以您可以在应用程序启动时将对实例的引用存储在应用程序的静态变量中,并且可以根据需要从静态方法(一种单例模式)中获取实例。 。

Caveats 注意事项

Care must be taken to ensure: 必须注意确保:

  1. The instance is available before you try to use it. 在尝试使用实例之前,该实例可用。
  2. That threading rules are appropriately observed. 该线程规则是适当遵守的。
  3. That the JavaFX Platform is appropriately shutdown when it is no longer required. 当不再需要JavaFX Platform时,将其适当关闭。

Sample Solution 样品溶液

The sample code below uses a lock and a condition to ensure that the application instance is available before you try to use it. 下面的示例代码使用锁和条件来确保在尝试使用应用程序实例之前该应用程序实例可用。 It will also require explicit shutdown of the JavaFX platform when it is no longer required. 当不再需要JavaFX平台时,它也需要显式关闭。

Thanks to StackOverflow user James-D for some edit assistance with this code. 感谢StackOverflow用户James-D在此代码方面的一些编辑帮助。

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CPUUsageChart extends Application {
    private static CPUUsageChart appInstance;

    private static final Lock lock = new ReentrantLock();
    private static final Condition appStarted = lock.newCondition();

    /**
     * Starts the application and records the instance.
     * Sets the JavaFX platform not to exit implicitly. 
     * (e.g. an explicit call to Platform.exit() is required
     *       to exit the JavaFX Platform).
     */
    @Override 
    public void start(Stage primaryStage) {
        lock.lock();

        try {
            Platform.setImplicitExit(false);
            appInstance = this;
            appStarted.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /**
     * Get an instance of the application.
     * If the application has not already been launched it will be launched.
     * This method will block the calling thread until the
     * start method of the application has been invoked and the instance set. 
     * @return application instance (will not return null).
     */
    public static CPUUsageChart getInstance() throws InterruptedException {
        lock.lock();

        try {
            if (appInstance == null) {
                Thread launchThread = new Thread(
                        () -> launch(CPUUsageChart.class), 
                        "chart-launcher"
                );
                launchThread.setDaemon(true);
                launchThread.start();
                appStarted.await();
            }
        } finally {
            lock.unlock();
        }

        return appInstance;
    } 

    /**
     * Public method which can be called to perform the main operation 
     * for this application.
     * (render a chart and store the chart image to disk).
     * This method can safely be called from any thread.
     * Once this method is invoked, the data list should not be modified
     * off of the JavaFX application thread.
     */
    public void renderChart(
        ObservableList<XYChart.Data<Number, Number>> data
    ) {
        // ensure chart is rendered on the JavaFX application thread.
        if (!Platform.isFxApplicationThread()) {
            Platform.runLater(() -> this.renderChartImpl(data));
        } else {
            this.renderChartImpl(data);
        } 
    }

    /**
     * Private method which can be called to perform the main operation 
     * for this application.
     * (render a chart and store the chart image to disk).
     * This method must be invoked on the JavaFX application thread.
     */
    private void renderChartImpl(
        ObservableList<XYChart.Data<Number, Number>> data
    ) {
        LineChart<Number, Number> chart = new LineChart<>(
                new NumberAxis(),
                new NumberAxis(0, 100, 10)
        );
        chart.setAnimated(false);
        chart.getData().add(
                new XYChart.Series<>("CPU Usage", data)
        );

        Scene scene = new Scene(chart);

        try {
            LocalDateTime now = LocalDateTime.now();
            File file = Paths.get(
                    System.getProperty("user.dir"),
                    "cpu-usage-chart-" + now + ".png"
            ).toFile();
            ImageIO.write(
                    SwingFXUtils.fromFXImage(
                            chart.snapshot(null, null),
                            null
                    ),
                    "png",
                    file
            );

            System.out.println("Chart saved as: " + file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

To use this (from any thread): 要使用它(从任何线程):

try {
     // get chartApp instance, blocking until it is available.
     CPUUsageChart chartApp = CPUUsageChart.getInstance();
     // call render chart as many times as you want
     chartApp.renderChart(cpuUsageData);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
} finally {
     // note your program should only ever exit the platform once.
     Platform.exit(); 
}

Complete sample application which creates five graphs of cpu usage data with ten samples in each chart, each sample spaced by 100 milliseconds. 完整的示例应用程序,它将创建五张cpu使用情况数据图形,每个图表中包含十个示例,每个示例之间的间隔为100毫秒。 As the sample invokes the chart application to render the charts, it will create chart png image files in the current java working directory and the file names will be output to the system console. 当示例调用图表应用程序渲染图表时,它将在当前java工作目录中创建图表png图像文件,并且文件名将输出到系统控制台。 No JavaFX stage or window is displayed. 没有显示JavaFX阶段或窗口。

Code to sample CPU usage copied from: How to get percentage of CPU usage of OS from java 复制以下示例中的CPU使用率的代码: 如何从Java获取操作系统的CPU使用率百分比

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.XYChart;

import javax.management.*;
import java.lang.management.ManagementFactory;

public class ChartTest {
    public static void main(String[] args) {
        try {
            CPUUsageChart chart = CPUUsageChart.getInstance();
            for (int i = 0; i < 5; i++) {
                ObservableList<XYChart.Data<Number, Number>> cpuUsageData = FXCollections.observableArrayList();
                for (int j = 0; j < 10; j++) {
                    cpuUsageData.add(
                           new XYChart.Data<>(
                                   j / 10.0, 
                                   getSystemCpuLoad()
                           )
                    );
                    Thread.sleep(100);
                }
                chart.renderChart(cpuUsageData);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (MalformedObjectNameException | ReflectionException | InstanceNotFoundException e) {
            e.printStackTrace();
        } finally {
            Platform.exit();
        }
    }

    public static double getSystemCpuLoad() throws MalformedObjectNameException, ReflectionException, InstanceNotFoundException {
        MBeanServer mbs    = ManagementFactory.getPlatformMBeanServer();
        ObjectName name    = ObjectName.getInstance("java.lang:type=OperatingSystem");
        AttributeList list = mbs.getAttributes(name, new String[]{ "SystemCpuLoad" });

        if (list.isEmpty())     return Double.NaN;

        Attribute att = (Attribute)list.get(0);
        Double value  = (Double)att.getValue();

        if (value == -1.0)      return Double.NaN;  // usually takes a couple of seconds before we get real values

        return ((int)(value * 1000) / 10.0);        // returns a percentage value with 1 decimal point precision
    }
}

Sample output (percentage CPU usage on the Y axis, and time in tenth of second sample spacing on the X axis). 样本输出(Y轴上的CPU使用率百分比,X轴上的时间以十分之一秒为单位)。

在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明在此处输入图片说明

Background Information 背景资料

Alternate Implementations 替代实现

  • You could use a JFXPanel rather than a class which extends Application. 您可以使用JFXPanel而不是扩展Application的类。 Though, then your application would also have a dependency on Swing. 但是,您的应用程序也将依赖于Swing。
  • You could make the main class of your application extend Application, so the application is automatically launched when your application is started rather than having a separate Application just for your usage chart. 您可以使应用程序的主类扩展应用程序,以便在启动应用程序时自动启动该应用程序,而不是仅为您的使用图创建一个单独的应用程序。
  • If you have lots and lots of charts to render you could look a this off screen chart renderer implementation . 如果您有很多要渲染的图表,则可以查看此屏幕外的图表渲染器实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 JavaFX应用程序的处理注销按钮操作 - Handle Logout Button Action of JavaFX Application 为什么必须从自己的类中启动JavaFx应用程序? - Why does a JavaFx Application HAVE to be launched from within its own class? 使用石英调度程序时,如何处理JavaFX应用程序的应用程序关闭? - How to handle application close for a JavaFX app when using quartz scheduler? JavaFx 中的 jxBrowser - 线程“JavaFX 应用程序线程”中的异常 java.lang.IllegalStateException:无法获取本机 Z05B8C74CBD96FBF2DE4C1A352702FFF4Z 句柄 - jxBrowser in JavaFx - Exception in thread “JavaFX Application Thread” java.lang.IllegalStateException: Failed to get native window handle 使用JNLP启动后,应用程序无响应 - Unresponsive application after launched with JNLP 启动了GUI beanshell,但是应用程序冻结了 - launched GUI beanshell but application froze 处理JavaFX中的派生属性 - handle derived property in JavaFX 隐藏由Java应用程序启动的窗口化应用程序? - Hiding a windowed application launched by a java application? 未找到 Javafx/应用程序 Gradle JavaFX - Javafx/application not found Gradle JavaFX 如果A由B启动,如何在JavaFX线程B中改变线程A的场景? - How to make thread A change scene in JavaFX thread B if A was launched by B?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM