Pourquoi l’en-tête JTable n’apparaît pas dans l’image?

Je proposais de capturer une image de données tabulaires sur Java API ou Tool pour convertir des données tabulaires en fichier image PNG – lorsque l’OP demandait un échantillon de code. Se révèle être plus difficile que je pensais! L’en JTable tête JTable disparaît du JTable PNG écrit par le code.

PNG

PNG

Capture d’écran

entrer la description de l'image ici

 import javax.swing.*; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; class TableImage { public static void main(Ssortingng[] args) throws Exception { Object[][] data = { {"Hari", new Integer(23), new Double(78.23), new Boolean(true)}, {"James", new Integer(23), new Double(47.64), new Boolean(false)}, {"Sally", new Integer(22), new Double(84.81), new Boolean(true)} }; Ssortingng[] columns = {"Name", "Age", "GPA", "Pass"}; JTable table = new JTable(data, columns); JScrollPane scroll = new JScrollPane(table); JPanel p = new JPanel(new BorderLayout()); p.add(scroll,BorderLayout.CENTER); JOptionPane.showMessageDialog(null, p); BufferedImage bi = new BufferedImage( (int)p.getSize().getWidth(), (int)p.getSize().getHeight(), BufferedImage.TYPE_INT_RGB ); Graphics g = bi.createGraphics(); p.paint(g); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); } } 

Remarque: J’ai vérifié la classe Screen Image de camickr et inclus un appel à la doLayout(Component) . La méthode est utile si un Component n’a jamais été réalisé à l’écran, mais n’a aucun effet sur ce code (qui affiche le panneau contenant la table dans un volet d’options avant d’essayer de le rendre).

Qu’est-ce qui est nécessaire pour obtenir l’en-tête de la table à rendre?

Mise à jour 1

Changer la ligne ..

  p.paint(g); 

..to (avec une importation appropriée) ..

  p.paint(g); JTableHeader h = table.getTableHeader(); h.paint(g); 

..produces ..

2e essai

Je vais continuer à le peaufiner.

Mise à jour 2

kleopatra (stratégie 1) et camickr (stratégie 2) ont chacun fourni une réponse, les deux fonctionnant bien, et aucune des deux JTable l’ajout de la JTable à un composant factice (qui est un énorme piratage OMI).

Alors que la stratégie 2 recadrer (ou élargir) à «juste la table», la 1ère stratégie capturera le panneau contenant la table. Cela devient problématique si la table contient de nombreuses entrées, affichant une image d’une table tronquée avec une barre de défilement.

Alors que la stratégie 1 pourrait être modifiée pour contourner ce problème, J’aime beaucoup la simplicité naïve de la stratégie 2, donc ça tient le coup.

Comme l’a souligné kleopatra, aucun «ajustement» n’était nécessaire. Alors je vais essayer encore ..

Mise à jour 3

C’est l’image produite par les méthodes avancées par camickr et kleopatra. Je l’aurais posée deux fois, mais à mon sens, elles sont identiques (bien que je n’aie pas fait de comparaison pixel par pixel).

JTable dans Nimbus PLAF

 import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; class TableImage { Ssortingng[] columns = {"Name", "Age", "GPA", "Pass"}; /** Any resemblance to persons living or dead is purely incidental. */ Object[][] data = { {"André", new Integer(23), new Double(47.64), new Boolean(false)}, {"Jeanie", new Integer(23), new Double(84.81), new Boolean(true)}, {"Roberto", new Integer(22), new Double(78.23), new Boolean(true)} }; TableImage() { } public JTable getTable() { JTable table = new JTable(data, columns); table.setGridColor(new Color(115,52,158)); table.setRowMargin(5); table.setShowGrid(true); return table; } /** Method courtesy of camickr. https://stackoverflow.com/questions/7369814/why-does-the-jtable-header-not-appear-in-the-image/7375655#7375655 Requires ScreenImage class available from.. http://tips4java.wordpress.com/2008/10/13/screen-image/ */ public BufferedImage getImage1(JTable table) { JScrollPane scroll = new JScrollPane(table); scroll.setColumnHeaderView(table.getTableHeader()); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); BufferedImage bi = ScreenImage.createImage(p); return bi; } /** Method courtesy of kleopatra. https://stackoverflow.com/questions/7369814/why-does-the-jtable-header-not-appear-in-the-image/7372045#7372045 */ public BufferedImage getImage2(JTable table) { JScrollPane scroll = new JScrollPane(table); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); // without having been shown, fake a all-ready p.addNotify(); // manually size to pref p.setSize(p.getPreferredSize()); // validate to force recursive doLayout of children p.validate(); BufferedImage bi = new BufferedImage(p.getWidth(), p.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); return bi; } public void writeImage(BufferedImage image, Ssortingng name) throws Exception { ImageIO.write(image,"png",new File(name + ".png")); } public static void main(Ssortingng[] args) throws Exception { UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); TableImage ti = new TableImage(); JTable table; BufferedImage bi; table = ti.getTable(); bi = ti.getImage1(table); ti.writeImage(bi, "1"); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); table = ti.getTable(); bi = ti.getImage2(table); ti.writeImage(bi, "2"); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); } } 

Les deux atteignent l’objective. En utilisant la méthode de camickr, vous exploitez la puissance supplémentaire de l’API ScreenImage. Utiliser la méthode de kleopatra – environ une douzaine de lignes (moins les commentaires et les espaces blancs) du J2SE pur.

Alors que ScreenImage est une classe que je vais utiliser et recommander à l’avenir, l’autre approche utilisant le kernel J2SE est ce que j’utiliserais probablement pour cette circonstance exacte.

Ainsi, alors que le «tic-tac» restra avec camickr, la prime ira à kleopatra.

Ce thread n’avait pas besoin de rendre la table sans l’afficher au préalable, mais c’était le but ultime

ScreenImage s’en charge.

Vous devez append manuellement l’en-tête au volet de défilement.

 import javax.swing.*; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; class TableImage { public static void main(Ssortingng[] args) throws Exception { Object[][] data = { {"Hari", new Integer(23), new Double(78.23), new Boolean(true)}, {"James", new Integer(23), new Double(47.64), new Boolean(false)}, {"Sally", new Integer(22), new Double(84.81), new Boolean(true)} }; Ssortingng[] columns = {"Name", "Age", "GPA", "Pass"}; JTable table = new JTable(data, columns); JScrollPane scroll = new JScrollPane(table); scroll.setColumnHeaderView(table.getTableHeader()); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); BufferedImage bi = ScreenImage.createImage(p); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); } } 

Remarque: La suggestion de Kleopatra d’utiliser addNotify () sur le panneau ne fonctionnera pas avec ScreenImage. La méthode addNotify () rend le composant displayable et le code ScreenImage ne présente que les composants pour les composants non affichables. Je pourrais envisager de rendre cela plus général.

c’était difficile

Était déconcerté que cette en-tête ne figurait pas sur l’image du tout: il n’était pas coupé ou quelque chose, il n’a pas été peint du tout. La raison en est que, au moment de peindre le panneau sur l’image, l’en-tête ne fait plus partie de la hiérarchie. Lors de la fermeture du optionPane, table.removeNotify supprime l’en-tête. Pour l’append à nouveau, appelez addNotify, comme dans cet extrait:

  JScrollPane scroll = new JScrollPane(table); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); JOptionPane.showMessageDialog(null, p); table.addNotify(); p.doLayout(); BufferedImage bi = new BufferedImage(p.getWidth() + 100, p.getHeight() + 100, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); 

[résolu dans edit] Ce que je ne comprends toujours pas pourquoi le panneau apparaît vide sans avoir au préalable été montré dans l’optionPane – typiquement une combinaison de ..

  p.doLayout(); p.setSize(p.getPreferredSize()) 

… ça ira

modifier

Dernière confusion résolue: pour forcer une nouvelle mise en page récursive du volet, tous les conteneurs le long de la ligne doivent être amenés à croire qu’ils ont un homologue – la validation est optimisée pour ne rien faire sinon. Faire le addNotify sur le panneau (au lieu de sur la table) plus valider fait l’affaire

  // JOptionPane.showMessageDialog(null, p); // without having been shown, fake a all-ready p.addNotify(); // manually size to pref p.setSize(p.getPreferredSize()); // validate to force recursive doLayout of children p.validate(); BufferedImage bi = new BufferedImage(p.getWidth() + 100, p.getHeight() + 100, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); 

Non testé pour les effets secondaires.

Modifier 2

Juste grommeler un peu sur le ..

la stratégie 1 pourrait être encore modifiée pour contourner cette [colonne de tableau tronquée]

En fait, aucun ajustement n’est nécessaire (ni le même ajustement que pour ScreenImage, cela dépend de ce que vous considérez comme un ajustement 🙂 – les deux ont besoin de faire en sorte que JScrollPane ne fasse pas son travail en définissant prefViewportSize de la table, exactement la même ligne dans les deux:

  // strategy 1 table.setPreferredScrollableViewportSize(table.getPreferredSize()); p.addNotify(); // strategy 2 scroll.setColumnHeaderView(table.getTableHeader()); table.setPreferredScrollableViewportSize(table.getPreferredSize()); 

L’ajout manuel de votre JPanel à un JFrame de votre propre création, et pas seulement celui que JOptionPane utilise, semble résoudre ce problème.

L’introduction de JFrame vous permet d’ignorer l’affichage de la boîte de dialog initiale, si vous le souhaitez.

 // ...snip JOptionPane.showMessageDialog(null, p); JFrame frame = new JFrame(); frame.setContentPane(p); frame.pack(); BufferedImage bi = new BufferedImage( (int)p.getSize().getWidth(), (int)p.getSize().getHeight(), BufferedImage.TYPE_INT_RGB ); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); frame.dispose(); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); 

Image de la table

 import javax.swing.*; import javax.swing.table.JTableHeader; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; class TableImage { public static void main(Ssortingng[] args) throws Exception { Object[][] data = { {"Hari", new Integer(23), new Double(78.23), new Boolean(true)}, {"James", new Integer(23), new Double(47.64), new Boolean(false)}, {"Sally", new Integer(22), new Double(84.81), new Boolean(true)} }; Ssortingng[] columns = {"Name", "Age", "GPA", "Pass"}; JTable table = new JTable(data, columns); JScrollPane scroll = new JScrollPane(table); JPanel p = new JPanel(new BorderLayout()); p.add(scroll,BorderLayout.CENTER); JOptionPane.showMessageDialog(null, p); JTableHeader h = table.getTableHeader(); int x = h.getWidth(); int y = h.getHeight() + table.getHeight(); BufferedImage bi = new BufferedImage( (int)x, (int)y, BufferedImage.TYPE_INT_RGB ); Graphics g = bi.createGraphics(); h.paint(g); g.translate(0,h.getHeight()); table.paint(g); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); } } 

s’il vous plaît, ce n’est pas la réponse juste un commentaire et un peu … 🙂

@ Andrew Thompson, si j’ai bien compris, par exemple , juste la référence au J/Components est à l’intérieur / passé Graphics2D g2 = (Graphics2D) g; et non accepté TableHeader .... référencé / dérivé TableHeader .... probablement (je paresseux pour vérifier que) quelque chose dans l’API

des émissions de code 🙂

 g2.scale(scale,scale); tableView.paint(g2); g2.scale(1/scale,1/scale); g2.translate(0f,pageIndex*pageHeightForTable); g2.translate(0f, -headerHeightOnPage); g2.setClip(0, 0, (int) Math.ceil(tableWidthOnPage), (int)Math.ceil(headerHeightOnPage)); g2.scale(scale,scale); tableView.getTableHeader().paint(g2); //paint header at top 

entrer la description de l'image ici

 import javax.swing.*; import javax.swing.table.JTableHeader; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; class TableImage { public static void main(Ssortingng[] args) throws Exception { Object[][] data = { {"Hari", new Integer(23), new Double(78.23), (true)}, {"James", new Integer(23), new Double(47.64), (false)}, {"Sally", new Integer(22), new Double(84.81), (true)} }; Ssortingng[] columns = {"Name", "Age", "GPA", "Pass"}; JTable table = new JTable(data, columns); JScrollPane scroll = new JScrollPane(table); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); JOptionPane.showMessageDialog(null, p); JTableHeader h = table.getTableHeader(); int x = h.getWidth(); int y = h.getHeight() + table.getHeight(); BufferedImage bi = new BufferedImage( x, y, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); //g.translate(0, table.getTableHeader().getHeight()); Graphics2D g2 = (Graphics2D) g; table.paint(g2); //table.paint(g); table.getTableHeader().paint(g2); //h.paint(g); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi, "png", new File("ctable.png")); } private TableImage() { } } 

Essayez de désactiver le double tampon sur le composant (ou globalement) avant de l’imprimer ( c.setDoubleBuffered(false) ). Apparemment, cela a un effet 🙂 (voir http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm et http://www.apl.jhu.edu/~hall/java/ Swing-Tutorial / Swing-Tutorial-Printing.html )