2013-06-10 4 views
2

JPanel을 헤드리스 모드로 BufferedImage에 페인트하고 싶습니다 (화면에 GUI가 전혀 표시되지 않음).헤드리스 페인팅

final JPanel panel = createPanel(); 
panel.setSize(panel.getPreferredSize()); 
panel.validate(); 

// JFrame frame = new JFrame(); 
// frame.getContentPane().add(panel); 
// frame.pack(); 
// frame.setVisible(true); 

final BufferedImage image = new BufferedImage(
      panel.getBounds().width, 
      panel.getBounds().height, 
      BufferedImage.TYPE_INT_ARGB 
); 

final Graphics2D gc = image.createGraphics(); 
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 

try { 
    panel.paint(gc); 
    ...save the image somewhere... 
} finally { 
    gc.dispose(); 
} 

하지만 항상 내가 무거운 중량의 구성 요소에 패널을 넣을 때까지 빈 이미지를 얻을이 화면에 표시 (주석 코드 참조). 나는 그것을 보여주고 싶지 않습니다,이 응용 프로그램은 서버에서 실행됩니다. 여기

는 SSCCE입니다 :

public class Example { 

    private static JPanel createPanel() { 
     final JPanel panel = new JPanel(new GridBagLayout());   
     final JLabel label = new JLabel("Yeah, it's working!", SwingConstants.CENTER); 
     label.setFont(new Font("Arial", Font.PLAIN, 12));   
     final GridBagConstraints constraints = new GridBagConstraints(); 
     constraints.fill = GridBagConstraints.BOTH; 
     constraints.weightx = 1; 
     constraints.weightx = 1; 
     panel.add(label, constraints);   
     return panel; 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       final JPanel panel = createPanel(); 
       panel.setSize(panel.getPreferredSize()); 
       panel.validate(); 

    //    JFrame frame = new JFrame(); 
    //    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    //    frame.getContentPane().add(panel); 
    //    frame.pack(); 
    //    frame.setVisible(true); 

       final BufferedImage image = new BufferedImage(
         panel.getBounds().width, 
         panel.getBounds().height, 
         BufferedImage.TYPE_INT_ARGB 
       );  
       final Graphics2D gc = image.createGraphics(); 
       gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
       gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);  
       try { 
        panel.paint(gc); 
        ImageIO.write(image, "png", new File("image.png")); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } finally { 
        gc.dispose(); 
       }     
      } 
     }); 
    }  
} 
+0

Linux에서이 작업을 수행하는 전통적인 방법은 실제로 아무 것도 표시하지 않는 "가짜"X-Window 제공자를 실행하는 것입니다. 그러나 Java를 행복하게 유지합니다. –

+1

더 나은 도움을 받으려면 [SSCCE] (http://sscce.org) –

+0

[[JTable 헤더가 이미지에 나타나지 않는 이유는 무엇입니까?] (http://stackoverflow.com/q/7369814/418556)를 참조하십시오. 실현되지 않은 구성 요소를 페인팅하는 방법에 대한 정보는 이를 토대로 작동시키지 못한다면 @GuillaumePolet의 조언에 따라 SSCCE를 게시하십시오. –

답변

3

구성 요소가 0 크기가 사용되어야한다.

체크 아웃 Screen Image을 확인하십시오. 패널에서 doLayout()을 호출하여 모든 구성 요소의 크기가 올바른지 확인하여이 문제를 처리합니다.

+0

실수로 문제가되지 않지만 하위 구성 요소에 문제가 있습니다. 귀하의 대답은 절대적으로 정확합니다 : +1 –

+0

네, 그게 효과가 있습니다. 내가 왜 재귀 적으로 doLayout()을 호출해야하는지 궁금하다. 이것은 단순히 구성 요소의 유효성을 검사하여 수행해야합니다. IMHO, 다른 스윙의 함정. – Behnil

0

시도 대신에 그것의 생성자 중 하나를 사용하여 BufferedImage를 인스턴스화. GraphicsEnvironment 클래스는 아마 실제 GraphicsEnvironment로 (화면, ...) 도장 방법이 작동하지 않도록 구성 요소가 실현 될 때까지

new BufferedImage(panel.getBounds().width, panel.getBounds().height, BufferedImage.TYPE_INT_ARGB) 
+0

'GraphicsEnvironment.getLocalGraphicsEnvironment()'는 헤드리스 모드에서 예외를 throw합니다. –

+0

@GuillaumePolet 예, @Behnil은'LocalGraphicsEnvironment'가있는 컴퓨터에서 테스트 중이지만 GUI가없는 경우 잘못된 이미지를 생성 할 수 있습니다. ie'JFrame'). – gma

+0

그는 "진짜"GE가 있지만 그의 코드가 작동하지 않는다는 것을 의미합니다. 결국 헤드리스 환경에서는 전혀 작동하지 않습니다.문제는 거기에서 직접 오지 않습니다. (결국에는 동의하지 않지만 동의합니다.) –

2

다음은 이미지 파일에 간단한 레이블을 칠한 다음 이미지 파일이 열리는 스 니펫입니다 (데스크톱 컴퓨터의 경우). (당신의 SSCCE와)

import java.awt.Desktop; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 

import javax.imageio.ImageIO; 
import javax.swing.JLabel; 

public class Test { 

    public static void main(String[] args) throws IOException { 
     JLabel label = new JLabel("Hello world"); 
     label.setSize(label.getPreferredSize()); 
     BufferedImage image = new BufferedImage(label.getWidth(), label.getHeight(), BufferedImage.TYPE_INT_ARGB); 
     label.paint(image.getGraphics()); 
     File output = new File("C:\\test\\hello world.png"); 
     if (!output.getParentFile().exists()) { 
      output.getParentFile().mkdirs(); 
     } 
     ImageIO.write(image, "png", output); 
     Desktop.getDesktop().open(output); 
    } 

} 

편집 :

당신의 패널 validate() 아니라 doLayout() (당신이 패널을 중첩 한 경우, 재귀 적으로 호출해야합니다)를 호출하지 마십시오

import java.awt.Desktop; 
import java.awt.Font; 
import java.awt.Graphics2D; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.RenderingHints; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 

import javax.imageio.ImageIO; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.SwingConstants; 
import javax.swing.SwingUtilities; 

public class Example { 

    private static JLabel label; 

    private static JPanel createPanel() { 
     final JPanel panel = new JPanel(new GridBagLayout()); 

     label = new JLabel("Yeah, it's working!", SwingConstants.CENTER); 
     label.setFont(new Font("Arial", Font.PLAIN, 12)); 

     final GridBagConstraints constraints = new GridBagConstraints(); 
     constraints.fill = GridBagConstraints.BOTH; 
     constraints.weightx = 1; 
     constraints.weightx = 1; 
     panel.add(label, constraints); 

     return panel; 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       final JPanel panel = createPanel(); 
       panel.setSize(panel.getPreferredSize()); 
       panel.doLayout(); 
       System.err.println(label.getSize() + ""); 
       // JFrame frame = new JFrame(); 
       // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       // frame.getContentPane().add(panel); 
       // frame.pack(); 
       // frame.setVisible(true); 

       final BufferedImage image = new BufferedImage(panel.getBounds().width, panel.getBounds().height, 
         BufferedImage.TYPE_INT_ARGB); 

       final Graphics2D gc = image.createGraphics(); 
       gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
       gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 

       try { 
        panel.paint(gc); 
        File output = new File("image.png"); 
        ImageIO.write(image, "png", output); 
        Desktop.getDesktop().open(output); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } finally { 
        gc.dispose(); 
       } 

      } 
     }); 
    } 
} 
+0

doLayout()은 중첩 패널이없는 경우에만 작동합니다. 'ScreenImage'는 모든 패널의 모든 구성 요소의 크기를 확인하기 위해 doLayout()을 재귀 적으로 수행합니다. – camickr

+0

@camickr 예, 실제로 재귀 적으로 수행해야합니다. 귀하의 링크를 따라'ScreenImage' 코드를 읽었습니다. –

0

인쇄/페인트하려고하는 루트 구성 요소에서 addNotify을 호출하면 문제가 해결됩니다. 이 문제의 핵심은 컨테이너에 '피어 (peer)'가 없다면 유효성 검사가 단락 회로를 호출하는 것 같습니다. addNotify을 호출하면 Component.validate에 대한 후속 호출이 헤드리스가 아닌 시나리오에서 정상적으로 작동하는 것을 허용하는 피어가 초기화됩니다.

doLayout이 하위 구성 요소를 배치하지 않아서 구성 요소가 더 깊게 중첩 된 경우에는 doLayout을 호출하기 때문에 대체 솔루션으로 제출하십시오. (Camickr의 답변에서 언급 한 ScreenImage 클래스가 doLayout을 재귀 적으로 호출하여이 문제를 해결합니다.)