[Java] Voortgang weergeven

Pagina: 1
Acties:

  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Ik ben voor mijn werk een applicatie aan het maken die in een map een aantal XML bestanden doorzoekt en aanpast. Dit werkt inmiddels naar behoren maar er is een ding waar ik niet uitkom. Mijn bedoeling is om in de GUI aan te geven wat de stand van zaken is. Wat ik nu heb gedaan is om nadat een bestand is verwerkt een regeltje in een textarea te zetten. Dit heb ik als volgt geimplementeerd:
code:
1
2
3
4
for(int tel=0; tel<fileList.length; tel++){
  fileProcessClass.processFile(fileList[tel]);
  gui.outputArea.setText(gui.outputArea.getText()+"\n"+fileList[tel]+" is processed");
}

Dit leek mij een mooie simpele oplossing maar het bereikt niet het beoogde effect. Als ik nu op mijn de knop druk waarmee ik alles start dan blijft deze een hele tijd op ingedrukt staan en als hij klaar is komt in een keer alles in de textarea (dus niet regel voor regel). Wat moet ik doen om ervoor te zorgen dat de GUI een goede weergave geeft van wat er gebeurt. Dit hoeft niet perse via een testArea, een leuke progress bar is ook niet verkeerd ;)

Ik gebruik verder Java 1.4 en hoef niet backwards compatible te zijn!

[ Voor 5% gewijzigd door Deddiekoel op 29-08-2004 12:07 ]

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Johnny
  • Registratie: December 2001
  • Laatst online: 22-05 10:01

Johnny

ondergewaardeerde internetguru

Tijdens het openen van bestanden wordt de thread gelockt, en kan er dus niets anders worden gedaan. Je zult een tweede thread moeten maken die via een syncronized (of was het iets anders?) methode de status uitleest en vervolgens weergeeft.

Aan de inhoud van de bovenstaande tekst kunnen geen rechten worden ontleend, tenzij dit expliciet in dit bericht is verwoord.


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Je voert deze operatie uit op de zogenaamde 'event dispatch thread'. Deze thread wordt gebruikt om alle aanpassingen aan de GUI te verwerken. Als er iets gedaan moet worden aan de GUI, wordt er een 'Runnable' op deze event dispatch thread gezet via de methode SwingUtilities.invokeLater. Deze event dispatch thread werkt deze runnables 1 voor 1 af. Als jij deze event dispatch thread bezig houdt, in jouw geval met het inlezen van een file, worden deze Runnables niet afgewerkt. Daardoor komen de aanpassingen aan de GUI dus pas door nadat je klaar bent met het lezen van file.

Grote operaties moet je dus nooit op de event dispatch thread uitvoeren. In plaats daarvan moet je een eigen thread starten. Hierdoor kan de event dispatch thread doorgaan met z'n werk.

Voor meer info: How to use threads in de Java Tutorial over Swing.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Johnny schreef op 29 augustus 2004 @ 12:10:
Tijdens het openen van bestanden wordt de thread gelockt
?

Swing heeft 1 hoofd thread waarop alle events worden verwerkt, namelijk de event dispatching thread. Als er op een knop gedrukt wordt, dan gaat deze thread de actionlisteners aanroepen en zodoende kom je dan uiteindelijk uit bij de uit te voeren logica. Als deze logica vrij lang duurt, dan heeft de event dispatching thread geen tijd om andere events te verwerken en de GUI lijkt dan bevroren.

Je verhaal heeft dus wel een kern van waarheid, maar je omschrijving is slecht.
, en kan er dus niets anders worden gedaan. Je zult een tweede thread moeten maken
Dat is idd de oplossing om ervoor te zorgen dat de gui niet meer bevroren raakt.
die via een syncronized (of was het iets anders?)
synchronized
methode de status uitleest en vervolgens weergeeft.
?

Wat je het beste kunt doen is op het moment dat er een nieuw iets gebeurt (bv nieuwe file begonnen met inlezen), dat je een event verstuurd naar een object die er iets mee kan (kan ook via een callback). Het veiligste is dat je dit doet op de event dispatching thread met invoke methodes (zie SwingUtilities). Dan wordt dat event verder verwerkt op de event dispatching thread en kan de worker thread verder gaan met inladen zonder zich verder nog druk te maken over de afhandeling van dat event.

Ik hoop niet dat jij (johnny) professioneel met Java en threads bezig bent, want uit je verhaal maak ik op dat je er niet veel van begrijpt.

  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Ok, ik moet dus een tweede thread aanmaken voor mijn logica. Ik ben een beetje aan het rond kijken geweest en ben erachter dat ik voor een Thread een klasse moet maken die Thread klasse extend. De logica die ik wil laten uitvoeren moet ik dan in de run() methode van deze klasse onderbrengen, toch?

Momenteel heb ik een main-class die een controller-klasse aanroept. Deze controller klasse opent op zijn beurt weer de GUI. Deze GUI op zijn beurt opent in de controller klasse de functie die logica start. Als ik een Thread klasse maak kan ik die dan ook die klasse start of moet ik die methode in zijn geheel naar de Thread klasse zetten?

En als laatste vraag, hoe laat ik de Thread klasse met de GUI communiceren!

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Deddiekoel schreef op 29 augustus 2004 @ 12:47:
Ok, ik moet dus een tweede thread aanmaken voor mijn logica. Ik ben een beetje aan het rond kijken geweest en ben erachter dat ik voor een Thread een klasse moet maken die Thread klasse extend. De logica die ik wil laten uitvoeren moet ik dan in de run() methode van deze klasse onderbrengen, toch?
Bijna. Threads extenden is eigelijk not done (waarom weet ik eigelijk niet eens). Je moet werken via een Runnable.

code:
1
2
3
4
5
6
7
8
9
class BlaWorker implements Runnable{
    void run(){
        ... jouw logica
    }
}


Thread t = new Thread(new BlaWorker());
t.start();
Momenteel heb ik een main-class die een controller-klasse aanroept. Deze controller klasse opent op zijn beurt weer de GUI. Deze GUI op zijn beurt opent in de controller klasse de functie die logica start. Als ik een Thread klasse maak kan ik die dan ook die klasse start of moet ik die methode in zijn geheel naar de Thread klasse zetten?
Voor dit soort dingen maak ik meestal gebruik van innerclasses (dus een class in je class die overal bij kan).

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Deddiekoel: Ok, ik moet dus een tweede thread aanmaken voor mijn logica. Ik ben een beetje aan het rond kijken geweest en ben erachter dat ik voor een Thread een klasse moet maken die Thread klasse extend. De logica die ik wil laten uitvoeren moet ik dan in de run() methode van deze klasse onderbrengen, toch?
Dat kan ja. Je kan je ook beperken tot het implementeren van de Runnable interface en daarmee een Thread aanmaken. De Runnable interface heeft slechts 1 methode: run. Het aardig van deze interface is dat niet direct vast zit aan het maken van een nieuwe Thread. Je zou namelijk ook zelf de run methode aan kunnen roepen op de huidige thread, of de Runnable meegeven aan het een systeem zoals de event dispatcher.
Als ik een Thread klasse maak kan ik die dan ook die klasse start of moet ik die methode in zijn geheel naar de Thread klasse zetten?
Je kan het beste de echte taak in een aparte klasse zetten, die dus Runnable implementeert of Thread extend. De thread start je dan op vanuit de code die het event afhandelt wat de hele zaak in werking heeft gezet.
En als laatste vraag, hoe laat ik de Thread klasse met de GUI communiceren!
Je moet uitkijken dat Swing GUIs niet thread safe zijn. Je kan dus niet zomaar vanaf een thread (die niet de event dispatch thread is) methodes van Swing componenten aanroepen. Toevallig is JTextArea.setText echter wel thread-safe, dus die zou je zonder problemen direct kunnen aanroepen. In het algemeen kan je beter een Runnable op de event dispatch thread plaatsen (SwunghUtilities.invokeLater) vanuit de code van je eigen thread. Hierdoor wordt de aanpassing op de event dispatch thread uitgevoerd.

Lees ook die link eens goed door die ik je eerder gaf: hier wordt alles uitgelegd. Je kan overwegen om de SwingWorker te gebruiken.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Ok, ik heb nu een innerclass gemaakt die de Thread voor zijn rekening neemt.

Edit (na lezen laaste reply): Als ik hier nu de gui in aanroep dan moet het dus werken?

[ Voor 33% gewijzigd door Deddiekoel op 29-08-2004 13:17 ]

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Deddiekoel schreef op 29 augustus 2004 @ 13:14:
Ok, ik heb nu een innerclass gemaakt die de Thread voor zijn rekening neemt.
Maar zorg ik er nu voor dat vanuit die thread mijn GUI wordt bijgewerkt?
Java:
1
2
3
4
5
6
7
8
9
10
11
12
GuiUpdater implements Runnable{
    void run(){
           ... doe hier al je gui update acties.
     }
}

//pseudo code
for(....){
    File file = fileIterator.next();
    SwingWorker.invokeLater(new GuiUpdater());
    //doe dikke file bewerking
}

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Deddiekoel: Als ik hier nu de gui in aanroep dan moet het dus werken?
Voor het geval van JTextArea.getText kan je direct de GUI aanroepen vanuit de thread ja. In het algemeen kan dit echter niet en moet je weer een nieuwe Runnable op de event dispatch thread zetten.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Het direct updaten van de TextArea werkt idd als trein! Maar nu heb ik ook een progressBar toegevoegd en wil deze ook graag werkend hebben. Hiervoor moet ik dus die GUIUpdater maken! Maar waar moet ik die implementeren? Kan ik vanuit de ene thread (die in de innerclass) een andere Thread aanspreken? Of moet ik vanuit de Thread in de innerclass weer een thread starten?

Het enige wat ik nog wil bereiken is dat er een setValue waarde naar de progressBar wordt gestuurd...

[ Voor 13% gewijzigd door Deddiekoel op 29-08-2004 13:29 ]

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Nee, je moet (zoals al enkele keren gezegd ;) ) een Runnable op de event dispatch thread zetten. Je moet dus een Runnable implementeren die deze update uitvoert. Die kan in een inner class, anonieme inner class, of wat dan ook: dat maakt allemaal niet uit. De runnable zet je op de event dispatch thread met de invokeLater methode van SwingUtilities.

Inner classes zijn met name handig zodat je direct bij alle velden van de omringende klasse kan, maar je kan de benodigde objecten ook gewoon als argumenten meegeven aan een gewone top level class.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Ok, er moet dus een nieuwe runnable komen die de updates doet. Maar hoe weet ik dan wat de progress is?

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Je hebt een fileList.length en een index in die array. Het lijkt me dus nogal simpel om een voortgang te bepalen. Dat moet je zelf wel verder op kunnen lossen ...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Dat probeer ik idd dus al. Maar als ik index/length in een variabele stop dan komt daar steeds 0 uit...
Maar als de logica thread die variabele vult kan ik met een TimerTask die uitlezen en naar de GUI schoppen? Dat is wat ik werkend moet krijgen!

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
5/10 = 0.5

Als je als minimale en maximale waarden 0 en 10 instelt, schiet dat dus niet zo op. Je moet gewoon de index als value van de progressbar gebruiken en het minimum en maximum instellen op 0 en het aantal files.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Hebbes! De thread schrijft nu de index naar een global variable. Deze laat ik via een TimerTask uitlezen en naar de GUI schoppen. Ik moest gewoon de Timer goed instellen |:(

Thanks a million!

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Hmmm, nu ik alles dus mooi werkend heb loop ik nu tegen iets anders aan. In de eerdere situatie bevroor de GUI. Hierdoor kon je niet meerdere malen de logica laten draaien! Maar nu kan dat dus wel!
Ik wil dit dus voorkomen. Ik dacht er eerst aan om in het process van de Thread op te nemen dat de button, die alles in werking zette, uit te zetten. Maar de JButton.disable() functie heeft niet het gewenste effect.... Enig idee waarmee ik dit wel kan bereiken?

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Deddiekoel schreef op 29 augustus 2004 @ 19:40:
Hmmm, nu ik alles dus mooi werkend heb loop ik nu tegen iets anders aan. In de eerdere situatie bevroor de GUI. Hierdoor kon je niet meerdere malen de logica laten draaien! Maar nu kan dat dus wel!
Modal progress dialog.

(Gewoon een JDialog modal maken en daarin een progres bar prikken). Oja.. het is NIET mogelijk dat je uberhaubt nog maar een kont grafisch kan updaten in andere schermen dan die modal dialog. Hou daar rekening mee.

[ Voor 26% gewijzigd door Alarmnummer op 29-08-2004 19:42 ]


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Ik snap niet wat ik hiermee kan. Mijn progress bar werkt al naar behoren ik wil alleen dat je tijdens het uitvoeren van de logica niet nog een keer op de "start" knop kan klikken...

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Deddiekoel schreef op 29 augustus 2004 @ 19:52:
Ik snap niet wat ik hiermee kan. Mijn progress bar werkt al naar behoren ik wil alleen dat je tijdens het uitvoeren van de logica niet nog een keer op de "start" knop kan klikken...
Als jij een modal Dialog op gaat werpen met een mooie JProgressBar erin, dan is het achterliggende scherm niet meer toegankelijk voor events. (Dus ook niet meer voor drukken op knoppen). Je kan pas weer bij het achterliggende scherm komen, als de dialog gesloten is, en die sluit jij (in je programma) op het moment dat je job klaar is.

[ Voor 15% gewijzigd door Alarmnummer op 29-08-2004 19:59 ]


  • Deddiekoel
  • Registratie: Maart 2000
  • Laatst online: 12-11-2025

Deddiekoel

Gadget nerd

Topicstarter
Dan zal ik dat dan maar eens gaan proberen.

Verlanglijstje: Switch 2, PS5 Pro Most wanted: Switch 2


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ik zou toch nog maar even gaan prutsen met die knop. Waarom wordt hij niet disabled?

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

disable() werkt niet, probeer dan vlak voor je de logic thread opstart:

JButton.setEnabled( false );

oh, en vergeet dan niet de button weer aan te zetten als de logic klaar is. Om alles een beetje universeel te houden kun je het beste in je GUI class een interface implementeren die je speciaal maakt voor het afhandelen van de signalen v/d logic thread.

Mischien iets van:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface ILogicListener {

    public void LogicEnded();

}

public class MyGUI implements ILogisListener {

        // ....
        JButton myButton;

        private class HandleLogicSignals implements Runnable {
                            
            public void run() { 
                //  maybe replace by switch to handle different
                // signals
                
                myButton.setEnabled( true );                             
            }       
    }

        public void logicEnded() {
        SwingUtilities.invokeLater( new HandleLogicSignals () );        
    }  

}


Op deze manier kun je makkelijker bepalen wie de signals van je thread gaat afhandelen. Zeker na refactoren wil dat nog wel eens veranderen. ;)

[ Voor 84% gewijzigd door Verwijderd op 29-08-2004 20:22 ]

Pagina: 1