 
View Full Version : [JAVA]Componente progress bar sfarfalla
Ho creato un programmino che effettua l'FTP comprensivo di stato di avanzamento del processo tramite componente JProgressBar.
La progress bar l'ho inserita all'interno di un ciclo while che invia byte per byte il file.
Il problema è l'effetto sfarfallio che si ottiene quando viene ridisegnata ogni volta la progress bar.
while (true) { 
        c = is.read(); // LEGGO DAL FILE BYTE PER BYTE
        if (c < 0) 
          break; 
        os.write(c); 
        numByte++;
        percentualeStatus = (int)Math.round(((numByte) / byteTotal) * 100);
       
        if (percentualeStatus != percentualeStatusOld) {
          jProgressBarFTP.setValue(percentualeStatus); // SETTO IL VALORE IN PERCENTUALE
          percentualeStatusOld = percentualeStatus;
          jProgressBarFTP.paint(jProgressBarFTP.getGraphics()); //RIDISEGNO IL COMPONENTE
        }
} 
Come posso togliere lo sfarfallio? Per mezzo di un thread? Ma in corrispondenza solo del ridisegnamento del componente?
Aiuto, grazie.
sottovento
09-05-2006, 17:04
...omissis...
Come posso togliere lo sfarfallio? Per mezzo di un thread? Ma in corrispondenza solo del ridisegnamento del componente?
Aiuto, grazie.
Ciao,
  in effetti e' la soluzione migliore: crei un nuovo thread ed all'interno di quest'ultimo esegui le operazione di lettura dalla rete, mentre il thread principale sara' libero di rinfrescare la tua progress bar.
In questo modo non solo elimini la paint(), causa dei tuoi problemi: puoi anche integrare la tua progress bar in un dialogo/frame con un bottone "Cancella" per fermare l'esecuzione (ti basta una semplice variabile booleana).
High Flying
Sottovento
Questa riga
jProgressBarFTP.paint(jProgressBarFTP.getGraphics()); //RIDISEGNO IL COMPONENTE 
è un po' anomala.
L'invocazione di setValue provvede in via autonoma ad aggiornare l'aspetto della barra di progresso.
Nel caso in cui non provveda c'è qualcosa che non va. Una svista tipica è l'invocazione di "setValue" ad opera di un Thread diverso dallo AWT Event Dispatcher Thread. E' possibile (solo possibile) che sia il tuo caso perchè leggo che maneggi byte lungo un flusso, cosa a cui si deve dedicare un Thread certamente diverso dall'Event Dispatcher. In questo caso circondi l'invocazione che aggiorna il componente AWT/Swing con uno:
//final value = percentualeStatus;
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        jProgressBarFTP.setValue(value);
    }
});
Sinteticamente, "invokeLater" fa sì che il metodo "run" del Runnable che riceve come argomento sia eseguito da quell'unico Thread (Event Dispatcher) che può e deve produrre un corretto aggiornamento grafico dei componenti.
Grazie per le risposte.
Avevo già provato a far girare il ciclo while() all'interno di un thread, ma non funziona. Mi da lo stesso errore se inserisco il pezzo di codice suggeritomi da PGI-Bis, errore di scrittura sul socket e mi blocca la rete.
Non posso più accedere alla rete.
Come posso ripristinare lo stato iniziale? Io ho provato con net stop e net start, ma non ha effetto, devo riavviare la macchina per avere risultati.
Bà????
sottovento
10-05-2006, 16:45
Grazie per le risposte.
Avevo già provato a far girare il ciclo while() all'interno di un thread, ma non funziona. Mi da lo stesso errore se inserisco il pezzo di codice suggeritomi da PGI-Bis, errore di scrittura sul socket e mi blocca la rete.
Non posso più accedere alla rete.
Come posso ripristinare lo stato iniziale? Io ho provato con net stop e net start, ma non ha effetto, devo riavviare la macchina per avere risultati.
Bà????
Il suggerimento di PGI-Bis e' corretto (come sempre). E' probabile che ci sia qualcos'altro da controllare. Se vuoi, pubblica il codice, lo guardiamo insieme.
Cmq alcuni suggerimenti:
- per quanto riguarda lo spostamento del ciclo while all'interno di un thread, e' la soluzione che adotto praticamente sempre. Non ho voglia di inventare cose nuove, per cui uso sempre quella in quanto e' semplice e robusta.
- Ovviamente l'apertura di un eventuale dialogo di attesa, con la relativa JProgressBar NON deve esser fatta in quel thread: se vuoi aprire una simile finestra, lo dovrai fare dal thread principale.
- La rete non viene influenzata da questo. Pubblica il codice, vediamo di risolvere. Beh, a dire il vero ora vado a dormire, ma su questo forum c'e' sicuramente gente molto piu' in gamba di me che ti sa aiutare....
High Flying
Sottovento
ok, non sono d'accordo sul mettere tutto il codice, perchè penso che sia fastidioso leggere tutte queste righe, ma se tu mi dici che le leggeranno...
metto solo il metodo che implementa l'invio del file in ftp, la jProgressBar è nella classe che richiama questo metodo:
 private void inviaFileFTP (String pathFile)  {
    double byteTotal = 0;
    String fileName = "";
    int errorConnect;
    int percentualeStatusOld = 0;
    
    errorConnect = FTPConnect (utente.getFtpConfig().getHost(), utente.getFtpConfig().getUser(), utente.getFtpConfig().getPass());
    if (errorConnect > 0 && errorConnect != 230) {
      return ;
    }
    
    String file_da_aprire = pathFile; 
    File inputFile = new File(file_da_aprire);
    
    byteTotal = inputFile.length();
    fileName = inputFile.getName();
  
    textAreaFTP.append("transfert: " + fileName + " " + String.valueOf(byteTotal) + " byte\r\n");
    
    OutputStream os = null;
    FileInputStream is = null;
     
    try {  
       ftp.setFileType( FTPClient.BINARY_FILE_TYPE );
       int indexToken = fileName.lastIndexOf(".");
       
       String ext = fileName.substring(indexToken);
       newNameFile = "FILE_" + DateUtilities.getString(materiale.getDataPubblicazione()) + ext;
       
       os = ftp.storeFileStream(newNameFile); 
            
      is= new FileInputStream( file_da_aprire); 
      int c; 
      int numByte = 0;
      
      jProgressBarFTP.setValue(0);
      jProgressBarFTP.setStringPainted(true);
      
      
      while (true) { 
        c = is.read(); 
        if (c < 0) 
          break; 
        os.write(c); 
        numByte++;
       final int percentualeStatus = (int)Math.round(((numByte) / byteTotal) * 100);
        //textField1.setText(String.valueOf(numBuyte));
        //System.out.println(percentualeStatuts);
        if (percentualeStatus != percentualeStatusOld) {
          jProgressBarFTP.setValue(percentualeStatus);
          /*
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  jProgressBarFTP.setValue(percentualeStatus);
              }
          });
          */
          percentualeStatusOld = percentualeStatus;
          
          //jProgressBarFTP.paint(jProgressBarFTP.getGraphics());
        }
      } 
      
      
      //InviaFile iFile = new InviaFile(is, os, jProgressBarFTP, byteTotal);
      //Thread t = new Thread(iFile);
      //t.run();
      os.close(); 
      is.close(); 
     
      textAreaFTP.append(ftp.getReplyString());
     
      
     }
     catch (Throwable t) {
        MessageDialog.throwable(this, t);
    }  
    
    FTPDisconnect ();
  
  }
   
}
sottovento
10-05-2006, 17:05
ok, non sono d'accordo sul mettere tutto il codice, perchè penso che sia fastidioso leggere tutte queste righe, ma se tu mi dici che le leggeranno...
metto solo il metodo che implementa l'invio del file in ftp, la jProgressBar è nella classe che richiama questo metodo:
... omissis ...
    
}
Buon codice. Tuttavia le parole che hai scritto sopra sembrano piu' interessanti:
la jProgressBar è nella classe che richiama questo metodo
Se si tratta dello stesso thread, allora non hai chance di aggiornare correttamente la jprogress bar, in quanto terrai questo thread "fermo" nella tua funzione. La tua funzionalita' deve quindi essere spostata in un altro thread e lasciare che il thread che gestisce la jProgressBar possa ritornare il controllo alla virtual machine, la quale provvedera' all'aggiornamento.
In alternativa, usa il codice di PGI-Bis,  ma ti consiglio vivamente di fare questa funzione in un thread separato. Si tratta di poco lavoro....
Per quanto riguarda la rete, non vedo alcun problema. Purtroppo non ho il tempo di fare prove...
High Flying
Sottovento
Grazie, grazie ancora. 
Eppure ogni volta che metto il codice di PGI o  se vedi la parte commentata c'era un therad che faceva il ciclo, mi blocca la rete.
I canali os e is li chiudo, forse devo chiuderli all'interno del thread, adesso provo
Ancora grazie.
si funziona, mettendo la chiusura dei canali is ed os all'interno del thread l'invio del file avviene e si chiudono senza bloccarmi la rete, solamente il thread sembra essere sempre bloccante, perchè la jProgressBar non si visualizza se non alla fine (100%).
La classe InviaFile 
package it.computime.object;
import cfc.swing.MessageDialog;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
public class InviaFile implements Runnable {
  int c; 
  int numByte = 0;
  int percentualeStatus = 0, percentualeStatusOld = 0;
  FileInputStream is = null;
  OutputStream os = null ;
  JProgressBar jProgressBar = null;
  double byteTotal = 0;
  
  public InviaFile(FileInputStream is, OutputStream os, JProgressBar jProgressBar, double byteTotal)   {
    this.is = is;
    this.os = os;
    this.jProgressBar = jProgressBar;
    this.byteTotal = byteTotal;
  }
  
  public void run() {
    try {  
      while (true) { 
          c = is.read(); 
          if (c < 0) 
            break; 
          os.write(c); 
          numByte++;
          percentualeStatus = (int)Math.round(((numByte) / byteTotal) * 100);
         
          if (percentualeStatus != percentualeStatusOld) {
            jProgressBar.setValue(percentualeStatus);
            percentualeStatusOld = percentualeStatus;
            jProgressBar.repaint();
          }
        }
      os.close(); 
      is.close();  
    }  
    catch (IOException e) {
        JOptionPane.showMessageDialog(null, e);
    }     
  }
}
.l'errore è nel far partire il thread con run, ecco perchè è bloccante.
Devo farlo partire con il metodo start(), però mi dice "Bad File Descriptor"
eppure il file è is............?????
forse ho capito, perchè chiuso subito il canale, il thread parte e io gli chiudo il canale sotto... il puzzle si chiude!
Risolto.
Ho sbagliato di concetto.
Io pensavo di dover mettere nel thread solo il ciclo while, ma da come si vede nel metodo, si aprono canali e si chiudono al termine dell'operazione.
Mettendo tutte le operazioni del metodo all'interno del thread e spostando l'intero controllo dell'operazione al thread, funziona tutto benissimo.
E' vero, la finestra principale ha la possibilità di ridisegnarsi, quindi spostando anche la finsestra la progressBar non sfarfalla (utilizzando solamente setValue()) e indica il progresso del processo.
Grazie per i fantastici aiuti.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.