Archief - [PROG][JAVA] Jframe met progressbar die werkt met aparte thread

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.

yannick

Legacy Member
Om mijn vraag wat te verduidelijken:

Voor een project moeten wij csv inlezen en deze in de database zetten, omdat dit nogal lang kan duren als de database op ne remote server staat zouden wij graag een Jframe maken met een progressbar in die werkt via een Thread (als we dit ni via een thread doen zal de JFrame pas tevoorschijn komen als alles al gdn is).

Dit is wat ik nu heb:

De Thread klasse
Code:
//*
 * CsvCheckDoneThread.java
 *
 * Created on 2 juni 2007, 18:12
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package Data;
import View.CsvProgress;
import java.awt.*;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
/**
 *
 * @author Welles
 */
public class CsvCheckDoneThread implements Runnable {
    CsvProgress progress=new CsvProgress();
    boolean ok=true;
    /** Creates a new instance of CsvCheckDoneThread */
    public CsvCheckDoneThread(){
    }
    
    public void run() {
        progress.setVisible(true);
    }
    public void stopthread(){
        progress.dispose();
        ok=false;
    }
    public void setValue(int value){
        progress.setValue(value);
    }   
}

De Jframe die via de thread wordt opgeroepen
Code:
/*
 * CsvProgress.java
 *
 * Created on 2 juni 2007, 18:35
 */

package View;

import javax.swing.*;
import java.awt.*;

/**
 *
 * @author  Welles
 */
public class CsvProgress extends JFrame {
    
    /** Creates new form CsvProgress */
    public CsvProgress() {
        initComponents();
        //centerscreen();
        this.prgbar.setValue(0);
    }
    private void centerscreen(){
        Dimension dim = getToolkit().getScreenSize();
        Rectangle abounds = getBounds();
        setLocation((dim.width - abounds.width) / 2,(dim.height - abounds.height) / 2);
    }
    public void setValue(int value){
        this.prgbar.setValue(this.prgbar.getValue()+value);
    }
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">                          
    private void initComponents() {
        jPanel1 = new javax.swing.JPanel();
        prgbar = new javax.swing.JProgressBar();
        jLabel1 = new javax.swing.JLabel();
        jPanel2 = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setAlwaysOnTop(true);
        setBackground(new java.awt.Color(214, 205, 244));
        setResizable(false);
        prgbar.setStringPainted(true);

        jLabel1.setFont(new java.awt.Font("Calibri", 0, 14));
        jLabel1.setForeground(new java.awt.Color(0, 0, 153));
        jLabel1.setText("<html>De csv's worden geladen.<br><p align=\"center\">Even geduld aub</p></html>");

        jPanel2.setBackground(new java.awt.Color(67, 67, 201));
        jLabel2.setFont(new java.awt.Font("Calibri", 0, 48));
        jLabel2.setForeground(new java.awt.Color(255, 255, 255));
        jLabel2.setText("<html>D<br>C<br>I</html>");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addGap(29, 29, 29)
                .addComponent(jLabel2)
                .addContainerGap(37, Short.MAX_VALUE))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(23, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(27, 27, 27)
                        .addComponent(prgbar, javax.swing.GroupLayout.PREFERRED_SIZE, 242, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(80, 80, 80)
                        .addComponent(jLabel1)))
                .addGap(34, 34, 34))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                .addContainerGap(65, Short.MAX_VALUE)
                .addComponent(jLabel1)
                .addGap(18, 18, 18)
                .addComponent(prgbar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(77, 77, 77))
            .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        pack();
    }// </editor-fold>                        
    
    /**
     * @param args the command line arguments
     */
    
    // Variables declaration - do not modify                     
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JProgressBar prgbar;
    // End of variables declaration                   
    
}

De functie waarin de Thread gestart wordt
Code:
public void InitializeCsv(String path) throws SQLException, IOException{
        csvcheckclass=new CsvCheckDoneThread();
        Thread cvsth = new Thread(csvcheckclass);
        try{
            cvsth.start();
            Csv c= new Csv(game, wedstrijd, speeldag, speler, team, club, reeks, bc,csvcheckclass);
            c.LeesCsv(path);
            kalender k= new kalender(14,wedstrijd,team, club, bc);
            k.create();  
            csvcheckclass.stopthread();
        } catch (SQLException e){
            csvcheckclass.stopthread();
            throw new SQLException();
        }
    }

Dit blijkt dus niet te werken, want hij laat de JFrame enkel zien als alles gedaan is :(.

Iemand een idee hoe ik dit kan oplossen?
mvg

Yannick Wellens

Da Turtle

Legacy Member
public void run() {
progress.setVisible(true);
}

Een thread wordt beïndigd als de run methode ten einde is. Dus wat hier gebeurt als je de thread start, is dat er een nieuwe thread gestart wordt, deze thread voert setVisible van uw progress JFrame uit in de thread wordt beïndigd.

// edit: Is het echt de bedoeling dat de progress bar de hoeveelheid verkregen data van het totaal toont? Ik zie niet echt een manier hoe je dit kunt meten (en dus de juiste waarde door de progressbar laten tonen).

yannick

Legacy Member
Ja, da meten is gelukt :). Ik zie gwn hoeveel functies em moet uitvoeren, dan doe ik 100/aantal functies, zo weet ik hoeveel em moet updaten na elke functie die gdn is.

Is er dus ook een oplossing voor dit probleem, zou ik op 1 of andere manier ervoor kunnen zorgen dat de thread blijft bestaan tot de kader moet sluiten?
Ik denk omdat Swing sowieso al een eigen thread gebruik, de thread die ik aangeef niet wordt gebruikt om mijn Jframe te tekenen, hierdoor moet ik dus wachten totdat de csv'z zijn ingelezen alvorens het scherm te krijgen.

mvg

yannick

Legacy Member
Weet niemand hier een antwoord op? Want ik zou dit toch nog graag in mijn project krijgen :)

mvg

QplQyer

Legacy Member
Je mag niet zomaar Swing-functies oproepen vanuit andere threads, dus ook niet zomaar rechtstreeks methodes van een frame aanroepen.
Hier is meer info:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
Het keyword "EventDispatcherThread" of "EventDispatchThread" (niet meer zeker van de naam) kan ook al wat helpen bij je zoektocht.

Uw vraag over de "stopthread()" (geen goede code voor thread-safety redenen trouwens): ja daar is een goede manier voor.

Code:
public run () {
   SwingUtilities.invokeLater(new Runnable { run () { frame.setVisible(true); } });
   while (frame.isVisible()) { }
   frame.dispose();
}

Maak dan dat je frame sluitknop enkel je frame invisible maakt en dan stopt de thread automatisch als deze gesloten wordt en smijt de thread je frame weg (desnoods zet je de dispose in de frame-sluitknop, maar ik vrees voor synchronisatieproblemen dan: wat het effect van visible op een disposed frame oproepen is weet ik namelijk niet).
Doe alleszins die "stopthread()" weg, dat is niet de goede manier om een thread te stoppen (een voorwaardelijke whilelus wordt meestal gebruikt).

yannick

Legacy Member
QplQyer zei:
Je mag niet zomaar Swing-functies oproepen vanuit andere threads, dus ook niet zomaar rechtstreeks methodes van een frame aanroepen.
Hier is meer info:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
Het keyword "EventDispatcherThread" of "EventDispatchThread" (niet meer zeker van de naam) kan ook al wat helpen bij je zoektocht.

Uw vraag over de "stopthread()" (geen goede code voor thread-safety redenen trouwens): ja daar is een goede manier voor.

Code:
public run () {
   SwingUtilities.invokeLater(new Runnable { run () { frame.setVisible(true); } });
   while (frame.isVisible()) { }
   frame.dispose();
}

Maak dan dat je frame sluitknop enkel je frame invisible maakt en dan stopt de thread automatisch als deze gesloten wordt en smijt de thread je frame weg (desnoods zet je de dispose in de frame-sluitknop, maar ik vrees voor synchronisatieproblemen dan: wat het effect van visible op een disposed frame oproepen is weet ik namelijk niet).
Doe alleszins die "stopthread()" weg, dat is niet de goede manier om een thread te stoppen (een voorwaardelijke whilelus wordt meestal gebruikt).

Ja sry, die stopthread() ben ik vergeten te veranderen hier. Ik doe het normaal altijd met een while lus.
In stopthread had moeten staan: killme=true; en dan had ik het zo met een while gedaan: while(!killme).

Maar omdat ik nog nooit een thread had gebruikt voor een Jframe op te roepen deed ik het hier anders :)

Kzal allesinds da stukske code is proberen.
Heel hard bedankt voor de moeite :)!!

*EDIT*

Da stukske code help me eig nog altijd niet verder :p. Want hij laat de gui pas zien als alles ingelezen is. Ik moet dus ervoor zorgen dat deze Jframe TIJDENS het inlezen getoond wordt en de progressbar wordt geupdate :)
mvg

QplQyer

Legacy Member
Ja, ik had je code eigenlijk nog niet zo goed bekeken en dus over het hoofd gezien dat je niet alles laadt in die aparte thread, noch de status controleert in die aparte thread.

Je probleem is dat je niet in de thread gewoon een JFrame moet tonen, maar de update-methode voor de progressbar in je thread moet steken (en eventueel de laadcode). Als je immers die JFrame nu in die thread laat tonen (door invokeLater() op te roepen), heb je het probleem van het wachten op het laden niet omzeild omdat de DispatchThread nog altijd blokkeert tijdens dat laden (en pas na het laden die invokeLater() eindelijk zal uitvoeren.

Dus iets zoals dit:

Code:
public void run () {
    while (true) {
       // Check status van het laden en de eventueel de laadcode zelf
       SwingUtilities.invokeLater(new Runnable () { progressbar.setValue(progressbar.getValue()+5))
    }
}
Zoiets zou je dus moeten gebruiken (behoudens de klassenaam-fouten, de +5 enzo natuurlijk. Hoe je de status opvraagt ben ik nu wel niet meer zeker. Ik denk dat je wel get-methoden kunt opvragen vanuit een aparte thread, maar niet het tekenen kunt beïnvloeden, maar dat ben ik dus hoegenaamd niet meer zeker.

Kijk ook eens op de link die ik gaf, die heeft een voorbeeld van hoe je een laadscherm moet aanpakken.

yannick

Legacy Member
QplQyer zei:
Ja, ik had je code eigenlijk nog niet zo goed bekeken en dus over het hoofd gezien dat je niet alles laadt in die aparte thread, noch de status controleert in die aparte thread.

Je probleem is dat je niet in de thread gewoon een JFrame moet tonen, maar de update-methode voor de progressbar in je thread moet steken (en eventueel de laadcode). Als je immers die JFrame nu in die thread laat tonen (door invokeLater() op te roepen), heb je het probleem van het wachten op het laden niet omzeild omdat de DispatchThread nog altijd blokkeert tijdens dat laden (en pas na het laden die invokeLater() eindelijk zal uitvoeren.

Dus iets zoals dit:

Code:
public void run () {
    while (true) {
       // Check status van het laden en de eventueel de laadcode zelf
       SwingUtilities.invokeLater(new Runnable () { progressbar.setValue(progressbar.getValue()+5))
    }
}
Zoiets zou je dus moeten gebruiken (behoudens de klassenaam-fouten, de +5 enzo natuurlijk. Hoe je de status opvraagt ben ik nu wel niet meer zeker. Ik denk dat je wel get-methoden kunt opvragen vanuit een aparte thread, maar niet het tekenen kunt beïnvloeden, maar dat ben ik dus hoegenaamd niet meer zeker.

Kijk ook eens op de link die ik gaf, die heeft een voorbeeld van hoe je een laadscherm moet aanpakken.

Mja ben aant zoeken, maar nog niet echt gevonden (kben ook bezig met java server faces, exaam morge, het java project moet trouwens morge ook af zijn :p. Tis enkel dit stuk dat nog niet werkte, als ik iets in menne kop haal moet ik het der ook in krijgen, eigen aan het beesje :p.

Bedankt voor de hulp allesinds, kheb weer wat bijgeleerd :)

*edit*

pff never mind. Zelfs met al uw hulp krijg ik deze kader ni getoond. Kzalt maar laten zo want ik werk wrs ook al ff op uw zenuwen: "Waarom kan die lompe **** da nu ni vinden :p"
For the record
Code:
public void run() {
        while (ok) {
           // Check status van het laden en de eventueel de laadcode zelf
           SwingUtilities.invokeLater(new Runnable (){
                public void run() {
                    CsvProgress progress=new CsvProgress();
                    progress.setVisible(true);
                }
           });
        }
    }
Dit is de code, het is waarschijnlijk verkeerd, maja bon :).

Deze code ook geprobeerd omdat ik het nogal raar vond da da allemaal in een while lus staat (hij doet er dan altijd ne invoke later bij, denk ik):
Code:
public void run() {
           // Check status van het laden en de eventueel de laadcode zelf
           SwingUtilities.invokeLater(new Runnable (){
                public void run() {
                    CsvProgress progress=new CsvProgress();
                    progress.setVisible(true);
                }
           });
    }

*EDIT2*

ok ik heb wat liggen testen door gwn een a te laten afprinten binnen de invokelater. Wat blijkt, dit doet em ook pas wanneer alles gdn is-> Hij gebruikt gwn niets van thread of invokelater.

Als ik dit probeer in de klasse zelf (dus waar ik normaal de thread aanroep), doet hij ook net hetzelfde.

Heb het ff getest met dit:
Code:
public void InitializeCsv(String path) throws IOException, SQLException{
        /*CsvCheckDoneThread csvdoneth = new CsvCheckDoneThread();
        Thread th = new Thread(csvdoneth);
        th.start();*/
        SwingUtilities.invokeLater(new Runnable (){
             public void run() {
                 System.out.println("a");
             }
        });
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        /*Csv c= new Csv(game, wedstrijd, speeldag, speler, team, club, reeks, bc);
        c.LeesCsv(path);
        kalender k= new kalender(14,wedstrijd,team, club, bc);
        k.create(); 
        //csvdoneth.stopThread();*/
    }

Zo een raar probleem pfff


mvg

QplQyer

Legacy Member
Een vraagje: zit de laadcode eigenlijk in een aparte Thread?

Indien nee, dan zit daar (een deel van) het probleem: Swing zal altijd blokkeren totdat het laden gedaan is. Het JFrame zal dus pas tevoorschijn komen als Swing klaar is om het te tonen en dat is na de blokkering van het laden.

Indien ja: ok goed.

---

Oplossing voor alles:

- Steek de laad-code in een aparte thread.
- Geef aan deze thread een referentie naar een JProgressBar mee
- In de code die het laden start (niet in de thread-code!) maak je eerst een JFrame aan met een progressbar en je zet die op visible.
- Je maakt een nieuw laad-thread object aan en je geeft de referentie naar de JProgressBar op het pas aangemaakte frame mee.
- In de laad-thread update je telkens wanneer je klaar bent met iets in te laden ofzo de JProgressBar zijn waarde door de dispatchLater () op te roepen met een Runnable die op die progressbar gewoon setValue uitvoert met de juiste waarde.

En dan zou alles moeten werken naar behoren. Het JFrame laat je misshcien best pas afsluiten als de gebruiker dat wenst (dan ziet deze dat het laden gedaan is).

Wat me net te binnen schiet is dat een JProgressBar ook wel voorzien kon worden van een timer, maar Swing is wel al twee jaar geleden dus dat kan een foute herinnering zijn :).

Ik hoop dat je er nog wat aan hebt.

yannick

Legacy Member
QplQyer zei:
Een vraagje: zit de laadcode eigenlijk in een aparte Thread?

Indien nee, dan zit daar (een deel van) het probleem: Swing zal altijd blokkeren totdat het laden gedaan is. Het JFrame zal dus pas tevoorschijn komen als Swing klaar is om het te tonen en dat is na de blokkering van het laden.

Indien ja: ok goed.

---

Oplossing voor alles:

- Steek de laad-code in een aparte thread.
- Geef aan deze thread een referentie naar een JProgressBar mee
- In de code die het laden start (niet in de thread-code!) maak je eerst een JFrame aan met een progressbar en je zet die op visible.
- Je maakt een nieuw laad-thread object aan en je geeft de referentie naar de JProgressBar op het pas aangemaakte frame mee.
- In de laad-thread update je telkens wanneer je klaar bent met iets in te laden ofzo de JProgressBar zijn waarde door de dispatchLater () op te roepen met een Runnable die op die progressbar gewoon setValue uitvoert met de juiste waarde.

En dan zou alles moeten werken naar behoren. Het JFrame laat je misshcien best pas afsluiten als de gebruiker dat wenst (dan ziet deze dat het laden gedaan is).

Wat me net te binnen schiet is dat een JProgressBar ook wel voorzien kon worden van een timer, maar Swing is wel al twee jaar geleden dus dat kan een foute herinnering zijn :).

Ik hoop dat je er nog wat aan hebt.

1. Wat als nu mijn Jframe al gemaakt is in netbeans, maw, de klasse bestaat al.
2. Met laad code bedoel je dan: CsvProgress progress = new CsvProgress(); (dit is mijn Jframe)?

Dit is del aats keer dat ik iets vraag, want kvind het zelf al ambetant te vinden da ge er zoveel moeite in moet steken :p.

Het zit dus zo in elkaar:

1. Ik duw op het menuitem "Import csv", dit bevind zich in de HoofdGUi (waar ik begin)

2. HoofdGui gaat naar controller die dan Initialze CSV oproept.

3. Initialize Csv gaat naar klasse Csv en begin daar alles te doen: inlezen en in database zetten.

4. Terwijl dat dit gebeurt zou er een JFrame CsvProgress moeten getoond worden (die al aangemaakt is met GUI builden van netbeans). Deze roep ik op men een Thread CheckCsvDone() waarin ik start() oproep.

Voila, da is het duidelijkste dat ik kan uitleggen :p. Kzal allesinds zelf al proberen uit te zoeken wa ge juist bedoeld :).

Bedankt voor de hulp nog is ! :)

QplQyer

Legacy Member
Met laad-code bedoelde ik de code die de csv inleest.

Het is namelijk zo dat alles momenteel in dezelfde thread loopt. Als je in die thread een intensieve opdracht laat lopen die niet onmiddellijk returnt, zal heel Swing blijven vasthangen tot die opdracht terug is gekeerd. Enkel je progressbar threaden zal dan niet helpen, omdat die progressbar getoond zal worden nadat je "laad de csv en plaats hem in de db"-code klaar is (dan pas kan Swing je aanvraag om hem te tonen verwerken).

Wat je dus moet doen is de code die de csv laadt en hem in de db plaatst threaden.

Dat de JFrame is aangemaakt met Netbeans is geen probleem, je kan nog steeds de sourcecode editen en dan zo een functie voorzien die de progressbar die in dat frame gebruikt wordt opvraagt om alles te doen zoals ik daarnet ongeveer beschreef.

yannick

Legacy Member
Ah just, da heb ik al is moeten doen zo :p.

Bedankt, kzalt wel werkend krijgen nu ^^

grtz
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