Verzweifelt an der ProgressBar

Gordon80

Grünschnabel
Hallo an Alle,
das Anzeigen einer JProgressBar in meinem Fenster raubt mir gerade den letzten nerv. Ich sitze da jetzt seit Tagen dran und komm nicht weiter. Hab schon unzählige Foren durchstöbert und alles ausprobiert.
Nun poste ich mal mein Problem, evtl. hab ich irgendetwas übersehen oder falsch verstanden.

Ich hab ein Fenster bei dem nach betätigen des Buttons einige (theoretisch bis 999, in der Praxis so um die 100) Einträge in einer Hashtable gespeichert werden sollen. Die Einträge werden dynamisch in einer for-Schleife erzeugt. (Fahrplan von einer Bahn - erste Fahrt wird angegeben und dann alle xx Minuten wiederholen). Während des speicherns soll eine ProgressBar angezeigt werden.
Das hinzufügen selber hab ich in einen SwingWorker gepackt. Ich hab den erweitert um die Variable int progress und die Methode int getProgress(), so soll der Prozentwert des Fortschritts aus dem SwingWorker ausgelesen werden.
Hier nun der Code:
Code:
    private void WiederhAddBtnActionPerformed(java.awt.event.ActionEvent evt) {
        ProgressBar.setVisible(true);
        paintAll(this.getGraphics());
        final SwingWorker Adder = new SwingWorker() {
            
            private boolean runs = false;
            
            public Object construct() {
                runs = true;
                int readInt = -1;
                int h = -1;
                int min = -1;
                int takt = -1;
                int anzahl = -1;
                Linie linie = (Linie) WiederhLinienBox.getSelectedItem();
                int tag = EinmaligTagCBox.getSelectedIndex();
                
                readInt = getInt(WiederhStdField);
                if (readInt >= 0)
                    h = readInt;
                readInt = getInt(WiederhMinField);
                if (readInt >= 0)
                    min = readInt;
                readInt = getInt(WiederhTaktField);
                if (readInt >= 0)
                    takt = readInt;
                readInt = getInt(WiederhAnzahlField);
                if (readInt >= 0)
                    anzahl = readInt;
                
                if ((h >= 0) && (min >= 0) && (takt >= 1) && (anzahl >= 1) && (linie != null) && (tag >= 0)) {
                    for (int i = 0; i < anzahl; i++) {
                        int zeit = (h * 100) + min;
                        tempPlan.addStop(zeit, tag, linie, (Haltestelle) Haltestellen.getSelectedItem());
                        min = min + takt;
                        if (min > 59) {
                            h++;
                            if (h > 23)
                                h = h % 24;
                            min = min % 60;
                        }
                        progress = (( i * 100 ) / anzahl);
                        System.out.println("Zeit: " + zeit + " added.");
//                ProgressBar.setValue(progress);
//                ProgressBar.paint(ProgressBar.getGraphics());
                    }
                }
                runs = false;
                return new Boolean(true);
            }
            
            public void finished() {
                updatePlan();
                ProgressBar.setVisible(false);
            }
            
        };
        setInput(false);
        refreshTimer = new Timer(50, new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ProgressBar.setValue(Adder.getProgress());
                System.out.println("refresh");
                if (!Adder.isRunning()) {
                    refreshTimer.stop();
                }
            }
        });
        refreshTimer.start();
        Adder.start();
        boolean added = ((Boolean) Adder.get()).booleanValue();
        setInput(true);
        Adder.finished();
    }

In diesem Beispiel ist nun ein Timer benutzt worden. Ich hab es auch schon mit SwingUtilities.invokeLater() probiert. Funktioniert leider auch nicht. Anhand des System.out.println ist das Problem klar. Die ProgressBar wird immer erst hinterher aktualisiert. Bei invokeLater() hab ich sogar eine Out of Memory: Heap Space Exception, soviel invokeLaters() setzt er auf die Heap, arbeitet aber keines ab. auch nicht, wenn ich mit sleep() den SwingWorker schalfen lege, oder einen Monitor und wait() bzw. notifyAll() benutze.
Das einzige was geht ist, wenn ich im SwingWorker selber ProgressBar.paint() aufrufe. Aber dann flackert die ProgressBar unglaublich.
Kann ich eine paint-Methode erzwingen, die die ProgressBar nicht vorher mit dem weißen Hintergrund füllt? So könnte man das Flackern loswerden, dann wäre ich ja schon glücklich.

Also wenn einer eine Idee hat, bin ich über jeden Vorschlag sehr erfreut. Sonst mach ich schon mal mit flackernder Anzeige weiter. :(

Vielen Dank...
 
Hallo,

die Berechnung solltest du in einen eigenen Thread packen, und dann in diesem die Änderung an der Progressbar in einem SwingUtilities.invokeLater() vornehmen.

Java:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigInteger;

import javax.swing.*;

public class ProgressDemo extends JFrame implements ActionListener {

	private static final int max = 555;

	private int status = 0;

	private JButton btn = new JButton("Calculate all factorials till " + max);

	private JPreviewDialog previewDialog = new JPreviewDialog(this);

	private JProgressDialog progDialog = new JProgressDialog(this, max);

	public ProgressDemo() {
		super("Wait Problem");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLocationByPlatform(true);
		this.setAlwaysOnTop(true);

		btn.addActionListener(this);

		this.add(btn);

		this.pack();
		this.setVisible(true);
	}

	public static void main(String[] args) {
		new ProgressDemo();
	}

	public void actionPerformed(ActionEvent e) {

		new Thread() {
			public void run() {
				progDialog.showDialog();

				for (long i = 0; i <= max; i++) {
					previewDialog.updateTextArea("factorial of " + i + ": "
							+ ProgressDemo.this.getFactorial(i).toString()
							+ "\n");
					status = (int) i;
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							progDialog.setProgStatus(status);
						}
					});
				}
				progDialog.disposeDialog();
				previewDialog.showDialog();
			}
		}.start();
	}

	public BigInteger getFactorial(long n) {
		BigInteger bi = new BigInteger("1");
		if (n == 0)
			return bi;
		for (long i = 1; i <= n; i++) {
			bi = bi.multiply(new BigInteger("" + i));
		}
		return bi;
	}

	class JPreviewDialog extends JDialog {

		private JTextArea tArea = new JTextArea();

		public JPreviewDialog(JFrame owner) {
			super(owner, "MyDialog", true);
			this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			this.setLocationByPlatform(true);
			this.setSize(480, 640);

			tArea.setEditable(false);

			this.add(new JScrollPane(tArea), BorderLayout.CENTER);
			this.add(btn, BorderLayout.SOUTH);
		}

		public void showDialog() {
			this.setVisible(true);
		}

		public void updateTextArea(String s) {
			tArea.append(s);
		}
	}

	public class JProgressDialog extends JDialog {
		private int max = 20;

		private JProgressBar bar;

		public JProgressDialog(JFrame owner, int max) {
			super(owner, "Druckvorbereitung", false);
			this.max = max;

			bar = new JProgressBar(0, max);

			this.setAlwaysOnTop(true);
			this.setLocationByPlatform(true);
			this.setSize(300, 50);
			this.add(bar);
		}

		public void showDialog() {
			bar.setValue(0);
			this.setVisible(true);
		}

		public void disposeDialog() {
			bar.setIndeterminate(false);
			this.dispose();
		}

		public void setProgStatus(int stat) {
			if (stat < max)
				bar.setValue(stat);
			else if (stat == max)
				bar.setIndeterminate(true);
		}

		public int getProgStatus() {
			return bar.getValue();
		}
	}
}


Vg Erdal
 
Hab da was für dich aus meinem Reportoire. Sollte für dich klappen.

Aufrufen tust dus:
//Wobei frame dein JFrame ist
Thread ladebildschirmthread= new Thread(new BildschirmRunnable(frame));
ladebildschirmthread.start();

Bei einer Progressbar muss man immer MultiThreaded arbeiten.

Unten der Code:
Code:
import javax.swing.JFrame;

public class BildschirmRunnable implements Runnable {
	
	private Ladebildschirm ladefenster;
	
	public BildschirmRunnable(JFrame frame){
		ladefenster = new Ladebildschirm(frame);
	}
	
	public void run() {
		 ladefenster.setVisible(true);
		 ladefenster.setFortschrittMaximum(1000);
		 try {
			 for(int i=3;i>0;i--){
		    		ladefenster.setBeschriftung("Gestartet in " + i + " Sekunden");
		    		Thread.sleep(1000);
		    	}
			} catch (InterruptedException e) {
				System.out.println("Fehler im Thread:" + e.getMessage());
			}
	     for (int i = 0; i <= 1000; i++) {
	    	 ladefenster.setFortschritt(i);
	       try {
	        if(i!=500){
	        	ladefenster.setBeschriftung(i + " von 1000");
	        	System.out.println(i + " von 1000");
	        }else{
	        	System.err.println("Hole Vorgang vom Host");
	        	ladefenster.setBeschriftung("Hole Vorgang vom Host");
	        	ladefenster.schalteProzentStatus(true);
	        	Thread.sleep(3000);
	        	ladefenster.schalteProzentStatus(false);
	        }
	       } catch (InterruptedException e) {
				System.out.println("Fehler im Thread:" + e.getMessage());
	       }
	    }
	    try {
	    	for(int i=3;i>0;i--){
	    		ladefenster.setBeschriftung("Erfolgreich beendet in " + i + " Sekunden");
	    		Thread.sleep(1000);
	    	}
			ladefenster.dispose();
		} catch (InterruptedException e) {
			System.out.println("Fehler im Thread:" + e.getMessage());
		}
	}

}
Code:
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.EtchedBorder;

public class Ladebildschirm extends JDialog {

	private static final long serialVersionUID = 1L;
	
	private JProgressBar fortschrittsanzeige;
	private JLabel beschriftung;
	
	public Ladebildschirm(JFrame frame) {
		super(frame,false);
		setBounds(50,50,300,40);
		setResizable(false);
		setUndecorated(true);
		setLocationRelativeTo(frame);
		fortschrittsanzeige = new JProgressBar();
		fortschrittsanzeige.setBorderPainted(false);
		fortschrittsanzeige.setVisible(true);
		fortschrittsanzeige.setStringPainted(true);
		JPanel panel = new JPanel(new BorderLayout());
		beschriftung = new JLabel("Start");
		panel.add(beschriftung,BorderLayout.PAGE_START);
		panel.add(fortschrittsanzeige,BorderLayout.PAGE_END);
		panel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		add(panel);
	}

	public void setFortschrittMaximum(int max){
		fortschrittsanzeige.setMaximum(max);
	}
	
	public void setFortschritt(int prozent){
		fortschrittsanzeige.setValue(prozent);
	}
	
	public void setBeschriftung(String text){
		beschriftung.setText(text);
	}
	
	public void schalteProzentStatus(boolean bool){
		fortschrittsanzeige.setIndeterminate(bool);
	}
	
}
 
Also vielen Dank für die Antworten.
Die Variante mit invokeLater hat ja leider auch keine Verbesserung gebracht. Die Anzeige in einen eigenen Frame zu packen, hab ich auch probiert, diesen auch mal Runnable implementieren lassen und als Thread aufgerufen.... Alles keine Verbesserung.
Mittlerwiele bin ich in der Programmierung weiter vorangeschritten und meine Datentypen sind fertig. Dies hat einen so enormen Geschwindigkeits-Schub gebracht, dass ich die ProgressBar eigentlich gar nicht mehr brauch. Sie ist zwar noch da, aber ich kann nicht mehr erkennen, ob die mittläuft, oder erst hinterher aktualisiert wird.
Evtl. war die erste Implementierung so prozessorlastig, dass die ProgressBar deshab nicht mehr funktioniert hat.

Aber nochmals vielen Dank.
 
Hallo!

Schau mal hier:
Java:
/**
 * 
 */
package de.tutorials;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Executors;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

/**
 * @author Tom
 * 
 */
public class ProgressBarExample extends JFrame implements ActionListener {

    JProgressBar progressBar;

    JButton actionA;

    JButton actionB;

    JButton actionC;

    IProgressIndicator progressIndicator;

    boolean workInProgress;

    public ProgressBarExample() {
        super("ProgressBarExample");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        progressBar = new JProgressBar();

        add(progressBar, BorderLayout.CENTER);

        JPanel panel = new JPanel(new GridLayout(3, 1));
        actionA = new JButton("1 ... 100");
        actionA.addActionListener(this);
        panel.add(actionA);
        actionB = new JButton("1 ... 1000");
        actionB.addActionListener(this);
        panel.add(actionB);
        actionC = new JButton("1 ... 5000");
        actionC.addActionListener(this);
        panel.add(actionC);
        add(panel, BorderLayout.SOUTH);

        progressIndicator = new IProgressIndicator() {

            public void updateProgress(int value) {
                progressBar.setValue(value);
            }

            public void init(int start, int end) {
                workInProgress = true;
                progressBar.setMinimum(start);
                progressBar.setMaximum(end);
            }

            public void finish() {
                workInProgress = false;
            }
        };

        pack();
        setVisible(true);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        new ProgressBarExample();
    }

    public void actionPerformed(ActionEvent e) {
        if (!workInProgress) {
            if (e.getSource() == actionA) {
                Executors.newSingleThreadExecutor().execute(
                        new Task(0, 100, progressIndicator));
            } else if (e.getSource() == actionB) {
                Executors.newSingleThreadExecutor().execute(
                        new Task(0, 1000, progressIndicator));
            } else if (e.getSource() == actionC) {
                Executors.newSingleThreadExecutor().execute(
                        new Task(0, 5000, progressIndicator));
            }
        }
    }

    static class Task implements Runnable {

        int start;

        int end;

        IProgressIndicator progressIndicator;

        public Task(int start, int end, IProgressIndicator progressIndicator) {
            this.start = start;
            this.end = end;
            this.progressIndicator = progressIndicator;
        }

        public void run() {
            progressIndicator.init(start, end);
            for (int i = start; i <= end; i++) {
                this.progressIndicator.updateProgress(i);
                try {
                    Thread.currentThread().sleep(10L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            progressIndicator.finish();
        }
    }

    static interface IProgressIndicator {
        void init(int start, int end);

        void updateProgress(int value);
        
        void finish();
    }
}

Gruß Tom
 
Zurück