Rafraichissement de textes dans une UI en Java

Bonjour,

je réalise en ce moment une petite application où je souhaite afficher du texte qui se rafraîchit ligne par ligne (c’est-à-dire une console, en gros).
J’utilise des JEditorPane et la fonction setText(String s). Par défaut, la méthode setText force un refresh de la fenêtre. Le problême, c’est que le rafraîchissement ne s’effectue qu’une fois que toutes les instructions de mon application sont effectuées: j’obtiens donc tout le texte d’un seul coup à la fin et non pas ligne par ligne. Pour en avoir le coeur net, je fais moi-même un repaint/show sur la JFrame après chaque changement de texte.

Si quelqu’un a compris mon problème et qu’il (ou elle) a la solution, je suis preneur :stuck_out_tongue:

on peut avoir un peu de code ? Sinon moi je verrai bien l’utilisation d’une thread mais sans avoir le code, je ne peux pas trop dire…

Je vote aussi pour un problème de thread. Il faut que les instructions de ton application s’exécutent dans un thread différent de celui qui affiche le GUI (qui est toujours le thread principal en Java).

J’y ai pensé aussi pour le thread.

Voilà pour le code

[code]public class MyServer implements DiscoveryListener, ActionListener{

private PeerGroup netPeerGroup;
private DiscoveryService discovery;
private JFrame window = new JFrame(“Server control panel”);
private JEditorPane comments;
private JEditorPane connectedPeers;
private JButton startServer;
private JButton stopServer;
private PipeAdvertisement pipeAdv;

public MyServer(){

      …
 
      //left text panel
      JPanel center = new JPanel();
      center.setLayout(new GridLayout(1,2,10,10));
      comments = new JEditorPane();
      JScrollPane editorCommentsPane = new JScrollPane(comments);
      editorCommentsPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
      editorCommentsPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
      comments.setEditable(false);
      center.add(editorCommentsPane);
   …
}[/code]

Et tous mes appels pour modifier le contenu des JEditorPane se font par cette méthode:

[code]printScreen(0,“ceci est mon nouveau texte à afficher”);

private void printScreen(int i, String s){

 if( i == 0)
 comments.setText(comments.getText() + “\n\n” + s);
 else
 connectedPeers.setText(connectedPeers.getText() + “\n” + s);
}[/code]

Vu que ma classe contient d’autres choses qui n’ont pas de rapport direct avec mon problème, j’ai coupé tout ça à la tronçonneuse pour être plus court.
Si il manque des choses, dites le et j’éditerai.

Merci

Ce qui serait intéressant d’avoir, c’est le ou les méthode(s) qui appelle(nt) la méthode printScreen().

T’aurais pas un vilain (buffered) stream a flusher qqpart dans ton appli?

[quote name=‘GloP’ date=’ 2 May 2005, 20:23’]T’aurais pas un vilain (buffered) stream a flusher qqpart dans ton appli?
[right][post=“355580”]<{POST_SNAPBACK}>[/post][/right][/quote]

Non vu qu’il utilise un setText(String text)

Désolé mais j’étais débordé ====> pas eu le temps de poster du code.
Donc, à la demande générale (ou plutôt celle de ZGoblin), voici les méthodes qui appellent printScreen()

[code] public void actionPerformed(ActionEvent e){
 //if the user clicks on “Start Server”
 if( e.getSource().equals(startServer)){
 startJxta();
 printScreen(0,“Reading in pipe.adv”);
 try {
   FileInputStream is = new FileInputStream(“pipe.adv”);
   pipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(MimeMediaType.XMLUTF8, is);
   is.close();
   serverPipe = new JxtaServerPipe(netPeerGroup,pipeAdv);
   // we want to block until a connection is established
   serverPipe.setPipeTimeout(0);
 } catch (Exception ex) {
   printScreen(0,“failed to bind to the JxtaServerPipe due to the following exception”);
   ex.printStackTrace();
   System.exit(-1);
 }
 newGroup = createGroup();
 if (newGroup != null) {
   joinGroup(newGroup);
 }
 run();
 
 }else{
 //click “Stop server” button
 System.exit(0);
 }
}

public void peerDiscovery() {

netPeerGroup.getDiscoveryService();
 rdv = netPeerGroup.getRendezVousService();
 //Wait until we connect to a rendezvous peer
 printScreen(0,“Waiting to connect to rendezvous…”);
 while (!  rdv.isConnectedToRendezVous()) {
 try {
   Thread.sleep(200);
 }
 catch (InterruptedException ex)
 {
   printScreen(0,“NEDA SA CONECTNUT”);
 }
 }
 printScreen(0,“connected!”);
}

public void run() {

 System.out.println(“Waiting for JxtaBidiPipe connections on JxtaServerPipe”);
 while (true) {
 //repaint();
 try {
   JxtaBiDiPipe bipipe = serverPipe.accept();
   if (bipipe != null ) {
   printScreen(0,“JxtaBidiPipe accepted, sending 100 messages to the other end”);
   //Send a 100 messages
   sendTestMessages(bipipe);
   }
 } catch (Exception e) {
   e.printStackTrace();
 }
 }
}[/code]

Et svp, un peu d’indulgence pour le code car je sais qu’il est crado (c’est un pote qui l’a fait et j’ai pas encore eu le temps de nettoyer).

EDIT: PS: je suis en bouse en Thread donc c’est fort probable que ça vienne de là.

Oui, ça doit bien être un problème de Thread, mais pas celui relevé.
Il ne FAUT pas que le traitement soit fait dans un autre thread, ce n’est pas une obligation. Tout dépend de la durée de ce traitement.

Par contre, il FAUT que toute manipulation d’objet graphique soit fait dans le thread de gestion des événements graphiques. Pour cela, il existe une méthode spéciale : SwingUtilities.invokeLater(Runnable r)

Cette méthode prend un « Runnable » qui fera la mise à jour de l’objet graphique dans sa méthode run(), qui sera appelée par le thread de gestion des événements graphiques, quand il aura le temps (parce qu’il a pas que ça à faire :stuck_out_tongue: )

Moi je modifierait la méthode printScreen come ceci :

[code]private void printScreen(int i, String s){
  // C’est la déclaration du Runnable en « anonyme » qui demande des objets « finaux »
  // faut peut-être les déclarer dans les if pour pouvoir les assigner…
  final String newtext;
  final JTextPane output;

  if( i == 0){
   newtext = comments.getText();
   output = comments;
  }else{
   newtext = connectedPeers.getText();
   output = connectedPeers;
  }

  SwingUtilities.invokeLater(
      new Runnable(){
          public void run(){
               output.setText( newtext + « \n\n » + s);
          }
      }
   );

}[/code]

C’est une des trucs compliqué et chiant a gérer en Java…

Bon, je viens de tester ta solution mccricri et ça ne marche pas.

Premièrement, le texte s’affiche toujours d’un coup à la fin et deuxièmement, le string s doit etre final, donc je n’obtiens que la dernière modification apportée par le setText.

Any other suggestion ?

Ouais, je crois qu’on a oublié un détail dans l’histoire :stuck_out_tongue:

Donc, en fait, Java fonctionne sur 1 thread, qui s’occupe de la gestion de l’interface.
Quand on implémente un « événement », comme dans actionPerformed(), on est toujours dans ce thread, et si le travail est long, ben ça bloque le programme et la gestion de l’affichage jusqu’à ce que le traitement soit terminé.

Donc, pour gérer le travail, il faut le mettre dans un thread :

&nbsp;public void faireMonAction(){ &nbsp; &nbsp; &nbsp;new Thread(new Runnable(){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// ... ici faire le travail &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ).start(); &nbsp;}
Bon, OK, c’est moche, y a un peu plus propre comme moyen, mais c’est l’idée.

Ensuite, comme le travail est maintenant fait dans un thread séparé, il ne peut pas affecter directement les éléments de l’interface. ça c’est le travail du thread principal. C’est pourquoi il faut en passer par ce que j’expliquais plus tôt pour modifier le texte de la JTextArea…

C’est le truc le plus cass-bonbon à gérer en Java, et je crois qu’il n’y a pas ce genre de problème avec .net (les spécialistes confirmeront), mais une fois compris le truc, on s’en sort.