PDA

View Full Version : [JAVA]Problemi basilari nelle progress bar: un aiuto per favore?


fbcyborg
13-11-2008, 11:13
Salve a tutti,

mi è sempre rimasto oscuro il mondo delle progress bar perché dei dubbi mi attanagliano da sempre e non riesco a venirne fuori, anche guardando il Java Tutorial.

Quello che vorrei fare è mettere una progress bar (magari anche in popup) durante il caricamento della mia applicazione (un classico). Nella mia applicazione parte un form all'inizio e dopo un click su un JButton viene avviata la vera interfaccia grafica (che richiede un po' di tempo per esser caricata in memoria). Quì vorrei mettere la progress bar.

I problemi che ho sono:
1) Come faccio a sapere a priori quanto ci metterà a caricare, se devo impostare un avanzamento di una percentuale?

2) Come faccio a dirgli che deve partire dopo che ho premuto sul JButton e deve fermarsi quando l'applicazione è pronta?

3) Un'altro problema è che il JButton rimane premuto fino a che non ha finito di caricare in memoria tutto e la vera interfaccia non è comparsa. Pensavo all'utilizzo di un Thread, ma è questo il caso?

lovaz
13-11-2008, 12:53
1) Come faccio a sapere a priori quanto ci metterà a caricare, se devo impostare un avanzamento di una percentuale?

Non puoi.
In passato ho considerato di suddividere il lavoro di costruzione dell'interfaccia
in vari task (ad esempio 10, creazione finestra principale, creazione dialog1, eccetera)
e al completamento di ogni task far avanzare la pbar di 1/10 (sempre ad esempio).
Non ti serve farlo in un thread separato, anzi e' meglio se lo fai sull'EDT.

2) Come faccio a dirgli che deve partire dopo che ho premuto sul JButton e deve fermarsi quando l'applicazione è pronta?

Spiegati meglio.


3) Un'altro problema è che il JButton rimane premuto fino a che non ha finito di caricare in memoria tutto e la vera interfaccia non è comparsa. Pensavo all'utilizzo di un Thread, ma è questo il caso?
se il pulsante e' nella stessa finestra della pbar ti consiglio di disabilitarlo,
altrimenti chiudi la finestra con il pulsante (non ha piu' senso)

fbcyborg
13-11-2008, 13:29
Non puoi.
In passato ho considerato di suddividere il lavoro di costruzione dell'interfaccia
in vari task (ad esempio 10, creazione finestra principale, creazione dialog1, eccetera)
e al completamento di ogni task far avanzare la pbar di 1/10 (sempre ad esempio).
Non ti serve farlo in un thread separato, anzi e' meglio se lo fai sull'EDT.
Ok, grazie, punto 1 chiarito.

2) Come faccio a dirgli che deve partire dopo che ho premuto sul JButton e deve fermarsi quando l'applicazione è pronta?


Spiegati meglio.Dunque, riepilogando la situazione è questa: quando la mia applicazione parte, mi chiede di fare login (e quindi la prima ed unica cosa a partire è una semplice maschera con pochissime cose e un JButton OK che serve ad inviare i dati di login e autenticarsi). Una volta inseriti username e password se l'autenticazione (con il database) è andata a buon fine provvede a lanciare la vera interfaccia. Per lanciare questa interfaccia ci vuole un po' di tempo. Per tutto questo tempo (tempo di autenticazione + tempo di caricare in memoria tutta la GUI principale) il JButton OK resta premuto. Quindi in teoria dovrei fare in modo innanzitutto che l'avvio della GUI principale dopo il form di login avvenisse a "JButton released (o UP)" e che la progress bar partisse (solo che non ci riesco), per poi terminare la sua "visualizzazione" al termine del caricamento. Spero di esser stato più preciso ora.


se il pulsante e' nella stessa finestra della pbar ti consiglio di disabilitarlo,
altrimenti chiudi la finestra con il pulsante (non ha piu' senso)
No.. Non devo nè posso disabilitarlo. Non capisco il perché dovrei farlo sinceramente.

Grazie per il supporto.

banryu79
13-11-2008, 13:47
Per il discorso del bottone che "rimane premuto" (in sostanza il repaint schedulato non viene eseguito in tempo, cioè prima di tutto l'ambaradam di codice che apre l'interfaccia principale e che, come il repaint del bottone, sarà eseguito nel contesto dell'EDT [se l'interfaccia è in AWT/Swing, come credo]) puoi risolvere in questo modo, se le premesse son quelle che seguono:
- il processo che fa partire l'ambaradam di cui sopra è invocato nell'actionPerformed() del tuo Button.

Prova questo, nell'actionPerformed del button:

public void actionPerformed(ActionEvent e)
{
Runnable buildInterface = new Runnable()
{
public void run()
{
// ...
// ... codice di costruzione dell'interfaccia principale
// ...
}
};
SwingUtilities.invokeLater(buildInterface);
}


In questo modo, dato che nel metodo actionPerformed ti trovi già nel contesto dell'EDT, eviti di far eseguire immediatamente (cioè prima di uscire dal metodo actionPerformed del Button) "codice Swing/AWT" (che viene appunto eseguito subito nell'EDT, di fatto costruendo l'interfaccia, e solo dopo, perchè si esce dal metodo actionPerformed, schedulando il repaint del button) per la costruzione della tua interfaccia che sarà invece "appeso" agli eventi che l'EDT dovrà processare, uscendo così da actionPerformed e permettendo l'aggiornamento del Button (e magari la chiusura della finestra in cui si trova) prima dell'esecuzione effettiva della costruzione dell'interfaccia principale.

Questo se ho capito bene la faccenda, il che non è scontato, eheh

fbcyborg
13-11-2008, 14:09
Perfetto! Grazie banryu79!
Ora è una scheggia..
Non resta che mettere una JProgressBar o un cursore di attesa nel momento che intercorre fra il dispose della JDialog di login e la comparsa della GUI principale.

banryu79
13-11-2008, 14:23
De nada :)
Per il futuro, se hai problemi simili considera che questo know how lo reperisci facilmente nel sito java.sun (ovviamente in inglese).

fbcyborg
13-11-2008, 16:06
Stavo pensando ad una cosa: è possibile lanciare una JProgress bar in indeterminate mode, subito prima che parta l'inizializzazione di tutta la GUI, e disattivarla non appena inizializzata?
Che dite, può funzionare come idea?

Perché in caso la metterei direttamente nella mia funzione initialize() della GUI.

banryu79
13-11-2008, 16:34
Stavo pensando ad una cosa: è possiile lanciare una JProgress bar in indeterminate mode...

Ah, però, non le conoscevo... si tratta di questo (http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.4/pb.html)?

fbcyborg
13-11-2008, 16:59
Esistono due tipi di JProgressBar: quelle con la percentualehttp://java.sun.com/docs/books/tutorial/figures/uiswing/components/progressbar.png e quelle indeterminate http://java.sun.com/docs/books/tutorial/figures/uiswing/components/indprogressbar.gif.

banryu79
13-11-2008, 17:12
Mi pare proprio che la seconda faccia al caso tuo.
La fai partire non appena lanci il processo di costruzione della GUI, e la spegni al completamento di tale processo che quando ha finito le fa un fischio.

AnonimoVeneziano
13-11-2008, 19:21
Come ha detto lovaz puoi suddividere i vari momenti del caricamento in fasi ed incrementare la barra di progresso in base alla fase in cui il caricamento si trova.

Ovviamente per funzionare la barra la fase di caricamento e di login deve essere eseguita in un thread diverso dall'event dispatching thread .

Ciao

fbcyborg
13-11-2008, 19:27
E' possibile mettere una scritta lampeggiante nella progress bar del tipo "Caricamento ..." ?

AnonimoVeneziano
13-11-2008, 19:31
Beh, JProgressBar ha un metodo setString() e se non sbaglio puoi anche impostare stili di barre personalizzati, quindi dovrebbe essere abbastanza costumizzabile, però io personalmente non l'ho mai fatto, quindi più di così non so

Ciao

fbcyborg
13-11-2008, 19:37
il setString() l'avevo provato ma non funziona. Per caso bisogna metterlo in un ciclo e farglielo fare ad ogni iterazione?

AnonimoVeneziano
13-11-2008, 19:40
il setString() l'avevo provato ma non funziona. Per caso bisogna metterlo in un ciclo e farglielo fare ad ogni iterazione?

Prova a dare anche un "setStringPainted(true)"

Ciao

fbcyborg
13-11-2008, 19:43
Grazie! Ha funzionato!!!

;)

fbcyborg
14-11-2008, 17:15
Ho usato il sistema proposto da banryu79, ovvero il seguente codice per risolvere il problema del tasto OK che rimaneva premuto:
public void actionPerformed(ActionEvent e)
{
Runnable buildInterface = new Runnable()
{
public void run()
{
// ...
// ... codice di costruzione dell'interfaccia principale
// ...
}
};
SwingUtilities.invokeLater(buildInterface);
}
Ora in effetti, premuto OK, il thread parte e lancia l'interfaccia grafica principale.
Ora vorrei riuscire a far visualizzare una JProgressBar indeterminata in uno dei due seguenti modi, e deve essere visualizzata nel tempo che intercorre fra la pressione del tasto OK e la comparsa della GUI principale.
Ho fatto diverse prove ma non sono ancora riuscito a trovare il modo giusto.

1) JProgressBar che viene visualizzata come popup, dopo che si è premuto il tasto OK e che quindi il form di login è scomparso
2) JProgressBar che è incorporata nel form di login e che viene visualizzata solo quando si preme OK (quindi sempre in fase di caricamento). Quì però il problema è che appena si preme OK, faccio una dispose() del JDialog (ok, potrei toglierlo, e farlo fare alla GUI principale alla fine della sua inizializzazione.

Che consigli potete darmi?

banryu79
14-11-2008, 17:40
Usa un altro Runnable, che chiamiamo "showProgression", così lo puoi accodare nell'EDT prima di "buildInterface".

Naturalmente devi passare a "buildInterface" una reference che punti al componente grafico da lanciare in "showProgression" in modo da poter fermare la ProgressBar e chiudere la relativa finestra appena la GUI è inizializzata (cioè al termine del processo definito in "buildInterface")

A questo punto ti potresti definire i due Runnable come classi al di fuori del metodo actionPerformed, che si limiterà a schedulare correttamente i due "task":
public void actionPerformed(ActionEvent e)
{
// costruisce "showProgression" e lo accoda nell'EDT
ShowProgression progressionFrame = new ShowProgression();
SwingUtilities.invokeLater(progressionFrame);

// costruisce "buildInterface", passa riferimento al "showProgression"
// da chiudere al completamento e lo accoda nell'EDT
SwingUtilities.invokeLater(new BuildInterface(progressionFrame));
}
Il codice è di puro esempio per illustrare il concetto.

- ShowProgression è una classe che estende JFrame o JDialog (per la versione popup della ProgressBar) e implementa Runnable (così è accodabile come thread da eseguire nell'EDT)

- BuildInterface è una classe che semplicemente estende Thread oppure implementa Runnable.

fbcyborg
14-11-2008, 17:45
Ok, prima di provare ho una domanda ancora: le due classi che implementano runnable, devono implementare il metodo run(). All'interno però non ci scrivo nulla giusto?

banryu79
14-11-2008, 18:09
Eh no, all'interno dei due metodi run() devi proprio metterci il codice che deve essere eseguito.

Riferendomi al problema di lanciare la costruzione dell'interfacica, ad esempio:

class BuildInterface implements Runnable
{
private ShowProgression progression;

public BuildInterface(ShowProgression sp)
{
progression = sp;
//...
}

public void run()
{
// ...
// ... codice di costruzione dell'interfaccia principale
// ... "chiudere" progression alla fine
}
};


Per ShowProgression, ad esempio:

class ShowProgression extends JFrame implements Runnable
{

// membri di ShowProgression...

public ShowProgression()
{
//... costruttore, costruisci l'interfaccia via codice
//... operazioni classiche, istanziazione componenti,
// setting dei LayoutManager, creazione e setting della JProgressBar
//... ecc...
}

public void run()
{
// ...
// ...codice di realizzazione del JFrame e dei suoi componenti (setVisible)
// ...attivazione della progress bar
// ...
}
}


Spero sia più chiaro, e spero sia esatto perchè sono di fretta, sto staccando dall'ufficio!

fbcyborg
14-11-2008, 20:30
Dunque, ho provato a fare come hai detto, solo che quando parte la progressbar (forse per un tempo troppo breve?) si vede il frame che la dovrebbe contenere, ma non si muove la barra all'interno (l'effetto del cosetto interno che si muove).
A questo punto non so se conviene mettere un cursore di attesa (tipo clessidra sul puntatore del mouse). Forse è anche più facile??

banryu79
15-11-2008, 11:15
Dunque, ho provato a fare come hai detto, solo che quando parte la progressbar (forse per un tempo troppo breve?) si vede il frame che la dovrebbe contenere, ma non si muove la barra all'interno (l'effetto del cosetto interno che si muove).

Beh, non so se è perchè il tempo di attesa è "troppo breve"... Voglio dire, se è così veloce a caricare l'interfaccia grafica chi te l'ha fatto fare di voler visualizzare una progress bar se si deve aspettare così poco tempo?

Se puoi posta il codice, così vediamo di capire meglio la cosa.


A questo punto non so se conviene mettere un cursore di attesa (tipo clessidra sul puntatore del mouse). Forse è anche più facile??
Questo è sicuramente più facile, devi solo settare il cursore prima di lanciare il processo di caricamento dell'interfaccia, e quando l'interfaccia è caricata lo risetti al cursore di default.

fbcyborg
15-11-2008, 11:42
Dunque, hai ragione, in effetti la ProgressBar forse ora è inutile. Il fatto è che prima quando si bloccava il JButton ci metteva molto più tempo.
Ora ho provato a cambiare il cursore ma non funziona.

La mia BuildInterface che estende JFrame ha un costruttore stile Eclipse (sto appunto usando Eclipse) ed è press'a poco così:
public MyFrame() {
super();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
frmInserisci = new InserimentoDialog(this);
frmCancella = new CancellazioneDialog(this);
frmModifica = new ModificaDialog(this);
m1 = new MyTableModel();
initialize();
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
Ho provato ad usare il setCursor ma non vedo nessun cambiamento.

banryu79
17-11-2008, 08:12
public MyFrame() {
super();
setCursor(new Cursor(Cursor.WAIT_CURSOR));
frmInserisci = new InserimentoDialog(this);
frmCancella = new CancellazioneDialog(this);
frmModifica = new ModificaDialog(this);
m1 = new MyTableModel();
initialize();
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
Ho provato ad usare il setCursor ma non vedo nessun cambiamento.
Immagino che in quel punto del codice (nel costruttore di MyFrame) il tuo oggetto MyFrame che stai costruendo non sia ancora ne "visible" ne "displayable" ne "enabled".

Forse è questo il problema. Non so se hai già risolto, cmq quando qualcosa non va come ci si aspetta bisognerebbe consultare la documentazione ufficiale per trovare risposte: spesso bastano 5 min.
Ecco cosa ho trovato (http://java.sun.com/javase/6/docs/api/java/awt/Component.html#setCursor(java.awt.Cursor)):

setCursor

public void setCursor(Cursor cursor)

Sets the cursor image to the specified cursor. This cursor image is displayed when the contains method for this component returns true for the current cursor location, and this Component is visible, displayable, and enabled. Setting the cursor of a Container causes that cursor to be displayed within all of the container's subcomponents, except for those that have a non-null cursor.

The method may have no visual effect if the Java platform implementation and/or the native system do not support changing the mouse cursor shape.

Parameters:
cursor - One of the constants defined by the Cursor class; if this parameter is null then this component will inherit the cursor of its parent

Since:
JDK1.1

See Also:
isEnabled(), isShowing(), getCursor(), contains(int, int), Toolkit.createCustomCursor(java.awt.Image, java.awt.Point, java.lang.String), Cursor


@EDIT:
Che in parole povere vuol dire che la grafica del cursore viene aggiornata a video (displayed) quando il Component relativo è completamente costruito e visibile E il mouse si trova sopra il Component.

fbcyborg
17-11-2008, 18:27
Grazie,
no non ho ancora risolto.

E allora secondo come dici tu e come dice la documentazione è impossibile risolvere.
Io voglio mettere la clessidra (o una progress bar, per rimanere in tema), proprio dal momento in cui scompare la schermata di login e compare il mio MyFrame.

fbcyborg
21-02-2010, 14:43
Torno su questo vecchio thread perché la parte di codice che mi è stata suggerita ora mi sta dando dei problemi.

Il codice al quale faccio riferimento è il seguente:
Runnable buildInterface = new Runnable()
{
public void run()
{
m = new MainFrame();
m.setVisible(true);
}
};
SwingUtilities.invokeLater(buildInterface);
Il problema è che, sebbene questo codice funzioni quando lancio il programma da Eclipse, quando creo il jar non funziona. Il programma viene terminato senza dare errori.

Ricordo che questo codice viene utilizzato dentro un JDialog di Login, nell'actionPerformed di un tasto OK, che dovrebbe quindi aprire la vera interfaccia del programma.