Swing JProgressBar läuft nicht mit

Snuke

Mitglied
Ich habe wiedermal ein Problem...


Und zwar habe ich eine JProgressBar die mir den Stand des Kopierens anzeigen soll. Extra Threads soll man ja nicht nutzen.

Java:
localPBar = new JProgressBar();
localPBar.setStringPainted(true);
localPBar.setMinimum(0);

und in einem ActionListener hab ich dann (auf Knopfdruck)

Java:
localPBar.setMaximum(vItemsToCutCopy.size());
					localPBar.setValue(0);
					localPBar.setVisible(true);
					
					
					synchronized (vItemsToCutCopy) 
					{
						for(int i = 0; i < vItemsToCutCopy.size(); i++)
						{
							Tree t = (Tree)vItemsToCutCopy.get(i);
							
							File fOld = new File(t.getPath());
							
							if(!fOld.exists())
							{
								localPBar.setMaximum(localPBar.getMaximum()-1);
								
								continue;
							}
							
							File fNew = new File(localPath.getText()+t.getName());
							
							if(localIsCut)
							{
								fOld.renameTo(fNew);
							}else{
								try
								{
									if(fOld.isDirectory())
									{
										copyLocalDirectory(fOld,fNew);
										
										continue;
									}
									
									FileChannel inChannel = new FileInputStream(fOld).getChannel();
									FileChannel outChannel = new FileOutputStream(fNew).getChannel(); 
									inChannel.transferTo(0, inChannel.size(), outChannel);
									
									localPBar.setValue(localPBar.getValue()+1);
									
									
								}
								catch(FileNotFoundException e1){}
								catch(IOException e2){}
							}
							
							
							
							t = null;
							fOld = null;
							fNew = null;
						}
					}
					
					vItemsToCutCopy.removeAllElements();
					
					localPBar.setVisible(false);
					localPBar.setValue(0);

hier ist auch noch die copyDir:

Java:
private void copyLocalDirectory(File fOld,File fNew)
	{
		File[] files = fOld.listFiles();
		
        fNew.mkdirs();
        for (File file : files) 
        {
            if (file.isDirectory()) 
            {
            	copyLocalDirectory(file, new File(fNew.getAbsolutePath() + System.getProperty("file.separator") + file.getName()));
            }else{
            	try
            	{
	            	FileChannel inChannel = new FileInputStream(file).getChannel();
					FileChannel outChannel = new FileOutputStream(fNew.getAbsolutePath() + System.getProperty("file.separator") + file.getName()).getChannel(); 
					inChannel.transferTo(0, inChannel.size(), outChannel);
					
					localPBar.setValue(localPBar.getValue()+1);
					
					
            	}
				catch(FileNotFoundException e1){}
				catch(IOException e2){}
            }
        } 
        
        files = null;
        fOld = null;
        fNew = null;
	}


Das Problem ist das die JProgressBar nicht aktuell ist... d.h. sie wird gar nicht angezeigt.

lg
 
Extra Threads soll man ja nicht nutzen.
Heißt genau? Grundsätzlich muss das kopieren der Daten in einem separaten Thread laufen - wie auch immer man das implementiert. Wenn man den Vorgang im EDT laufen lässt, kann sich die Progressbar nicht aktualisieren bzw. in Deinem Fall gar nicht erst angezeigt.
Die Änderung des Fortschritts der ProgressBar würde ich an einer zentralen Stelle machen.
 
Wenn man den Vorgang im EDT laufen lässt, kann sich die Progressbar nicht aktualisieren bzw. in Deinem Fall gar nicht erst angezeigt.
Die Änderung des Fortschritts der ProgressBar würde ich an einer zentralen Stelle machen.


Ich hab mich etwas eingelesen in das ganze GUI zeug... Ich bin mir allerdings nicht sicher wie man das am bestem umsetzt.


Angenommen ich baue mir eine Funktion:

Java:
private void copy(String from, String to)
{
}

Kann ich bei jedem Funktionsaufruf einen neuen Thread starten?

Java:
private void copy(String from, String to)
{
    Thread t = new Thread(new Runable()
    {
        ...... (run methode und in dieser kopieren sowie die JProgressBar inkrementieren)
    }
}

Denn meine bedenken sind einfach, dass wenn ich 1000 Dateien kopiere das doch ziemlich lastig (immerhin sind das dann 1000 Threads) wird oder nicht?

Oder sollte man am besten mit dem SwingUtilities.invokeLater arbeiten?


lg
 
Wo du den neuen Thread startest ist deine Sache. Möchtest du die Dateien hintereinander oder parallel kopieren?

Wichtig ist, dass der Kopier-Thread pro Fortschritt eine Runnable-Instance der
Code:
StringUtil.invokeLater
gibt. Diese Runnable Instanz aktualisiert dann deinen ProgressBar.
 
Wo du den neuen Thread startest ist deine Sache. Möchtest du die Dateien hintereinander oder parallel kopieren?

Wichtig ist, dass der Kopier-Thread pro Fortschritt eine Runnable-Instance der
Code:
StringUtil.invokeLater
gibt. Diese Runnable Instanz aktualisiert dann deinen ProgressBar.


Ich möchte die Dateien hintereinander kopieren... Dann komme ich also mit einem Thread der das kopieren und updaten der JProgessbar verwaltet aus?
 
Mit invokeLater kannst Du bewusst Code in den EDT (für Darstellung und aktualisierung der GUI verantwortlichen Thread) einschieben. Die "Gefahr" besteht darin, dass Du darin längere Berechnungen oder Deinen Kopiervorgang ausführen lässt.
Aber diese Gefahr besteht immer - auch bei Nutzung des SwingWorkers.

Der Unterschied (Vorteil) beim SwingWorker (ggüber dem direkten erzeugen von Threads) ist, dass Du Dich nicht selbst um das Erstellung eines neuen Threads kümmern musst. SwingWorker hat bestimmte Sachen bereits implementiert und bietet die Methode
Code:
doInBackground()
Alles was hier drinsteht wird in einem durch den Worker erzeugten Thread ausgeführt. Nach Abschluss dieser Methode wird "automatisch" die Methode
Code:
done()
aufgerufen. Diese läuft dann wiederum im EDT.
Um das alles kümmert sich der SwingWorker.

In Deinem Fall willst Du aber regelmäßig den Zwischenstand des Hintergrundprozesses an die GUI übermitteln. Das bedeutet, dass auch wenn Du den SwingWorker benutzt und den Kopierprozess in der
Code:
doInBackground()
durchführen lässt, Du innerhalb der doInBackground die Statusaktualisierung an die GUI per invokeLater() an die GUI "weitermelden" musst.

Es gibt also keine Vor/Nachteile zw. SwingWorker und invokeLater(), weil die auf unterschiedlicher "Ebene" stehen. SwingWorker nimmt dem Programmierer nur das erstellen eines neuen Threads ab und kümmert sich um ein paar Sachen. Aber in beiden Fällen kann die Verwendung von invokeLater() notwendig sein.

[EDIT]Wobei ich jetzt ganz vergessen habe, dass der SwingWorker (ich benutze ihn leider nie) eigene Mechanismen hat (
Code:
publish(...)
), um Informationen aus dem Hintergrund Thread in den EDT zu übertragen.[/EDIT]
 
Zuletzt bearbeitet:
Vielen Dank für die ausführliche Erklärung 🙂... Ich werde das mal mit SwingWorker ausprobieren und dann direkt das Ergebniss Posten
 
So ich habe das ganze ausprobiert aber komme nicht wirklich zurecht. Dateien kann ich Perfekt auf diese Art kopieren. Aber wenn es an einen Ordner samt Inhalt geht, funktioniert das nicht mehr.

Meine CopyDir sieht jetzt so aus:

Java:
private void copyLocalDirectory(File fOld,File fNew)
    {
        File[] files = fOld.listFiles();
        
        fNew.mkdirs();
        for (File file : files) 
        {
            if (file.isDirectory()) 
            {
                copyLocalDirectory(file, new File(fNew.getAbsolutePath() + System.getProperty("file.separator") + file.getName()));
            }else{
                try
                {
                    FileChannel inChannel = new FileInputStream(file).getChannel();
                    FileChannel outChannel = new FileOutputStream(fNew.getAbsolutePath() + System.getProperty("file.separator") + file.getName()).getChannel(); 
                    inChannel.transferTo(0, inChannel.size(), outChannel);
                    
                    localPBar.setValue(localPBar.getValue()+1);
                    System.out.println(localPBar.getMaximum());
                    
                }
                catch(FileNotFoundException e1){}
                catch(IOException e2){}
            }
        } 
        
        files = null;
        fOld = null;
        fNew = null;
    }

und mein Actionlistener:

Java:
localPBar.setMaximum(vItemsToCutCopy.size());
                    localPBar.setValue(0);
                    localPBar.setVisible(true);
                    
                    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>()
                    {
                        protected Void doInBackground()
                        {
                            synchronized (vItemsToCutCopy) 
                            {
                                for(int i = 0; i < vItemsToCutCopy.size(); i++)
                                {
                                    Tree t = (Tree)vItemsToCutCopy.get(i);
                            
                                     File fOld = new File(t.getPath());
                            
                                     if(!fOld.exists())
                                     {
                                         localPBar.setMaximum(localPBar.getMaximum()-1);
                                
                                         continue;
                                     }
                            
                                     File fNew = new File(localPath.getText()+t.getName());
                            
                                     if(localIsCut)
                                     {
                                         fOld.renameTo(fNew);
                                     }else{
                                         try
                                         {
                                             if(fOld.isDirectory())
                                             {
                                                 copyLocalDirectory(fOld,fNew);
                                        
                                                 continue;
                                             }
                                    
                                             FileChannel inChannel = new FileInputStream(fOld).getChannel();
                                             FileChannel outChannel = new FileOutputStream(fNew).getChannel(); 
                                             inChannel.transferTo(0, inChannel.size(), outChannel);
                                    
                                             localPBar.setValue(localPBar.getValue()+1);
                                         }
                                         catch(FileNotFoundException e1){}
                                         catch(IOException e2){}
                                     }    
                                     t = null;
                                     fOld = null;
                                     fNew = null;
                                } 
                          }
                          return null;
                    }

                    protected void done()
                    {
                    }
               };
               worker.execute();

Das Problem ist jetzt noch, dass die JProgressBar (wenn ich Ordner kopiere) sofort auf 100% springt und ca. 5 Sekunden später kann ich im Konsolenfenster beobachten wie die Funktion: copyLocalDirectory immer 1er ausspuckt.

Ich probier das schon den ganzen Abend 😡 Komme bisher leider nicht weiter. Weiß jemand was das Problem ist?

lg
 
Zunächst einmal würde ich den SwingWorker in eine eigene Klasse machen damit man mehr Übersicht hat, auch auf welche Variablen er zugreift.

Wenn du im SwingWorker (bzw. in irgendeinem Thread außer dem EDT) etwas wie
Java:
localPBar.setMaximum(localPBar.getMaximum()-1);
machst, ist das prinzipiell problematisch, weil es ein Aufruf an Swing ist und der sollte im EDT erfolgen. Das heißt solche Aufrufe entweder im bereits genannten
Code:
SwingUtilities.invokeLater()
verpacken oder den
Code:
publish()
-Mechanismus vom SwingWorker nutzen.

Prinzipiell hat der SwingWorker einen "progress"-Mechanismus, den man über einen PropertyChangeListener abfragen kann. Hier ist das mit dem EDT auch schon automatisch geregelt, sodass man im Minimalfall folgendes machen muss:

Java:
// GUI-Impl:
void onStartBackgroundTask() {
    SwingWorker worker = new MySwingWorker();
    worker.addPropertyChangeListener(new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent event) {
            // diese Event löst der Worker automatisch im EDT aus
            if ("progress".equals( event.getPropertyName )) {

                // man holt sich den aktuellen Fortschritt und setzt seine
                // Fortschrittsleiste entsprechend
                int progress = (int) event.getNewValue();
                myProgressBar.setValue(progress);
            }
        }
    });
    worker.execute();    
}

// swing worker
void doInBackground() {
    setProgress(0); //löst PropertyChangeEvent aus, s.o.
    
    while (blablabla) {
        setProgress( xy );
    }
    ...
}
 

Neue Themen


Zurück
Oben