JProgressBar richtig anwenden

Spewer

Aktives Mitglied
Hallo liebe Leute,

heute habe ich mal wieder ein Problem 😀

Aktuell versuche ich in mein Programm eine JProgressBar zu implementieren.
Diese soll von Anfang bis Ende laufen, wenn ich auf einen Button in meiner GUI klicke.
Beim klicken auf den Button wird ein Objekt erzeugt, welches Daten importiert. Dies dauert ein wenig, deswegen der Gedanke mit der ProgressBar.

Java:
if (e.getSource() == btnImport)
	{
	    benutzer = txtFldBenutzer.getText();
	    learnlog = txtFldLearnlog.getText();
	    
	    if (benutzer == null || learnlog == null || "".equals(benutzer) || "".equals(learnlog))
	    {
		String error = "Die Daten konnten nicht importiert werden. Bitte überprüfen Sie, ob die Dateien richtig ausgewählt wurden.";
		new ErrorDialog(error);
	    }
	    else
	    { 
//Hier muss denke ich die ProgressBar implementiert werden
		ImportData data = new ImportData(benutzer, learnlog);
	    }
	}

Habe schon gesucht und diverse Seiten gefunden, jedoch hat mir nichts wirklich geholfen.
Muss ich innerhalb der if-Schleife
Code:
new Thread()
verwenden ?

Wäre euch dankbar für ein paar Tipps
 
Hallo zusammen,

ich klinke mich mit meiner Frage mal hier ein: ich schreibe ein sehr umfangreiches Java-Programm, in dem ich Text-Dateien importiere, manipuliere und wieder exportiere.
Diese ganzen Schritte möchte ich mit einem ProgressMonitor veranschaulichen.

Das ganze läuft wie folgt ab: der Nutzer klickt auf einen Button, der ActionListener ruft dann bspw. die Import-Methode auf, in der der ProgressMonitor gezeigt und aktualisiert werden soll.
Ich habe mittlerweile schon herausgefunden, dass es so nicht geht:
Java:
JButton button = new JButton("klick mich");
button.addActionListener(this);

public void actionPerformed(ActionEvent e)
{
    if (e.getSource() == button)
    {
        readFile();
    }
}

public void readFile()
{
    ProgressMonitor progress_monitor = new ProgressMonitor(this, "", "", 0, max);
    progress_monitor.setNote("Reading");
    for (int i = 0; i < max; i++)
    {
        progress_monitor.setProgress(i);
    }
}
So erscheint zwar der ProgressMonitor, man erkennt aber keinen Fortschritt, er wird nicht gezeichnet.

Nach einigem Suchen habe ich folgende Website gefunden: JProgressBar
Diesen Code konnte ich auch erfolgreich umsetzen, aber ich scheitere jetzt daran, ihn auf mein Problem anzuwenden. Hier ein KSKB:

Java:
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;

public class MainFrame extends JFrame
{
	JButton button2;
	ProgressMonitor progress_monitor;
	int max, i, progress;
	Thread thread;

	public MainFrame()
	{
		max = 100;
		i = 0;
		progress = 0;
		
		progress_monitor = new ProgressMonitor(null, "", "bla", 0, max);
    	        progress_monitor.setMillisToDecideToPopup(1);
    	        progress_monitor.setMillisToPopup(1);
		
		ActionListener increaseActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e)
			{
				System.out.println("start action");
					
				thread = new Thread(runnable);
			        thread.start();
			        doSomethingDifficult();
			}
		};
        
                button2 = new JButton("doSomethingDifficult");
                button2.addActionListener(increaseActionListener);
        
                getContentPane().add(button2);
        
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		pack();
		setVisible(true);
	}
	
	public static void main(String[] args)
	{
		new MainFrame();
	}
	
	Runnable runnable = new Runnable()
	{
	    public void run()
	    {
	    	// ProgressMonitor
	    	while (progress <= progress_monitor.getMaximum())
	    	{
	    		progress_monitor.setProgress(progress);
	    	}
	    }
	};
	  
	public void doSomethingDifficult()
	{
		for (i = 0; i <= max; i++)
		{
			try
			{
				Thread.sleep(100);
				progress = i;
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}
Das Resultat bleibt das gleiche: ich sehe den ProgressMonitor, aber er wird nicht aktualisiert bzw. die Aktualisierung ist nicht sichtbar.
Die einzige Möglichkeit, die ich im Moment sehe, wäre alle meine Methoden, die ich zum Import, etc. verwende, auf mehrere Runnable-Objekte zu verteilen, aber mein Bauchgefühl sagt mir, dass das sicher nicht notwendig ist.

Könnt ihr mir helfen?

VG =)
 
Vielen Dank für deine prompte Antwort! In einem anderen Forum warte ich schon seit 2 Tagen auf Rückmeldung -,-

Wenn ich deinen geposteten Link richtig verstehe, stecke ich dann den Inhalt meiner Methode
Code:
doSomethingDifficult
und den ProgressMonitor in die Methode
Code:
doInBackground
des SwingWorkers, richtig?
Also lag ich mit meiner Vermutung gar nicht so schlecht, dass sich das nicht so ohne weiteres umsetzen lässt, weil ich benötige dann ungefähr 15 SwingWorker ^^
 
Ich bin gerade etwas am verzweifeln. Ich habe das mit dem SwingWorker versucht und umgesetzt, funzt soweit auch:
Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;


public class MainFrame extends JFrame
{
	JButton button2;
	ProgressMonitor progress_monitor;
	int max, i, progress;
	Thread thread;

	public MainFrame()
	{
		max = 100;
		i = 0;
		progress = 0;
		
		progress_monitor = new ProgressMonitor(null, "", "bla", 0, max);
    	progress_monitor.setMillisToDecideToPopup(1);
    	progress_monitor.setMillisToPopup(1);
		
		ActionListener increaseActionListener = new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				System.out.println("starte action");				

				final MyTask task = new MyTask();
				task.addPropertyChangeListener(new PropertyChangeListener()
				{
					public void propertyChange(PropertyChangeEvent evt)
					{
						if (task.isDone() == true)
						{
							System.out.println("beende task");
							task.cancel(true);
						}
					}
				});
				task.execute();
				
//				while (task.isDone() == false && task.isCancelled() == false)
//				{
//					System.out.println("warte");
//				}
				System.out.println("fertig");
			}
		};
        
        button2 = new JButton("erhöhe pm");
        button2.addActionListener(increaseActionListener);
        
        getContentPane().add(button2);
        
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		pack();
		setVisible(true);
	}
	
	public static void main(String[] args)
	{
		new MainFrame();
	}
	
	class MyTask extends SwingWorker<Void, Void>
	{
		public Void doInBackground()
		{
			for (i = 0; i <= max; i++)
			{
				try
				{
					Thread.sleep(50);
					progress = i;
					progress_monitor.setProgress(progress);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			progress_monitor.close();
			return null;
		}
		
		public void done()
		{
			System.out.println("done");
		}
	}
}

Ich möchte warten, bis der SwingWorker fertig ist (ohne geladene Daten macht Weiterarbeiten wenig Sinn), das verursacht folgende Probleme:
1. Kommentiere ich diese Zeile wieder ein:
Java:
while (task.isDone() == false && task.isCancelled() == false)
{
	System.out.println("warte");
}
Code:
task.isDone()
ist immer false, d.h. ich lande in einer Endlosschleife!
2. durch die WHILE-Schleife wird der ProgressMonitor zwar angezeigt, aber wieder nicht richtig gezeichnet 🙁
 
Hallo turtle,

danke für deinen Link! Habe meinen Quellcode angepasst und nun wird gewartet, bis der Thread fertig ist (durch meine WHILE-Schleife).
Allerdings ist der ProgressMonitor jetzt ÜBERHAUPT nicht mehr zu sehen. Kommentiere ich die WHILE-Schleife aus, sehe ich ihn wieder...

VG =)
 
So sieht mein Code jetzt aus:

Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitor;
import javax.swing.SwingWorker;


public class MainFrame extends JFrame
{
	JButton button2;
	ProgressMonitor progress_monitor;
	int max, i, progress_value;
	Thread thread;

	public MainFrame()
	{
		max = 100;
		i = 0;
		progress_value = 0;
		
		progress_monitor = new ProgressMonitor(null, "", "bla", 0, max);
    	progress_monitor.setMillisToDecideToPopup(1);
    	progress_monitor.setMillisToPopup(1);
		
		ActionListener increaseActionListener = new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				System.out.println("starte action");				

				final MyTask task = new MyTask();
				task.execute();
				
//				while (task.isDone() == false)
				{
//					System.out.println("task.isDone " + task.isDone());
//					System.out.println("task.isCancelled " + task.isCancelled());
//					System.out.println("warte");
				}
				System.out.println("fertig");
			}
		};
        
        button2 = new JButton("erhöhe pm");
        button2.addActionListener(increaseActionListener);
        
        getContentPane().add(button2);
        
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		pack();
		setVisible(true);
	}
	
	public static void main(String[] args)
	{
		new MainFrame();
	}
		
	class MyTask extends SwingWorker<Void, Integer>
	{
		protected void process(List<Integer> chunks) {
            for (Integer progress: chunks) {
            	progress_monitor.setProgress(progress_value);
            }
        }
		
		public Void doInBackground()
		{
			for (i = 0; i <= max; i++)
			{
				try
				{
					Thread.sleep(50);
					progress_value = i;
					publish(progress_value);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			progress_monitor.close();
			return null;
		}
		
		public void done()
		{
			System.out.println("done");
		}
	}
}

Es ist bestimmt nur eine Kleinigkeit, die ich nicht sehe/finde.

VG =)
 
Wenn du eine Schleife im Listener startest hast du das selbe Ergebnis. Sobald der Worker fertig ist ruft er done() im EDT auf, dort kannst du dann fortfahren.
 
Ich nehme an das "fertig" soll nach dem "done" erscheinen. Die Ausgabe muss aus der done Methode heraus angestossen werden. Diese ist ja dafür da, dass man nach Abschluss des Threads eine Aktion anstossen kann.
 
also die gewünschte Ausgabe sollte sein:

Code:
starte action
done
fertig

jetzt wird aber ausgegeben:

Code:
starte action
fertig
done

Ist auch logisch, da die WHILE-Schleife auskommentiert ist und der SwingWorker noch arbeitet... Kommentiere ich die WHILE-Schleife ein, so habe ich zwar die gewünschte Ausgabe, jedoch wird der ProgressMonitor nicht mehr gezeichnet.

VG =)
 
Ja, da die Schleife den EDT blockiert können wärend dessen keine GUI-Aktivitäten (wie Zeichnen) vorgenommen werden. Ansonst würde der ganze Worker nichts bringen. 😉

Schreib in done(), was nach der Aktivität passieren soll.
 
naja, das ist ja gut und schön, dass ich das in done() schreiben soll, das nützt mir aber problemspezifisch nix 😉 Ich schreibe eine 3D-Applikation. In diese sollen Daten geladen werden und der Ladefortschritt mit dem ProgressMonitor angezeigt werden. Wenn ich das mit dem SwingWorker mache, bestünde bei langen Ladevorgängen die Möglichkeit, dass der Benutzer weiterarbeitet, weil ja das Laden in nem eigenen Thread läuft.
Genau das darf/soll aber nicht sein. Der Nutzer MUSS warten.

Ich bin auch nicht scharf auf den SwingWorker 😉 Ich bin ein (notgedrungener) Java-Quereinsteiger und froh, dass ich das alles soweit so gut zusammengeschustert habe, auch wenn ein Java-Profi sicherlich manchmal die Augen verdrehen würde ^^ Aber iwie muss ich das zum Laufen kriegen.

VG =)
 
Du könntest einfach eine boolean setzen, die das Weiterarbeiten verhindert.
Alternativ könntest du eine JProgressBar auf einen modalen JDialog einblenden. Ich weiß nicht wie das mit dem 3D-Teil zusammenarbeitet, aber ein modaler JDialog blockt grundsätzlich Eingabeevents des Parent-Fensters.
 
Die alternative Variante schaue ich mir morgen @work mal an und berichte.
So ein Aufwand wegen nem ProgressBar/-Monitor... I miss .NET 😀

Danke einstweilen!
 
Ich verstehe grad iwie die ganze Aufregung nicht.... Wahrscheinlich hab ich das Problem nicht richtig begriffen 😉

Du möchtest den EDT blockieren, damit der User nichts mehr machen kann, richtig?

Java:
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
 
public class MainFrame extends JFrame
{
    public static void main(String[] args)
    {
	JFrame frame=new JFrame();
	JProgressBar bar=new JProgressBar();
	
	frame.add(bar);
	frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	frame.pack();
	frame.setVisible(true);
	
	bar.setStringPainted(true);

	for(int x=0; x<100; x++)
	{
	 	bar.setValue(x);
		frame.repaint();
		try{Thread.sleep(100);}catch(Exception e){}
	}
	frame.dispose();
     }
}

Hier ist nochmal eine andere 'Version', die vllt besser passt:
Java:
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import java.lang.Runnable;

public class MainFrame
{
    public static void doBar()
    {
	JFrame frame=new JFrame();
	JProgressBar bar=new JProgressBar();
	
	frame.add(bar);
	frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	frame.pack();
	frame.setVisible(true);
	
	bar.setStringPainted(true);

	for(int x=0; x<100; x++)
	{
	 	bar.setValue(x);
		frame.repaint();
		try{Thread.sleep(100);}catch(Exception e){}
	}
	frame.dispose();
	System.exit(0);
    }

    public static void main(String[] args)
    {
	Runnable runnable=new Runnable()
	{
		public void run()
		{
			doBar();
		}	
	};
	Thread thr=new Thread(runnable);
	thr.start();
	loadData();
     }
   
   public static void loadData()
   {
//HIER MUSS DANN DIE METHODE REIN, MITDER DU DIE DATEIEN LÄDST
	while(true){System.out.println("ICH LADE....");}
   }

}
 
Zuletzt bearbeitet:
Die main wird nicht im EDT ausgeführt!
Dein Source blockt keine Eingaben und ist sehr unsicher, da du die GUI-Aktivitäten im Main-Thread ausführst.

Ich denke ein modaler Dialog wäre das Beste, sofern das mit dem 3D zusammenarbeitet.
 
Weiß nicht, der Fragesteller hat ja nichts wirklich genaueres über das Programm gesagt.
Folgendes wäre möglich:

Java:
private static JFrame frame;

public static void main(String[] args) {
	EventQueue.invokeLater(new Runnable() {
		public void run() {
			buildGui();
		}
	});
}
private static void buildGui() {
	frame = new JFrame();
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.add(new JButton(new AbstractAction("Drück mich zum Laden.") {
		public void actionPerformed(ActionEvent e) {
			startLoading();
		}
	}));
	
	frame.pack();
	frame.setVisible(true);
}
private static void startLoading() {
	final JDialog dialog = new JDialog(frame, true);
	dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
	
	final JProgressBar bar = new JProgressBar();
	bar.setStringPainted(true);
	dialog.add(bar);
	
	dialog.pack();
	dialog.setLocationRelativeTo(frame);
	
	new SwingWorker<Void, Integer>() {
		protected Void doInBackground() throws Exception {
			//Hier wird geladen
			for(int i = 0; i < 100; i++) {
				TimeUnit.MILLISECONDS.sleep(100L);
				publish(i);
			}
			return(null);
		}
		protected void process(List<Integer> chunks) {
			for(int i:chunks) {
				bar.setValue(i);
			}
		}
		protected void done() {
			try {
				get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
			
			dialog.dispose(); //Eigendlich Threat-Safe, kann auch in die doInBackground
		}
	}.execute();
		
	dialog.setVisible(true);
}

Hier reagiert das Hauptframe wärend des Ladevorgangs nicht mehr auf Events.
 
Gut, dann hole ich mal gaaaaaaaanz weit aus 😉
Ich entwickle eine Anwendung, die Java3D implementiert, d.h. vergleichbar mit Programmen wie Gocad, Surpac, ArcScene, wem das was sagt.
Nun gibt es in der Anwendung versch. Prozesse (z.B. Laden, Generierung von Stützstellen, Export), die je nach Umfang sehr lange dauern können, es aber keinen Sinn macht, wenn der Nutzer dann weiterarbeiten kann. Ich hoffe, das war einigermaßen verständlich 😉 Bitte habt Verständnis, dass ich hier nicht die Anwendung posten kann.

@KingOfExceptions: Aufregung gibt's hier keine ^^ Deine Variante funktioniert, allerdings soll der PrgressBar/-Monitor erscheinen, wenn ich per Button-Klick (also ActionListener) eine Methode (bspw. ReadFile() oder CreateSomething()) aufrufe.

@Volvagia: die letzte Variante funktioniert als KSKB so wie gewünscht! Ich werde mich gleich mal daran machen, dass in meine Anwendung einzupflegen und testen.
Kannst du mir erklären, warum das so wie von dir programmiert funktioniert? Du hast ja "meine" WHILE-Schleife eingespart...

VG =)
 
Das liegt am Dialog.
Da er modal ist (2. Konstruktorparameter) und ein Parent-Fenster hat blockt er alle Eingaben in das Hauptfenster und hält ab dem Visible den momentanen EDT an, startet aber wärend dessen seine eigene Eventabhandlung wodurch andere Events weiter dispatcht werden können. (Genau kenne ich diesen Teil des Sources von JDialog nicht, der ist für meine Verhältnisse sehr kompliziert.)
Sobald der Dialog nichtmehr sichtbar ist und er mit allen Events fertig ist wird das momentane Event weiter abgearbeitet. Du könntest deshalb auch unter das dialog.setVisible(true) noch einen weiteren Vorgang einbauen, der erst nach dialog.dispose() aufgerufen wird.
 
Sehr sehr cool, das läuft prima!

Nur noch eine Frage: deine Methode startLoading() hat ja jetzt keinen Rückgabewert. Das betrifft auch 99% meiner Methoden, allerdings habe ich eine dabei, die eine ArrayList<String> zurückgeben soll.
Ich kann das return-Statement ja jetzt nicht einfach unter visible = true setzen, da arbeitet der SwingWorker ja noch...

Ich meine es also folgendermaßen:

Java:
private static ArrayList<String> startLoading() {
    ArrayList<String> result = new ArrayList<String>();

    final JDialog dialog = new JDialog(frame, true);
    dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
    
    final JProgressBar bar = new JProgressBar();
    bar.setStringPainted(true);
    dialog.add(bar);
    
    dialog.pack();
    dialog.setLocationRelativeTo(frame);
    
    new SwingWorker<ArrayList<String>, Integer>() {
        protected ArrayList<String> doInBackground() throws Exception {
            //Hier wird geladen
            for(int i = 0; i < 100; i++) {
                TimeUnit.MILLISECONDS.sleep(100L);

                // mach etwas mit result

                publish(i);
            }
            return result;
        }
        protected void process(List<Integer> chunks) {
            for(int i:chunks) {
                bar.setValue(i);
            }
        }
        protected void done() {
            try {
                get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            
            dialog.dispose(); //Eigendlich Threat-Safe, kann auch in die doInBackground
        }
    }.execute();
        
    dialog.setVisible(true);

So meckert Eclipse, dass die Methode startLoading() keinen Rückgabewert hat. Wie bekomme ich das noch hin?

VG =)
 
Nein. Nach setVisible(true) geht es erst weiter, nach dem dispose (oder setVisible(false)) aufgerufen wurde.

Java:
private static void buildGui() {
	frame = new JFrame();
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.add(new JButton(new AbstractAction("Drück mich zum Laden.") {
		public void actionPerformed(ActionEvent e) {
			List<String> stringList = startLoading();
		
			for(String s:stringList) {
				System.out.println(s);
			}
		}
	}));
	
	frame.pack();
	frame.setVisible(true);
}
private static List<String> startLoading() {
	final List<String> stringList = new ArrayList<String>(3);
	
	final JDialog dialog = new JDialog(frame, true);
	dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
	
	final JProgressBar bar = new JProgressBar();
	bar.setStringPainted(true);
	dialog.add(bar);
	
	dialog.pack();
	dialog.setLocationRelativeTo(frame);
	
	new SwingWorker<Void, Integer>() {
		protected Void doInBackground() throws Exception {
			//Hier wird geladen
			for(int i = 0; i < 100; i++) {
				TimeUnit.NANOSECONDS.sleep(1L);
				stringList.add(String.valueOf(i + 1));
				publish(i);
			}
			return(null);
		}
		protected void process(List<Integer> chunks) {
			for(int i:chunks) {
				bar.setValue(i);
			}
		}
		protected void done() {
			try {
				get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
			
			dialog.dispose();
		}
	}.execute();
		
	dialog.setVisible(true);
	
	return(stringList);
}
 
Also es scheint jetzt tatsächlich alles zu funktionieren! Bin zwar noch sehr vorsichtig, da ich mir gerade nen richtigen Dummpunkt geleistet habe, aber schaut echt gut aus und ich werde jetzt noch ausgiebig testen.

Mein lieber Volvagia, du hast keine Ahnung, wie sehr du mir geholfen hast! Nen riesigen Dank! Wirst im Abspann erwähnt :applaus:

VG =)
 
Zuletzt bearbeitet:

Zurück
Oben