Archief - [JAVA] Progressbar / monitor

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

MilM

Legacy Member
Ik zit wat vast bij het displayen van een progressbar/monitor.
De voorbeelden op internet maken veel gebruik van een timer en worden vaak opgeropen vanuit de statische main methode. Als ik deze laat runnen, werken deze voorbeelden perfect.

Wanneer ik dit echter kopieer binnen een echte berekeningsmethode, loopt het fout.

Situatie:
-) JFrame met menu
-) In menu een Action die files inleest en daarna een Dialog toont met informatie over deze files
Bedoeling:
-) Om tijdens het inlezen een progressbar in een dialog te tonen (of een progressmonitor)

Hieronder de code:

Code:
public something getSupportedFiles(File f) {

                /* copy paste code uit een voorbeeld */

		final ProgressBarExample it = new ProgressBarExample();

		JFrame frame = new JFrame("Progress Bar Example");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setContentPane(it);
		frame.pack();
		frame.setVisible(true);

                

		ArrayList<File> files = getAllFiles(f);
		
		for (int i = 0; i < files.size(); i++) {
			try {
				//Berekeningen
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("ERROR WITH FILE: "
						+ files.get(i).getName());
			}
			final int percent = i;
			try {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						it.updateBar(percent);
					}
				});
			} catch (Exception e) {
				
			}
		}
		return something;
	}

Code:
public class ProgressBarExample extends JPanel {

  JProgressBar pbar;
  static final int MY_MINIMUM=0;
  static final int MY_MAXIMUM=100;

  public ProgressBarExample() {
     pbar = new JProgressBar();
     pbar.setMinimum(MY_MINIMUM);
     pbar.setMaximum(MY_MAXIMUM);
     add(pbar);
  }

  public void updateBar(int newValue) {
    pbar.setValue(newValue);
  }
}

Ik weet dat het iets te maken heeft met treads (de main thread en de verwerkingsthread), maar het lukt mij niet om het op te lossen.
Ik heb al geprobeerd om het hele gedeelte in een inner class (die runnable implementeert) te steken. Ik heb ook al geprobeerd om enkel het frame in een inner class te steken, maar dan heb ik problemen met die final declaration.
Maar ik ben er maar op los aant gokken zonder echt te weten wat ik doe.

tha_rippa1be

Legacy Member
Dit legt alles goed uit: http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html

Voor een progressbar met een laadbalk (die van 0-100% loopt) moet ge dit eens bestuderen:
http://java.sun.com/docs/books/tuto...moProject/src/components/ProgressBarDemo.java

Het andere (Inditerminate mode) is een laadbalk die gewoon een animatie geeft die aantoont dat hij aant werken is, maar hij weet niet hoe ver hij zit met de verwerking.


Bij het invoeren van u percentage doet ge
Code:
final int percent = i;
maar stelt dat ge 150 files hebt en ge zit aan u 103de file, dan gaat hij het percentage van de progressbar op 103% zetten?
Code:
final int percent = (int)((double)i/files.size()*100);

*edit*
Ik zal een werkend voorbeeldje schijven.

*edit2*
Voorbeeld:
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.util.*;
import java.io.*;

public class Test extends JFrame{
	private JMenuBar mb;
	private JProgressBar pb;
	
    public Test(){
    	makeComponents();
    	showFrame();
    }
    
    class Task extends SwingWorker<Void, Void>{
    	//Als 'Task' aan gegevens moet kunnen moet ge ze meegeven wanneer ge een instantie van Task maakt
    	//In de constructor dus
    	private ArrayList<File> files;
    	public Task(ArrayList<File> files){
    		this.files = files;
    		//nu kunt ge aan files in doInBackground()
    	}
    	
        @Override
        public Void doInBackground() {
        	/* hetgeen gij hier wilt hebben:
        	for (int i = 0; i < files.size(); i++) {
    			try {
    				//Berekeningen
    			} catch (Exception e) {
    				e.printStackTrace();
    				System.out.println("ERROR WITH FILE: "
    						+ files.get(i).getName());
    			}
    			int percent = (int)((double)i/files.size()*100);
    			setProgress(percent);
    		}
    		*/
            
        	for(int i=0;i<files.size();i++){
        		try{
        			Thread.sleep(1000);
        		}catch(Exception e){
        			e.printStackTrace();
        		}
        		
        		int percent = (int)((double)i/files.size()*100);
        		pb.setValue(percent);
        	}
        	
            return null;
        }
        @Override
        public void done() {
            //dit gebeurt nadat doInBackground() klaar is met alles uit te voeren
            setCursor(null); //turn off the wait cursor
            pb.setValue(100);
        }
    }
    
    private void makeComponents(){
    	mb = new JMenuBar();
    	add(mb, BorderLayout.NORTH);
    	JMenuItem miAction = new JMenuItem("actie");
    	miAction.addActionListener(new ActionListener(){
    		public void actionPerformed(ActionEvent e){
    			//start testdata
    			ArrayList<File> f = new ArrayList<File>();
    			f.add(new File(""));
    			f.add(new File(""));
    			f.add(new File(""));
    			f.add(new File(""));
    			f.add(new File(""));
    			//stop testdata
    			
    			setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    			Task task = new Task(f);
    	        task.execute();
    		}
    	});
    	mb.add(miAction);
    	
    	pb = new JProgressBar();
    	add(pb,BorderLayout.SOUTH);
    }

    private void showFrame(){
    	pack();
    	setDefaultCloseOperation(EXIT_ON_CLOSE);
    	setVisible(true);
    	setMinimumSize(new Dimension(500,300));
    }
    
    public static void main(String [] args){
    	new Test();
    }
}

MilM

Legacy Member
Bedankt voor uw code!
Het werkt nu inderdaad, zowel met het frame in uw voorbeeld als met het ProgressBarFrame uit mijn voorbeeld.

Het grote nadeel is nu wel dat ik volledig mijn code moet omsmijten.

Ik heb in verschillende klasses methodes staan die bepaalde 'langere' methodes hebben (inlezen bestanden + bepaalde search algoritmes om bv te zoeken naar twee 'bijna' identieke strings).
Tot nu toe staan die allemaal mooi gegroepeerd in klasses en is de opbouw redelijk logisch.

Wanneer ik nu een progressbar wil voor deze specieke methodes, moet ik dus heel die opbouw overboord gooien?

Ik moet dan namelijk:
-) Een SwingWorker aanmaken voor elk van deze methodes
-) Hun implementatie cut/pasten in de background body
-) In de orginele methode dan een test klasse aanmaken (of een task)
-) Aangezien de methodes gebruik maakten van andere methodes in de klassen, moet er nu binnen de SwingWorker instanties van deze klassen aangemaakt worden (of statische methodes) ofwel indien mogelijk de orginele methode deze laten oproepen en dan als argumenten doorgeven aan de Test klasse
-) ...

Als ik het goed begrijp is het dus niet mogelijk om gewoon een "stuk code" in de orginele methodes te steken voor een progressbar?
En is dat dan omdat een gewone methode in de hoofddraad loopt en zo elke grafische verandering blokkeert?

Nu, zoiezo bedankt.
Als het niet anders kan, los ik het zo op :)

tha_rippa1be

Legacy Member
MilM zei:
Bedankt voor uw code!
Het werkt nu inderdaad, zowel met het frame in uw voorbeeld als met het ProgressBarFrame uit mijn voorbeeld.

Het grote nadeel is nu wel dat ik volledig mijn code moet omsmijten.

Ik heb in verschillende klasses methodes staan die bepaalde 'langere' methodes hebben (inlezen bestanden + bepaalde search algoritmes om bv te zoeken naar twee 'bijna' identieke strings).
Tot nu toe staan die allemaal mooi gegroepeerd in klasses en is de opbouw redelijk logisch.

Wanneer ik nu een progressbar wil voor deze specieke methodes, moet ik dus heel die opbouw overboord gooien?

Ik moet dan namelijk:
-) Een SwingWorker aanmaken voor elk van deze methodes
-) Hun implementatie cut/pasten in de background body
-) In de orginele methode dan een test klasse aanmaken (of een task)
-) Aangezien de methodes gebruik maakten van andere methodes in de klassen, moet er nu binnen de SwingWorker instanties van deze klassen aangemaakt worden (of statische methodes) ofwel indien mogelijk de orginele methode deze laten oproepen en dan als argumenten doorgeven aan de Test klasse
-) ...

Als ik het goed begrijp is het dus niet mogelijk om gewoon een "stuk code" in de orginele methodes te steken voor een progressbar?
En is dat dan omdat een gewone methode in de hoofddraad loopt en zo elke grafische verandering blokkeert?

Nu, zoiezo bedankt.
Als het niet anders kan, los ik het zo op :)

Ik ben zelf nog maar 6 maanden bezig met java, en dit is iets dat ik zelf nodig had en dan maar heb opgezocht.
Ik +- iets in mn hoofd dat een oplossing is voor u, maar ik kan er ook volledig naast zitten :P

Van die task klasse maakt ge een aparte klasse (geen inner class)
In de constructor geeft ge een klasse mee (een van die klasses die iets doen) en daar maakt ge een attribuut van in Task

In die klassen die iets doen maakt ge 2 methodes
verwerkEenStap()
//die doet 1 bewerking (1 x string vergelijking of 1x bestand inlezen)
percentage()
//die geeft het percentage aan hoeveel van de hoeveel bestanden ge al hebt ingelezen of strings vergeleken

Dan in de Task klasse maakt ge gebruik van die 2 methodes
Code:
while(attribuutKlasse.percentage()!=100){
verwerkEenStap();
attribuutKlasse.setProgressBarValue(attribuutKlasse.percentage());
}

Ik ben zeker dat er betere oplossingen zullen zijn, maar dat is hoe ik het zou oplossen met het gene dat ik nu ken van java.

MilM

Legacy Member
Ik heb het wat anders opgelost.
Zonder de progressbar was er een methode die een andere methode opriep (de berekeningsmethode) en het resultaat daarvan doorgaf aan een Frame.

Nu maakt die methode een klasse (swingworker) aan, execute deze en vraagt daarna het resultaat op aan deze klasse.
In de background body van de swingworker wordt dan één methode opgeropen en er wordt een ProgressMonitor meegegeven.

Nu heb ik wel nog een probleem (bij alle implementaties trouwens):
-) hij wil niet terugschieten naar mijn eerste draad
Bijv:
ReadSupportedFiles rf = new ReadSupportedFiles(frame,directory);
rf.execute();
System.out.println("NAAR VOLGENDE STAP");

Hij print de "NAAR VOLGENDE STAP" niet uit.

EDIT: hij schiet er blijkbaar wel naar terug, maar direct, zonder te wachten op het resultaat.
Blijkbaar wordt er niet gewacht tot de monitor gesloten is. (zoals bij een dialog met setVisible(false))

EDIT2: Ik heb het simpel opgelost door 'new SomeFrame(rf.getResult());' te verplaatsen naar de 'done' methode van de SwingWorker. (Dunno of dit een propere oplossing is of niet)

Code:
private void readSomething(File directory) {
                //Zie volgend stuk code voor onderstaande klasse
		ReadSupportedFiles rf = new ReadSupportedFiles(frame,directory);
		rf.execute();
		System.out.println("NAAR VOLGENDE STAP");
		new SomeFrame(rf.getResult());
	}

Code:
public class ReadSupportedFiles  extends SwingWorker<Void, Void> {
	private ArrayList<SomeFile> result;
	private File f;
	private ProgressMonitor pbar;

	public ReadSupportedFiles(JFrame frame, File f) {
		this.f =f;
		pbar = new ProgressMonitor(frame, "Monitoring Progress",
		           "Initializing . . .", 0, 100);
	}

	@Override
	public Void doInBackground() {
		DirectoryListing listing = new DirectoryListing();
                //Zie volgend stuk code voor onderstaande methode
		result = listing.getSupportedFiles(f, pbar);
		return null;
	}

	@Override
	public void done() {
        pbar.close();
	}

	public ArrayList<SomeFile> getResult() {
		return result;
	}
}

Code:
public ArrayList<AudioFile> getSupportedFiles(File f, ProgressMonitor p) {
		final ProgressMonitor pbar = p;
		ArrayList<File> files = getAllFiles(f);
		ArrayList<SomeFile> afiles = new ArrayList<SomeFile>();

		for (int i = 0; i < files.size(); i++) {
			try {
				int perc = (int) ((double) i / files.size() * 100);
				if (pbar.isCanceled()) {
			        pbar.close();
			      }
			    pbar.setProgress(perc);
			    pbar.setNote("Operation is "+perc+"% complete");
				
				//INLEZEN BESTANDEN HIER
			} catch (Exception e) {
				e.printStackTrace();
				System.out
						.println("ERROR WITH FILE: " + files.get(i).getName());
			}
			
		}
		return afiles;
	}

tha_rippa1be

Legacy Member
EDIT2: Ik heb het simpel opgelost door 'new SomeFrame(rf.getResult());' te verplaatsen naar de 'done' methode van de SwingWorker. (Dunno of dit een propere oplossing is of niet)
Da's juist ze, hetgeen dat na de berekeningen moet gebeuren moet ge in done zetten.

de execute() zegt gewoon tegen de swingworker dat hij moet beginnen met werken. Da's dus een kort commando, hij wacht niet tot de swingworker alles af heeft, maar gaat gewoon verder met de rest dat eronder staat.

MilM

Legacy Member
Mja, dat klopt dus toch niet om het in die done() te zetten.

Het inlezen van gegevens wordt opgeroepen vanuit verschillende methodes/acties.
De afhandeling van die gegevens (het resultaat) is dus altijd verschillend.
Met het huidige gedrag zou ik dus voor elke afhandeling een nieuwe klasse moeten schrijven (wegens andere done() methode).
Het is namelijk niet zo dat de het altijd 'new SomeFrame(rf.getResult());' is die moet worden uitgevoerd na het inlezen van bestanden.

Ik zou eerder iets moeten hebben in de trend van een dialog (zoals eenJFileChooser).
De berekening in de gewone methode wacht tot de monitor afgesloten is en daarna kun je dan het resultaat van de berekening opvragen en doorgaan.

Iemand een idee?

Als ik rf.get() doe blokkeert mijn progressbar opnieuw :-/
("Note: calling get on the Event Dispatch Thread blocks all events, including repaints, from being processed until this SwingWorker is complete.")

EDIT:
ik ben momenteel aant zien of de volgende projecten oplossingen kunnen vormen voor het probleem:
http://foxtrot.sourceforge.net/docs/license.php
http://spin.sourceforge.net/problem.html

MilM

Legacy Member
Ok, via de foxtrot API is mij dat gedrag dus gelukt.
Het voordeel van Foxtrox is dat het het gedrag nabootst van een modaal venster (bv JDialog)

De klasse ReadSupportedFiles (de SwingWorker klasse) is nu verdwenen.

Ik heb opnieuw enkel mijn oorspronkelijke methode (zoals dat het geval was voor het monitoren van progress) en heb daar dus 'enkel' wat code moeten toevoegen om de progressbar werken te krijgen.
de readSomething() methode roept dus nu direct die methode op

Voor de geïntresseerden, hier de code:

Code:
public ArrayList<SomeFile> getSupportedFiles(JFrame frame, File f) {
	final ArrayList<File> files = getAllFiles(f);
	ArrayList<SomeFile> result = new ArrayList<SomeFile>();
	final ProgressMonitor pbar = new ProgressMonitor(frame, "Loading Music Files",
		           "Initializing . . .", 0, 100);;
	try
        {
	   result = (ArrayList<SomeFile>)Worker.post(new Task()
           {
              public Object run() throws Exception
              {
            	  ArrayList<SomeFile> somefiles = new ArrayList<SomeFile>();
            	  for (int i = 0; i < files.size(); i++) {
          		try {
          			int perc = (int) ((double) i / files.size() * 100);
          			if (pbar.isCanceled()) {
          			    break;
          		        }
          			pbar.setProgress(perc);
          			pbar.setNote("Operation is "+perc+"% complete");
          				
          			if (files.get(i).getName().endsWith(".mp3")) {
          			        SomeFile af = SomeFileIO.read(files.get(i));
          				somefiles.add(af);
          			}
          		} catch (Exception e) {
          			e.printStackTrace();
          			LogFile.AddLine("ERROR WITH FILE: " + files.get(i).getName());
          		}
          			
          	}
                 return somefiles;
              }
           });
        }
        catch (Exception e){
        	e.printStackTrace();
        }
	pbar.close();
	return result;
}
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan