2009-08-02 4 views
2

Dans mon programme, j'ai souvent besoin d'imprimer divers composants JComponents (généralement des JPanels) et je les aime pour être en pleine page. La façon dont je le fais est maintenant en utilisant le code suivant:Redimensionner JPanel pour préparer l'impression sans la retirer de sa position d'origine

g2d.scale(pf.getImageableWidth()/componentToPrint.getWidth(), pf.getImageableHeight()/componentToPrint.getHeight()); 

mais cela étend souvent ou déformant autrement tout ce que je suis en train d'imprimer, et je préférerais faire quelque chose qui redimensionnée de façon intelligente, peut-être version fonctionnelle de:

componentToPrint.setSize(pf.ImageableWidth(), pf.ImageableHeight); 

ou dire l'ajout du composant dans une nouvelle JFrame, puis définir la taille du cadre (problème des composants ne peuvent pas exister dans deux places à la fois). Je ne m'inquiéterais pas si le redimensionnement rendrait le reste de l'interface graphique sembler terrible, tant que c'est quelque chose qui peut facilement être réinitialisé.

Y at-il un moyen de le faire?

Répondre

0

Je pense que vous avez répondu à votre propre question. Chaque composant a une méthode:

setSize(int width, int height); 
+1

le composant est sous le contrôle d'un gestionnaire de disposition, et n'a donc aucun contrôle sur sa propre taille. Appeler cela ne fera rien. –

1

Je pense que le problème auquel vous faites face n'a rien à voir avec le fonctionnement de l'échelle. C'est votre logique qui est incorrecte. Dans la plupart des cas, vos échelles x et y seront différentes, car l'échelle de Imageable et l'échelle de votre composant ne seront pas les mêmes. C'est la raison pour laquelle votre composant est déformé après l'opération d'échelle. Vous devez faire quelque chose comme:

double factorX = pf.getImageableWidth()/component.getWidth(); 
double factorY = pf.getImageableHeight()/component.getHeight(); 
double factor = Math.min(factorX, factorY); 
g2.scale(factor,factor); 

Après cela, vous pouvez traduire votre image aux coordonnées appropriées en fonction de son nouveau format. Hope it helps ...

+0

Pas tout à fait: le problème n'est pas seulement la ration d'aspect. La plupart de mon GUI repose fortement sur GridBagLayout qui redimensionne intelligemment les composants pour les adapter au mieux dans l'espace. Par exemple, la maximisation créera de plus grands espaces entre les objets, et le rétrécissement peut faire couper le texte "sample te ...". En outre, toute mise à l'échelle basée sur l'image entraînera une qualité inférieure, contrairement à la redimensionnement intelligent des composants. –

+0

Je comprends ce que vous avez dit à propos de GridBagLayout, mais je ne suis pas d'accord avec ce que vous pensez de la mise à l'échelle. Dans ce cas, il ne s'agit PAS d'un SCALING D'IMAGE, mais d'une AffineTransform appliquée aux opérations graphiques avant leur exécution. Il n'y a donc aucune perte de qualité. –

+0

C'est un bon point, je n'ai pas vérifié comment cela a évolué. Néanmoins, même si la qualité de l'image est constante, toute mise à l'échelle de ce type rend les impressions incohérentes (en fonction du rapport d'aspect sur l'écran) et n'est pas nécessairement affichée de façon optimale dans l'espace d'impression. –

1

Je factoriser le code serait de dessin de la méthode paintComponent() de votre mise en œuvre de JPanel à une méthode protégée public/paquet dans ce panneau qui peut attirer un objet Graphics arbitraire, de toute largeur/hauteur (sans doute que le code de dessin est assez général).

Cet exemple a une trame, contenant un panneau, qui a une certaine logique de dessin (un grand X, la taille du panneau). La méthode principale de cet exemple vous montre un moyen d'obtenir une image et de l'écrire dans un fichier, même si la taille de l'image est différente de la taille du panneau.


import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.image.BufferedImage; 
import java.io.File; 

import javax.imageio.ImageIO; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class MockFrame extends JFrame { 

    // throws Exception, as just an example (not really advised to do this) 
    public static void main(String[] args) throws Exception { 
     MockFrame frame = new MockFrame(); 
     frame.setVisible(true); 

     // different sizes from the frame 
     int WIDTH = 500; 
     int HEIGHT = 500; 

     BufferedImage b = new BufferedImage(WIDTH, HEIGHT, 
      BufferedImage.TYPE_INT_ARGB); 

     Graphics2D g2d = (Graphics2D) b.getGraphics(); 

     // should set some background, as the panel's background 
     // is dealt with by super.paintComponent() 
     g2d.setBackground(Color.white); 

     frame.getPanel().drawingLogic(b.getGraphics(), WIDTH, HEIGHT); 

     ImageIO.write(b, "png", new File("test.png")); 

    } 

    private MockPanel panel; 

    public MockFrame() { 
     this.setSize(200, 200); 
     this.setDefaultCloseOperation(EXIT_ON_CLOSE); 

     panel = new MockPanel(); 
     getContentPane().add(panel); 
    } 

    public MockPanel getPanel() { 
     return panel; 
    } 

    private class MockPanel extends JPanel { 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     drawingLogic(g, getWidth(), getHeight()); 
    } 

    public void drawingLogic(Graphics g, int width, int height) { 
     g.setColor(Color.black); 

     g.drawLine(0, 0, width, height); 
     g.drawLine(0, height, width, 0); 
    } 
    } 
} 

Cela permet aux objets externes à l'interface graphique de se connecter à son algorithme de dessin. Un inconvénient que je vois est que vous allez créer une dépendance entre n'importe quel objet voulant imprimer le panneau à l'implémentation réelle du panneau. Mais il est encore mieux que le redimensionnement du panneau à la volée (je l'ai essayé et semble avoir quelques problèmes - je pense qu'il faut un certain temps pour le setSize() changement de propager

EDIT:.

En réponse aux commentaires, j'ai fourni une version modifiée de l'extrait de code ci-dessus.Il n'est probablement pas la meilleure façon de le faire, et pas très convivial (donc je ne l'utiliserait pas dans une application d'utilisateur final), mais il redimensionne tout dans le cadre selon les règles du gestionnaire de disposition.


/* This code snippet describes a way to resize a frame for printing at 
* a custom size and then resize it back. 
* 
* Copyright (C) 
* 
* This program is free software: you can redistribute it and/or modify 
* it under the terms of the GNU General Public License as published by 
* the Free Software Foundation, either version 3 of the License, or 
* (at your option) any later version. 
* 
* This program is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
* GNU General Public License for more details. 
* 
* You should have received a copy of the GNU General Public License 
* along with this program. If not, see . 
*/ 

import java.awt.Container; 
import java.awt.Graphics2D; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.image.BufferedImage; 
import java.io.File; 

import javax.imageio.ImageIO; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JOptionPane; 

public class MockFrame extends JFrame { 

    // throws Exception, as just an example (not really advised to do this) 
    public static void main(String[] args) throws Exception { 
     final MockFrame frame = new MockFrame(); 
     frame.setVisible(true); 

     // different sizes from the frame 
     final int WIDTH = 500; 
     final int HEIGHT = 700; 

     final BufferedImage b = new BufferedImage(WIDTH, HEIGHT, 
       BufferedImage.TYPE_INT_ARGB); 

     final Graphics2D g2d = (Graphics2D) b.getGraphics(); 

     final int previousWidth = frame.getWidth(); 
     final int previousHeight = frame.getHeight(); 

     frame.setSize(WIDTH, HEIGHT); 
     frame.repaint(); 

     JOptionPane.showMessageDialog(null, 
       "Press OK when the window has finished resizing"); 

     frame.print(g2d); 
     frame.setSize(previousWidth, previousHeight); 
     ImageIO.write(b, "png", new File("test.png")); 

    } 

    public MockFrame() { 
     this.setSize(200, 200); 
     this.setDefaultCloseOperation(EXIT_ON_CLOSE); 

     boolean shouldFill = true; 
     boolean shouldWeightX = true; 

     Container pane = getContentPane(); 

     // code from 
     // http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html 
     // just to add some components in the frame... :) 
     // left out in here for brevity 

    } 

} 

Le code redimensionne essentiellement le cadre, montre à l'utilisateur un message de confirmation de sorte que le fil peut être bloqué jusqu'à ce que le repeindre est fait (qui pourrait être fait par Thread.sleep(), mais il est plus transparent à l'aide d'un message). Ensuite, il imprime le cadre et le redimensionne à sa forme d'origine. Un peu hacky, mais ça marche.

-- Flaviu Cipcigan

+0

C'est ce que je cherche, mais je ne suis pas sûr de savoir comment je pourrais faire ce travail. Toute la logique de la peinture provient de la méthode de peinture héritée des composants car il s'agit de tous les composants Swing; rien n'est dessiné par mon code. Tout ce code repose sur la taille réelle des composants à peindre, donc sans changer la taille réelle des composants, je ne peux pas changer la façon dont ils peignent. –

+0

Voir mon post édité ci-dessus pour une réponse révisée à votre commentaire. À votre santé! –

+0

C'est ce que j'espérais faire, mais un composant sélectionné au hasard est susceptible d'être contrôlé par un gestionnaire de mise en page, donc la méthode setSize() ne fait rien. –

2

Je pense que la solution que vous cherchez serait de construire un nouveau JPanel qui contient le contenu désiré et d'imprimer la copie à la place. Si vous utilisez le CellRendererPane, vous pouvez obtenir le comportement de redimensionnement exact que vous recherchez.

Si vos composants JComponent sont raisonnablement bien écrits, il ne devrait pas être un problème pour en créer un nouveau et pour que son modèle soit le même que celui de l'original.

CellRendererPane cellRendererPane = new CellRendererPane(); 
// It's important to add the cell renderer pane to something 
// you can use the same one for all of your exporting if you like and just 
// add it to your main frame's content pane - it won't show up anywhere. 
add(cellRendererPane); 

JPanel printPanel = createCopy(panel); 
cellRendererPane.paintComponent(g, printPanel, null, 0, 0, exportDim.width, exportDim.height, true); 

Voici un exemple de travail complet. La méthode createPanel() doit créer le composant que vous voulez rendre. Un vrai exemple devrait être sûr d'utiliser le même modèle plutôt que de recréer un nouveau modèle pour un composant jetable.

public class SCCE { 

    public static void main(String[] args) throws Exception { 
     UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
     final JFrame f = new JFrame("SCCE"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 


     f.getContentPane().setLayout(new BorderLayout()); 
     f.getContentPane().add(SCCE.createPanel()); 

     final CellRendererPane backgroundRenderer = new CellRendererPane(); 

     // Add the renderer somewhere where it won't be seen 
     f.getContentPane().add(backgroundRenderer, BorderLayout.NORTH); 
     f.getContentPane().add(createSaveButton(backgroundRenderer), BorderLayout.SOUTH); 

     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

    // Create your custom component from whatever model here.. 
    private static final Component createPanel() { 
     DefaultListModel model = new DefaultListModel(); 
     for (int i = 0; i < 10; i++) { 
      model.addElement("Item number " + i); 
     } 
     return new JList(model); 
    } 

    private static JButton createSaveButton(final CellRendererPane backgroundRenderer) { 
     return new JButton(new AbstractAction("Save image to file") { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       Dimension d = new Dimension(400, 300); 

       BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB); 
       Graphics2D g = img.createGraphics(); 
       backgroundRenderer.paintComponent(g, createPanel(), null, 0, 0, d.width, d.height, true); 
       g.dispose(); 

       try { 
        File output = new File("test.png"); 
        System.err.println("Saved to " + output.getAbsolutePath()); 
        ImageIO.write(img, "png", output); 
       } catch (IOException ex) { 
        ex.printStackTrace(); 
       } 
      } 
     }); 
    } 

} 
Questions connexes