簡體   English   中英

處理已啟動的JavaFX應用程序

[英]Handle on launched JavaFX Application

如何使用以下代碼啟動JavaFX應用程序的句柄?

CPUUsageChart.launch(CPUUsageChart.class);

CPUUsageChart從JavaFX擴展了Application,我從一個簡單的Java項目的主要方法啟動它。

我最終想要實現的是,我可以啟動App並在簡單的Java代碼中使用其方法,這樣就不必在Application擴展類的Constructor中進行調用。 我只想使用JavaFX的功能來繪制圖表並將其保存到HDD中,以備后用,但我不需要查看JavaFX中制作的任何GUI。

擬議的解決方案

只能啟動一個應用程序一次 ,因此將永遠只有一個應用程序類實例。

因為只有一個應用程序實例,所以您可以在應用程序啟動時將對實例的引用存儲在應用程序的靜態變量中,並且可以根據需要從靜態方法(一種單例模式)中獲取實例。 。

注意事項

必須注意確保:

  1. 在嘗試使用實例之前,該實例可用。
  2. 該線程規則是適當遵守的。
  3. 當不再需要JavaFX Platform時,將其適當關閉。

樣品溶液

下面的示例代碼使用鎖和條件來確保在嘗試使用應用程序實例之前該應用程序實例可用。 當不再需要JavaFX平台時,它也需要顯式關閉。

感謝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();
        }
    }
}

要使用它(從任何線程):

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

完整的示例應用程序,它將創建五張cpu使用情況數據圖形,每個圖表中包含十個示例,每個示例之間的間隔為100毫秒。 當示例調用圖表應用程序渲染圖表時,它將在當前java工作目錄中創建圖表png圖像文件,並且文件名將輸出到系統控制台。 沒有顯示JavaFX階段或窗口。

復制以下示例中的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
    }
}

樣本輸出(Y軸上的CPU使用率百分比,X軸上的時間以十分之一秒為單位)。

在此處輸入圖片說明在此處輸入圖片說明在此處輸入圖片說明在此處輸入圖片說明在此處輸入圖片說明

背景資料

替代實現

  • 您可以使用JFXPanel而不是擴展Application的類。 但是,您的應用程序也將依賴於Swing。
  • 您可以使應用程序的主類擴展應用程序,以便在啟動應用程序時自動啟動該應用程序,而不是僅為您的使用圖創建一個單獨的應用程序。
  • 如果您有很多要渲染的圖表,則可以查看此屏幕外的圖表渲染器實現

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM