Forum |  HardWare.fr | News | Articles | PC | Prix | S'identifier | S'inscrire | Aide | Shop Recherche
1049 connectés 

  FORUM HardWare.fr
  Programmation
  Java

  [RESOLU] JProgressBar qui affiche directement 100% :(

 



 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

[RESOLU] JProgressBar qui affiche directement 100% :(

n°1925303
Yop69
Posté le 20-09-2009 à 03:02:13  profilanswer
 

Bonjour,
 
Voila j'ai une JProgressBar (dans un JPanel d'un JFrame) pour lire un fichier d'un serveur distant. Mais ma JProgressBar m'affiche 100% dessuite :(
Pour la lecture du fichier j'utilise un getInputStream.
J'ai l'impression qu'il faut utiliser 2 threads l'un pour afficher la JProgressBar et l'autre pour rappatrier le fichier mais je ne saisi pas la notion sous jacente :(
 

Code :
  1. public void setValeur(int compteur){
  2.         
  3.         System.out.println(compteur+" | "+this.jpbmax);
  4.         
  5.         this.jpb.setValue(compteur);
  6.         this.jpb.setString(compteur * 100 / this.jpbmax +"%" );
  7.         this.jpbCdr.paintComponents(this.jpbCdr.getGraphics());
  8.         
  9.         try { Thread.sleep(1000);}
  10.         catch(Exception e) {}
  11.     }


 

Code :
  1. public void downloadFile() {
  2.         URLConnection connection = null;
  3.         InputStream is = null;
  4.         FileOutputStream destinationFile = null;
  5.         String pathfile = this.tabXml[ this.indexMaj ][2] + "/" + this.tabXml[ this.indexMaj ][3];
  6.         
  7.         try {
  8.             //On crée l'URL
  9.             URL url = new URL( pathfile );
  10.  
  11.             //On crée une connection vers cet URL
  12.             connection = url.openConnection( );
  13.  
  14.             //On récupère la taille du fichier
  15.             this.jpbmax = connection.getContentLength();
  16.  
  17.             //Si le fichier est inexistant, on lance une exception
  18.             if(this.jpbmax == -1){
  19.                 throw new IOException("Fichier vide" );
  20.             }
  21.  
  22.             //On récupère le stream du fichier
  23.             is = new BufferedInputStream(connection.getInputStream());
  24.  
  25.             //On prépare le tableau de bits pour les données du fichier
  26.             byte[] data = new byte[ this.jpbmax ];
  27.  
  28.             //On déclare les variables pour se retrouver dans la lecture du fichier
  29.             int currentBit = 0;
  30.             int deplacement = 0;
  31.             
  32.             //Tant que l'on n'est pas à la fin du fichier, on récupère des données
  33.             while(deplacement < this.jpbmax){
  34.                 currentBit = is.read(data, deplacement, data.length-deplacement);    
  35.                 if(currentBit == -1) break;
  36.                 deplacement += currentBit;
  37.                 setValeur(deplacement);
  38.             }
  39.  
  40.             //Si on n'est pas arrivé à la fin du fichier, on lance une exception
  41.             if(deplacement != this.jpbmax) throw new IOException("Le fichier n'a pas été lu en entier (seulement " + deplacement + " sur " + this.jpbmax + " )" );
  42.                     
  43.             //On crée un stream sortant vers la destination
  44.             destinationFile = new FileOutputStream( "tmp.jar" );
  45.  
  46.             //On écrit les données du fichier dans ce stream
  47.             destinationFile.write(data);
  48.  
  49.             //On vide le tampon et on ferme le stream
  50.             destinationFile.flush();
  51.  
  52.         } catch (MalformedURLException e) { System.err.println("Problème avec l'URL : " + pathfile); }
  53.         catch (IOException e) { e.printStackTrace(); }
  54.         finally {
  55.             try {
  56.                 is.close();
  57.                 destinationFile.close();
  58.                 
  59.                 
  60.                 
  61.             } catch (IOException e) { e.printStackTrace(); }
  62.         }
  63.     }


 
 
Please  :sweat:


Message édité par Yop69 le 23-09-2009 à 22:17:14
mood
Publicité
Posté le 20-09-2009 à 03:02:13  profilanswer
 

n°1925436
cbeyls
Hail to the King, Baby
Posté le 21-09-2009 à 03:16:16  profilanswer
 

C'est ici que tu devrais utiliser invokeLater() :)
 
Bon alors... d'abord ta JProgressbar tu dois lui spécifier un minimum et un maximum. Par défaut ça va de 0 à 100. Toi tu dois spécifier que le maximum est la valeur this.jpbmax.
 
Ensuite, si ta méthode downloadFile() est démarrée dans le thread graphique (en réponse à un clic sur un bouton, un événement clavier ou autres), tu dois changer ça. Il faut que la méthode downloadFile() soit lancée dans un nouveau Thread sinon l'interface graphique va se "figer" pendant toute la durée du download et se débloquer à la fin. Raison pour laquelle tu passes de 0 à 100% en une étape.
 
Troisièmement, tu dois exécuter le code de la méthode setValeur() avec SwingUtilities.invokeLater(). Il s'agit en effet du code qui met à jour l'interface graphique donc il doit s'exécuter dans le thread graphique et invokeLater te permet de lancer des actions dans le thread graphique (puisqu'on est dans le thread qui download).
 
Enfin, tu devrais écrire directement le fichier sur le disque sans le stocker entièrement en mémoire! Surtout si ton fichier fait plusieurs dizaines ou centaines de MégaOctets. Tu dois juste allouer un buffer (je dirais de 16, 32 ou éventuellement 64 KiloOctets) et transférer les données de l'inputstream vers l'outputstream au fur et à mesure qu'elles arrivent, via ce même buffer. Bien sûr, si tu vois que jpbmax est plus petit que la taille de ton buffer, alors alloue un buffer de taille jpbmax pour ne pas gaspiller de mémoire.


Message édité par cbeyls le 21-09-2009 à 03:21:56
n°1925438
Yop69
Posté le 21-09-2009 à 04:22:40  profilanswer
 

hihihi tu es un vrai noctambule :)
Bon je vais me renseigner sur les threads et invokeLater car j'ai des carences a combler après je pense que je comprendrais mieux :)
 
Merci de tes infos que je garde sous le coude.
 
concernant la jprogressbar je l'initialise correctement :
 

Code :
  1. public void jpb(JFrame jf){
  2.         this.jpbCdr = new JFrame("En cours de téléchargement..." );
  3.                 
  4.         JPanel panneau = new JPanel();
  5.         
  6.         this.jpb = new JProgressBar(0, this.jpbmax);
  7.         this.jpb.setStringPainted(true);
  8.         this.jpb.setValue(0);
  9.         this.jpb.setString("0%" );
  10.         
  11.         panneau.add("Center", this.jpb);
  12.         panneau.add("Center", new JLabel("Version " + newVersion()));
  13.         
  14.         this.jpbCdr.getContentPane().add(BorderLayout.CENTER, panneau);
  15.         this.jpbCdr.setSize(275,85);
  16.         this.jpbCdr.setLocation(
  17.                 jf.getX() + (jf.getWidth()-this.jpbCdr.getWidth())/2,
  18.                 jf.getY() + (jf.getHeight()-this.jpbCdr.getHeight())/2
  19.         );
  20.         this.jpbCdr.setResizable(false);
  21.         this.jpbCdr.setVisible(true);
  22.     }


 
et pour le fichier il ne sera pas gros donc je peux le stocker en mémoire pour le moment ! mais en effet ta remarque est juste :)


Message édité par Yop69 le 22-09-2009 à 01:20:29
n°1925567
cbeyls
Hail to the King, Baby
Posté le 21-09-2009 à 15:23:07  profilanswer
 

Si le fichier n'est pas gros... quelle est l'intérêt d'une progressBar?

n°1925621
Yop69
Posté le 21-09-2009 à 18:31:19  profilanswer
 

:D l'intérêt est juste de savoir me servir de la jprogressbar. Je débute en Java et donc j'étudie tout ce qui me parait interessant. Dans ce cas la ca me fait aborder les notions de threads notament :)
 
Donc concernant la JProgressBar ce que je ne comprend pas c'est que un thread est demarré par la méthode start et l'OS a un moment donné va donner du temps processeur a ce thread et executer la methode run() de la classe fille. Mais quand je veux 2 thread qui executent 2 fonctions différentes mais néanmoins liées (l'un est l'affichage graphique et l'autre le download du fichier) je dois obligatoirement faire 2 classes dans 2 fichiers différents ? je trouve cela lourd  
Tous les tutoriaux que je trouve les 2 thread partage la meme methode run et il partage une variable membre de cette classe. Ce qui n'est pas mon cas. :( Peux tu me confirmer ?

n°1925686
cbeyls
Hail to the King, Baby
Posté le 22-09-2009 à 00:38:48  profilanswer
 

Tu ne dois pas faire 2 classes bien au contraire, tu peux déclarer ta classe Runnable et y implémenter une méthode run() ou bien définir un Thread/Runnable directement dans le code:
 

Code :
  1. new Thread() {
  2.   public void run() {
  3.      Runnable guiUpdater = new Runnable() {
  4.         public void run() {
  5.            // code qui met à jour l'affichage de la progressBar
  6.         }
  7.      };
  8.      // boucle de téléchargement
  9.      for(...) {
  10.         ...
  11.         SwingUtilities.invokeLater(guiUpdater);
  12.      }
  13.   }
  14. }.start();


 
Le code ci-dessus définit et démarre un nouveau Thread. Il n'est pas nécessaire d'en garder une référence sauf si tu veux pouvoir appeler interrupt() dessus plus tard.
 
Ici les 2 threads partagent une variable aussi: c'est le nombre de bytes actuellement lus. Ce nombre est mis à jour par le thread qui download et il est aussi utilisé par le thread graphique pour calculer ce que la progressBar doit afficher.


Message édité par cbeyls le 22-09-2009 à 00:46:13
n°1925690
Yop69
Posté le 22-09-2009 à 00:52:47  profilanswer
 

Super !!
Je commence à comprendre la notion de thread et l'interface runnable ! (enfin c'est tellement plus facile avec la solution  ;) )
Ya aussi la classe abstraite SwingWorker que l'on peut utiliser.
 
PS : tu fais comment pour que ton texte dans la balise code apparaisse avec des couleurs ?  :??:


Message édité par Yop69 le 22-09-2009 à 00:54:30
n°1925694
cbeyls
Hail to the King, Baby
Posté le 22-09-2009 à 01:12:14  profilanswer
 

Oui là je le fais à la "mano" mais effectivement SwingWorker peut simplifier la tâche en exécutant des méthodes dédiées que tu redéfinis dans des threads différents. C'est une classe d'aide, nouveauté de Java 1.6. L'idéal est de lire la documentation de la classe, tout est expliqué. Dans ton cas, la méthode doInBackground() serait celle qui effectue le téléchargement. La méthode process() est appelée tout à la fin du téléchargement dans le thread graphique et reçoit le résultat.
 
Quoique je ne trouve pas que le code soit tellement simplifié avec SwingWorker puisque tu devrais créer également un "PropertyChangeListener" qui serait notifié à chaque étape du téléchargement afin de mettre à jour la progressBar. Cela fait beaucoup de tuyauterie.
 
Pour utiliser la coloration syntaxique du code java j'utilise les balises BBCode (code==java)(/code) où les parenthèses doivent être remplacées par des [].

n°1925697
Yop69
Posté le 22-09-2009 à 01:21:53  profilanswer
 

Bon je me rends compte que je ne comprends pas tout  :cry: . Ya encore des choses qui m'échappe. Je vais devoir bosser dessus
 
Je réflechis en séquentiel et pas en évenementiel. J'ai pas mal de mal  :sweat:  
 
Merci pour le bbcode, ca apparait niquel  [:cerveau huit]


Message édité par Yop69 le 22-09-2009 à 01:22:37
n°1925699
cbeyls
Hail to the King, Baby
Posté le 22-09-2009 à 01:35:53  profilanswer
 

Ah c'est sûr que les interfaces graphiques, c'est de l'événementiel à 100%! C'est ce qu'on doit apprendre en dernier après tout le reste. Et puis il y a aussi des gens qui ne comprennent jamais la notion de Thread, un peu comme ceux qui ne parviennent pas à comprendre la notion de récursivité.

mood
Publicité
Posté le 22-09-2009 à 01:35:53  profilanswer
 

n°1925700
Yop69
Posté le 22-09-2009 à 01:38:47  profilanswer
 

J'ai eu du mal avec la récursivité quand on m'a invité à mon taf à en faire pour un forum. Mais ensuite une fois qu'on comprends l'empilement et le dépilement ca roule  :p  
Pareil ici je pense, j'ai encore un peu de mal, mais le vocabulaire et les notions théoriques commencent à rentrer après je pense que je vais "voir" le code beaucoup plus facilement

n°1925701
Yop69
Posté le 22-09-2009 à 01:45:47  profilanswer
 

Une petite question :
 
 

Code :
  1. new Thread() {
  2.   public void run() {
  3.      Runnable guiUpdater = new Runnable() {
  4.         public void run() {
  5.            // code qui met à jour l'affichage de la progressBar
  6.         }
  7.      };
  8.      
  9.    uneMethode(guiUpdater);
  10.  
  11.   }
  12. }.start();
  13.  
  14. uneMethode(Runnable guiUpdater){
  15.  
  16. SwingUtilities.invokeLater(guiUpdater);
  17. }


 
J'ai découpé dans une méthode le téléchargement. Comment je peux passer la référence de guiUpdater au lieu d'avoir une copie locale dans la méthode "uneMethode" ?


Message édité par Yop69 le 22-09-2009 à 01:46:10
n°1925702
Yop69
Posté le 22-09-2009 à 01:49:09  profilanswer
 

Bon apparemment tous les objets sont passés par référence  [:prodigy]

n°1925703
Yop69
Posté le 22-09-2009 à 02:49:20  profilanswer
 

Bon j'ai réduit le code et cela marche très bien :)
Dans downloadfile, j'appelle directement ma méthode qui met à jour la JProgressBar.
 
[code==java]public void traitement(){
  new Thread() {
   public void run() {
    downloadFile();
   }
  }.start();
 }[/java]
 
Quel était l'intérêt de ta méthode ?  :??: Doit on absolument implementer la classe runnable pour la mise à jour graphique ? pourquoi ne pas laisser le nouveau thread gérer cela ?
 
Hum, j'ai changé le fichier qui est téléchargé par un fichier de 4Mo (au lieu de 20Ko) est ce pour cela que maintenant je n'ai pas le saut à 100% directement ?
 
 :cry:  
 

n°1925935
cbeyls
Hail to the King, Baby
Posté le 22-09-2009 à 18:33:15  profilanswer
 

En java les objets sont tous passés par référence et les types primitifs (int, long, float, double, boolean) sont tous passés par valeur.
 
Les instructions qui modifient l'interface graphique doivent être exécutées dans le thread de Swing. je ne dis pas ça pour t'emmerder, c'est écrit dans la documentation. Maintenant peut-être que dans ton cas ça fonctionne pour une raison ou une autre mais ce n'est pas la bonne façon de faire.
 
Et oui on est obligé de passer par une classe qui implémente Runnable en Java car il n'existe pas de pointeurs vers des fonctions dans ce langage.


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Java

  [RESOLU] JProgressBar qui affiche directement 100% :(

 

Sujets relatifs
[Résolu] popen marche pas ???[Résolu tout seul] [GTK] GTK Cell Renderer ne dessine qu'une ligne ?
[RESOLU] SplashScreen qui clignote[Matlab] (Résolu) Décalage de fréquences avec DCT
[Résolu] Informations d'une photo[Résolu] erreur stray \320 \240
Résolu : Récupérer le nom d'un tableau dynamique sous Excel2007[Résolu] warning friend declaration
[RESOLU] Lors de l'éxecution un coup ca marche, un coup ca marche pas[RESOLU]J2ME et la génération de timestamp...
Plus de sujets relatifs à : [RESOLU] JProgressBar qui affiche directement 100% :(


Copyright © 1997-2018 Hardware.fr SARL (Signaler un contenu illicite) / Groupe LDLC / Shop HFR