2013-07-30 43 views
9

Điều này nghe có vẻ lạ nhưng tôi muốn tạo hình ảnh biểu đồ ở phía máy chủ bằng cách sử dụng JavaFX. Bởi vì JavaFX có API canvas đẹp để thực hiện các phép biến đổi hình ảnh tham gia và định vị.JavaFX để tạo hình ảnh phía máy chủ

Đặc biệt tôi có dịch vụ MVC mùa xuân để tạo biểu đồ dưới dạng hình ảnh. Vấn đề chính là làm thế nào để gọi javaFX API từ một bean Spring thuận tiện. Nếu tôi cố gắng chỉ chạy mã JavaFX từ ứng dụng java (không mở rộng JavaFX Application lớp) tôi nhận được

java.lang.IllegalStateException: Toolkit not initialized 

Bạn có bất cứ lời đề nghị/ý tưởng làm thế nào để giải quyết vấn đề này?

Trả lời

6

vải Vì vậy, sau khi một số nghiên cứu tôi đã thực hiện vẽ với JavaFX và đây là một ví dụ đơn giản:

Đầu tiên tôi đã ứng dụng JavaFX đang được đưa ra trong một thread riêng biệt (tôi sử dụng Spring taskExecutor nhưng một java đơn giản thread có thể được sử dụng).

public class ChartGenerator extends Application { 

    private static Canvas canvas; 

    private static volatile byte[] result; 

    public static void initialize(TaskExecutor taskExecutor) { 
     taskExecutor.execute(new Runnable() { 
      @Override 
      public void run() { 
       launch(ChartGenerator.class); 
      } 
     }); 
    } 

    public static synchronized byte[] generateChart(final Object... params) { 
     Platform.runLater(new Runnable() { 
      @Override 
      public void run() { 
       ByteArrayOutputStream baos = null; 
       try { 
        GraphicsContext gc = canvas.getGraphicsContext2D(); 
        gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight()); 
        /** 
        * Do the work with canvas 
        **/ 
        final SnapshotParameters snapshotParameters = new SnapshotParameters(); 
        snapshotParameters.setFill(Color.TRANSPARENT); 
        WritableImage image = canvas.snapshot(snapshotParameters, null); 
        BufferedImage bImage = SwingFXUtils.fromFXImage(image, null); 
        baos = new ByteArrayOutputStream(); 
        ImageIO.write(bImage, chartType.outputFormat, baos); 
        result = baos.toByteArray(); 
       } catch (InstantiationException e) { 
        throw new ChartGenerationException(e); 
       } catch (IllegalAccessException e) { 
        throw new ChartGenerationException(e); 
       } catch (NoSuchMethodException e) { 
        throw new ChartGenerationException(e); 
       } catch (InvocationTargetException e) { 
        throw new ChartGenerationException(e); 
       } catch (IOException e) { 
        throw new ChartGenerationException(e); 
       } finally { 
        IOUtils.closeQuietly(baos); 
       } 
      } 
     }); 
     while (result == null) { 
      //wait 
     } 
     byte[] ret = result; 
     result = null; 
     return ret; 
    } 


    @Override 
    public void start(Stage stage) { 
     canvas = new Canvas(); 
    } 

    public static class ChartGenerationException extends RuntimeException { 
     public ChartGenerationException(String message) { 
      super(message); 
     } 
     public ChartGenerationException(Throwable cause) { 
      super(cause); 
     } 
    } 

} 

Sau đó tôi gọi là initialize() phương pháp khi ứng dụng Mùa xuân đang bắt đầu:

@Autowired private TaskExecutor taskExecutor; 

@PostConstruct private void initChartGenerator() { 
    ChartGenerator.initialize(taskExecutor); 
} 

Giải pháp này của cource có thể được chuyển đến một ứng dụng không mùa xuân.

Đây là giải pháp đơn luồng (trong trường hợp của tôi là đủ) nhưng tôi nghĩ rằng nó có thể được áp dụng cho việc sử dụng đa luồng (có thể sử dụng RMI để gọi phương thức vẽ).

Ngoài ra giải pháp này làm việc "nguyên trạng" trên máy trạm cửa sổ của tôi, nhưng trên môi trường máy chủ Linux một số hành động bổ sung nên được gọi:

  1. Bạn không thể sử dụng JavaFX trên OpenJDK (tính đến tháng 8 năm 2013) - phải chuyển Oracle JDK
  2. phiên bản Java phải không ít hơn Java 7u6
  3. Các phức tạp nhất - bạn phải sử dụng màn hình ảo để làm cho JavaFX chạy trên môi trường không đầu:

    apt-get install Xvfb

    // đó trở đi bắt đầu máy chủ ứng dụng:

    xuất khẩu DISPLAY = ": 99"

    start-stop-daemon --start --background --user cầu cảng --exec "/ usr/bin/sudo" - -u cầu cảng/usr/bin/Xvfb: 99 -screen 0 1024x768x24


PSBạn cũng có thể sử dụng các khả năng JavaFX khác trên phía máy chủ (ví dụ: xuất html sang hình ảnh) bằng giải pháp này.

+1

Điều này thật tuyệt. Tôi rất vui khi thử nó. Cảm ơn cho những nỗ lực! – GGrec

0

Có lẽ điều gì đó tương tự với giải pháp này sẽ hữu ích?

JavaFX 2.1: Toolkit not initialized

Nếu không, tôi sẽ xem xét việc tạo ra một dịch vụ và đẩy hình ảnh vào một kho dữ liệu và lấy nó trong ứng dụng mùa xuân của bạn.

Hy vọng cung cấp ít nhất một chút trợ giúp!

2

Trong trường hợp những người khác đang tìm kiếm điều này, đây là cách đơn giản hơn nhiều. Sử dụng JavaFX 2.2 tôi đã có thể thực hiện các thao tác sau.

waitForInit = new Semaphore(0); 
    root = new Group(); 
    root.getChildren().add(jfxnode); 
    FxPlatformExecutor.runOnFxApplication(() -> { 
     snapshot = jfxnode.snapshot(new SnapshotParameters(), null); 
     waitForInit.release(); 
    }); 

    waitForInit.acquireUninterruptibly(); 
    BufferedImage bi = SwingFXUtils.fromFXImage(snapshot, null); 

Không cần thêm nút vào nhóm. Từ đó bạn có thể thực hiện bất kỳ thao tác nào bạn muốn với hình ảnh.

Trình FxPlatformExecutor đến từ thư viện JME3-JFX mà tôi đang sử dụng cho dự án của mình. Xem: https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor.java

Bạn có thể dễ dàng tạo phương thức runOnFxApplication() hoặc tạo lớp FxPlatformExecutor.

Đây là mã.

package com.jme3x.jfx; 

import javafx.application.Platform; 

/** 
* TODO This Class should be replaced by some Workmanager implemntation 
* in the future 
* @author Heist 
*/ 
public class FxPlatformExecutor { 

    public static void runOnFxApplication(Runnable task) { 
     if (Platform.isFxApplicationThread()) { 
      task.run(); 
     } else { 
      Platform.runLater(task); 
     } 
    } 
} 

Tôi không viết mã này, liên kết github ở trên.

+0

Đây là một ví dụ không đầy đủ không có tham chiếu đến lớp FxPlatformExecutor - không thể tìm thấy. –

+0

Cảm ơn bạn đã chỉ ra điều đó. Việc nhập khẩu được tạo thành một thư viện của bên thứ ba trong dự án của tôi, xem [github link] (https://github.com/empirephoenix/JME3-JFX/blob/master/src/main/java/com/jme3x/jfx/FxPlatformExecutor. java). Có thể dễ dàng triển khai. Tôi sẽ thêm nó vào câu trả lời. – nucklehead

Các vấn đề liên quan