Affichage des articles dont le libellé est swing. Afficher tous les articles
Affichage des articles dont le libellé est swing. Afficher tous les articles


Analyser une interface Swing

Intervenir sur une application Swing peut vite s'avérer très complexe si celle-ci est basée sur une importante imbrication de composants et layouts personnalisés.

Afin de se faciliter la tâche, il existe Swing Explorer. Disponible sous forme de plugin (Eclipse, Netbeans...) ou autonome, il permet d'explorer l'arbre des composants graphiques au moment de l'exécution de l'application :

Comme vous pouvez le voir, la partie gauche contient les arbres de tous les objets Swing instanciés. Il est alors possible de double-cliquer dessus pour en afficher un aperçu dans la partie droite.

Il est alors possible de sélectionner à la souris chaque composant pour entrer dans leur détail.

C'est donc l'outil idéal pour retrouver n'importe quel composant graphique instancié dans une application, sans avoir à utiliser de point d'arrêt ou modification pas-à-pas.

Il dispose même d'un Player qui permet de rejouer la création et la mise en place des objets telles qu'exécutées par Swing :

Sur ce, bon debug! :)


Fichier(s) joint(s) :



Créer une console personnalisée avec JTextPane

Voici la problématique à résoudre : un outil a été développé en prévision de n'être exécuté qu'en ligne de commande. Toutes les informations ressorties le sont donc via System.out ou System.err. Or il se trouve maintenant qu'il est nécessaire d'intégrer cet outil dans une application comportant une interface graphique et donc de diriger les sorties vers un composant graphique en y ajoutant un formatage selon le type de message. Il est bien sûr impossible de modifier le code de l'outil... Voici un aperçu du résultat attendu :

Pour résoudre ceci, il faut commencer par redéfinir la sortie des messages. Java offre cette possibilité à l'aide de :

System.setOut(...);
System.setErr(...);

Ces deux méthodes attendent en entrée un objet héritant de java.io.PrintStream. Mais avant de voir comment mettre en place ces éléments, regardons comment est créée l'interface :

public static void buildGUI() throws Exception {
 final CustomConsole out = new CustomConsole();
 JScrollPane sp = new JScrollPane(out.getOutComponent());
 JButton btn = new JButton("Run!");
 btn.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent e) {
   out.clear();
   Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
     doJob();
    }
   });
   t.start();
  }
 });
 JPanel p = new JPanel(new BorderLayout());
 p.add(btn, BorderLayout.NORTH);
 p.add(sp, BorderLayout.CENTER);
 
 JFrame f = new JFrame("Test console");
 f.setSize(800, 850);
 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 f.add(p);
 f.setVisible(true);
}

Cette première méthode utilise des objets Swing courants pour construire l'interface, et introduit CustomConsole : c'est dans cette classe que réside toute notre customisation. Voici son contenu :

public class CustomConsole {

 private JTextPane outComponent;

 public CustomConsole() throws Exception {
  this.outComponent = new JTextPane() {
   private static final long serialVersionUID = 8575888440178628669L;
   // to remove line wrap
   public boolean getScrollableTracksViewportWidth() {
    return getUI().getPreferredSize(this).width <= getParent().getSize().width;
   }
   @Override
   public void setEditable(boolean b) {
    super.setEditable(false);
   }
  };
  // build error style
  Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
  Style s = outComponent.getStyledDocument().addStyle("error", def);
  StyleConstants.setForeground(s, Color.RED);
  StyleConstants.setItalic(s, true);
  // define standard out
  System.setOut(new CustomOutStream(outComponent));
  System.setErr(new CustomErrStream(outComponent));
 }

 public JTextPane getOutComponent() {
  return outComponent;
 }

 public void clear() {
  outComponent.setText("");
 }
}

Comme vous pouvez le constater, le code est assez simple. La définition du style de police pour les messages d'erreurs se fait à partir du style standard de police du JTextPane auquel on ajoute nos caractéristiques. Notez la subtilité lors de la définition de cet objet : pour empêcher les retours à la ligne lors de l'affichage, on surcharge la méthode getScrollableTracksViewportWidth(). Enfin, la suite de notre personnalisation réside dans les objets CustomOutStream et CustomErrStream : mais là encore, rien de très compliqué, les deux classes sont quasiment identiques :

public class CustomOutStream extends CustomPrintStream {

 private JTextPane outComponent;

 public CustomOutStream(JTextPane area) throws Exception {
  super();
  outComponent = area;
 }

 @Override
 public void write(byte buf[], int off, int len) {
  addToConsole(new String(buf, off, len));
 }

 @Override
 public void write(int b) {
  addToConsole(new String(new char[] { (char) b }));
 }

 @Override
 public void println(String x) {
  addToConsole(x);
 }

 private void addToConsole(String s) {
  StyledDocument doc = outComponent.getStyledDocument();
  try {
   if (!s.endsWith("\n")) {
    println();
   }
   doc.insertString(doc.getLength(), s, doc.getStyle(StyleContext.DEFAULT_STYLE));
  } catch (BadLocationException e) {
   e.printStackTrace();
  }
  outComponent.setCaretPosition(doc.getLength() - s.length());
 }

}

Elles redéfinissent les méthodes d'écriture les plus courantes afin de nous permettre de gérer les styles : dans cet exemple, pour la sortie standard, on choisi d'utiliser la police par défaut, correspondant au style StyleContext.DEFAULT_STYLE.

Pour ce qui est de la sortie d'erreur, on utilise le même code en remplaçant simplement le style par celui défini dans la console (italique rouge) : doc.getStyle("error")

L'instruction setCaretPosition permet de créer l'effet d'autoscroll sur la console.

Pour unifier la création du flux de sortie, j'ai créé la classe ci-dessous, mère des classes précédentes :

public class CustomPrintStream extends PrintStream {

 public CustomPrintStream() throws FileNotFoundException, UnsupportedEncodingException {
  // on indique un fichier texte parce que requis mais non utilisé ici
  // et on indique l'encodage désiré pour cette console
  super(new FileOutputStream("C:\\empty.txt"), true, "ISO-8859-1");
 }

}

Cette console peut alors être testée avec ce simple code :

private static void doJob() {
 FileInputStream fstream = null;
 try {
  System.out.println("Sortie console standard");
  if(true) {
   throw new Exception("premiere erreur!!");
  }
 } catch (Exception e) {
  e.printStackTrace();
  System.out.println("info après premiere erreur");
 } finally {
  try {
   fstream.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

Nous avons donc pu créer cette interface graphique sans modifier le code de l'outil de base.

Hope this helps!

Sources :


Fichier(s) joint(s) :



ScalableExplorer : libre et ouvert... à la critique!

Voilà quelques jours que j'ai commencé à m'exercer un peu plus avec Scala et je dois dire que j'en suis très satisfait! Afin de tester un maximum de fonctionnalités du langage, j'ai décidé de créer un projet d'explorateur de fichiers, qui est donc en cours de développement et auquel j'ajouterai des fonctionnalités régulièrement.

Pour l'occasion, j'ai mis en place un espace SourceForge afin de permettre à tout le monde d'accéder aux sources : l'intérêt est de permettre à qui le veut de jeter un oeil au code que j'ai écrit afin de découvrir les fonctionnalités de Scala à l'oeuvre, de me corriger ou de donner son avis, voire de contribuer à l'avancée du projet! ;)

L'application se nomme Scalable Explorer et est dors et déjà disponible. N'hésitez pas à aller consulter les fichiers, j'ai volontairement essayé d'exploiter au mieux la syntaxe et les outils de Scala. Brièvement, les fonctionnalités disponibles :

  • Arborescence des fichiers avec "lazy-loading"
  • Fenêtre d'aperçu pour les fichiers graphiques et textuels
  • Barre de menu avec raccourcis clavier
  • Menu contextuel (clic-droit) sur l'arbre (JTree)

Comme décrit sur SourceForge, ce projet est bien entendu en phase naissante, "pre-alpha" et est donc voué à largement évoluer dans les semaines à venir. Une fois de plus, si vous êtes intéressé(es), je vous invite à participer, que ce soit en commentant ce qui est mis en place ou en ajoutant des fonctionnalités! Pour information, le projet est développé sous Netbeans et peut être récupéré via SVN ou CVS.

[Edit]Nouvelles fonctionnalités au 18/11/10 :

  • Autoscroll sur le Jtree
  • Drag and drop pour le déplacement de fichiers

Fichier(s) joint(s) :



Eclipse (Swing) - Ajouter des composants dans un JTable

Afin d’améliorer l’ergonomie de certaines interfaces, il peut être utile d’ajouter des éléments interactifs (zones de texte, boutons radio…) directement dans les cellules d’un tableau.

Pour ce faire, l’API Swing permet de mettre rapidement en place des TableCellRenderer qui définissent le contenu des cellules. De base, une cellule est en fait un JLabel : on peut donc améliorer l’affichage en le remplaçant par une zone de texte srollable. C’est à partir de ce moment que les ennuis commencent…

Regardons tout d’abord le code nécessaire à la mise en place de cette zone de texte :

Définition du renderer personnalisé dans la dernière colonne du tableau :

Table table = new JTable() {
                    private static final long serialVersionUID = 3592907806336215067L;
                    public boolean isCellEditable(int rowIndex, int vColIndex) {
                        return (vColIndex==this.getColumnCount()-1);
                    }
                };
TableColumn col = table.getColumnModel().getColumn(resultColumnIndex);
TextAreaCellRenderer renderer = new TextAreaCellRenderer();
col.setCellRenderer(renderer);

Code du renderer :

import java.awt.Color;
import java.awt.Component;
 
import javax.swing.BorderFactory;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.DefaultTableCellRenderer;
 
public class TextAreaCellRenderer extends DefaultTableCellRenderer {
 
    private static final long serialVersionUID = -3987688353662913146L;
 
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {
 
        // Customize it
        JTextArea textArea = new JTextArea(value.toString());
        textArea.setEditable(false);
        textArea.setWrapStyleWord(true);
        textArea.setLineWrap(true);
        JScrollPane pane = new JScrollPane(textArea);
        pane.setBorder(BorderFactory.createLineBorder(Color.BLACK, 0));
        pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        pane
            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        return pane;
    }
}

Ces quelques lignes suffisent à faire apparaître une zone de texte dans les cellules en question. Cependant, impossible d’utiliser les barres de défilement!! En effet, comme expliqué dans ce forum, Swing n’ajoute pas réellement la zone de texte dans la cellule, mais plutôt une image de celle-ci.

Pour rendre les barres de défilement actives, voici le principe à mettre en place :

Au clic sur la cellule, il faut entrer en mode édition. Ainsi, une nouvelle zone de texte sera définie comme CellEditor et sera utilisable!

Voici donc le complément de code à mettre en place :

Définition de l’éditeur personnalisé :

Table table = new JTable() {
                    private static final long serialVersionUID = 3592907806336215067L;
                    public boolean isCellEditable(int rowIndex, int vColIndex) {
                        return (vColIndex==this.getColumnCount()-1);
                    }
                };
TableColumn col = table.getColumnModel().getColumn(resultColumnIndex);
TextAreaCellRenderer renderer = new TextAreaCellRenderer();
TextAreaCellEditor editor = new TextAreaCellEditor();
col.setCellEditor(editor);
col.setCellRenderer(renderer);

Code de l’éditor :

import java.awt.Color;
import java.awt.Component;
 
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.TableCellEditor;
 
public class TextAreaCellEditor extends AbstractCellEditor implements
    TableCellEditor {
 
    private static final long serialVersionUID = -9030406143699523582L;
 
    private String editingValue;
 
    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
        boolean isSelected, int row, int column) {
 
        editingValue = value.toString();
 
        // Customize it
        JTextArea textArea = new JTextArea(valueStr);
        textArea.setEditable(false);
        textArea.setWrapStyleWord(true);
        textArea.setLineWrap(true);
        JScrollPane pane = new JScrollPane(textArea);
        pane.setBorder(BorderFactory.createLineBorder(Color.BLACK, 0));
        pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        pane
            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        return pane;
    }
 
    @Override
    public Object getCellEditorValue() {
        return editingValue;
    }
 
    @Override
    public void cancelCellEditing() {
        super.cancelCellEditing();
    }
}

Maintenant, le contenu de la cellule est défilant.


Fichier(s) joint(s) :